aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/StaticAnalyzer
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/clang/lib/StaticAnalyzer')
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp80
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp12
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp22
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp14
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp81
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp19
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp623
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp10
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp357
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp16
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp12
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp3
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp299
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp1083
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp26
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp150
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp54
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp17
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp241
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp95
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp47
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp10
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp3
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp344
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h12
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp53
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp110
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.h22
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp1410
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp138
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp6
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp21
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp14
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp1406
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp18
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp3
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp33
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp149
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp181
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp6
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp5
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp3
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp66
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp9
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp3
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp10
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp562
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp101
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h32
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp21
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h17
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp14
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp8
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp180
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h33
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp80
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp189
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp26
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp2223
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp1048
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp11
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp21
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp3
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp298
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp7
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp9
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp93
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h84
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h36
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp155
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp172
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h59
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp167
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp195
-rwxr-xr-xcontrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h1
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp66
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp71
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp45
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp363
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp42
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp59
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp241
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp23
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp43
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicSize.cpp71
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicType.cpp206
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp12
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp5
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp50
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp14
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp354
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp36
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp56
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp18
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp6
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/MemRegion.cpp296
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp30
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp17
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp842
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RegionStore.cpp51
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp13
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp7
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Store.cpp3
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SubEngine.cpp13
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp71
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp156
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp175
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp (renamed from contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp)62
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp494
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CreateCheckerManager.cpp50
166 files changed, 11661 insertions, 5675 deletions
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
index 2ef50a727ece..0e8cbc60689a 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
@@ -13,13 +13,14 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Analysis/CFGStmtMap.h"
+#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/Support/ErrorHandling.h"
using namespace clang;
using namespace ento;
@@ -27,24 +28,20 @@ using namespace ento;
namespace {
class AnalysisOrderChecker
- : public Checker<check::PreStmt<CastExpr>,
- check::PostStmt<CastExpr>,
- check::PreStmt<ArraySubscriptExpr>,
- check::PostStmt<ArraySubscriptExpr>,
- check::PreStmt<CXXNewExpr>,
- check::PostStmt<CXXNewExpr>,
- check::PreStmt<OffsetOfExpr>,
- check::PostStmt<OffsetOfExpr>,
- check::PreCall,
- check::PostCall,
- check::EndFunction,
- check::NewAllocator,
- check::Bind,
- check::PointerEscape,
- check::RegionChanges,
- check::LiveSymbols> {
-
- bool isCallbackEnabled(AnalyzerOptions &Opts, StringRef CallbackName) const {
+ : public Checker<
+ check::PreStmt<CastExpr>, check::PostStmt<CastExpr>,
+ check::PreStmt<ArraySubscriptExpr>,
+ check::PostStmt<ArraySubscriptExpr>, check::PreStmt<CXXNewExpr>,
+ check::PostStmt<CXXNewExpr>, check::PreStmt<CXXDeleteExpr>,
+ check::PostStmt<CXXDeleteExpr>, check::PreStmt<CXXConstructExpr>,
+ check::PostStmt<CXXConstructExpr>, check::PreStmt<OffsetOfExpr>,
+ check::PostStmt<OffsetOfExpr>, check::PreCall, check::PostCall,
+ check::EndFunction, check::EndAnalysis, check::NewAllocator,
+ check::Bind, check::PointerEscape, check::RegionChanges,
+ check::LiveSymbols, eval::Call> {
+
+ bool isCallbackEnabled(const AnalyzerOptions &Opts,
+ StringRef CallbackName) const {
return Opts.getCheckerBooleanOption(this, "*") ||
Opts.getCheckerBooleanOption(this, CallbackName);
}
@@ -95,6 +92,26 @@ public:
llvm::errs() << "PostStmt<CXXNewExpr>\n";
}
+ void checkPreStmt(const CXXDeleteExpr *NE, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PreStmtCXXDeleteExpr"))
+ llvm::errs() << "PreStmt<CXXDeleteExpr>\n";
+ }
+
+ void checkPostStmt(const CXXDeleteExpr *NE, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PostStmtCXXDeleteExpr"))
+ llvm::errs() << "PostStmt<CXXDeleteExpr>\n";
+ }
+
+ void checkPreStmt(const CXXConstructExpr *NE, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PreStmtCXXConstructExpr"))
+ llvm::errs() << "PreStmt<CXXConstructExpr>\n";
+ }
+
+ void checkPostStmt(const CXXConstructExpr *NE, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PostStmtCXXConstructExpr"))
+ llvm::errs() << "PostStmt<CXXConstructExpr>\n";
+ }
+
void checkPreStmt(const OffsetOfExpr *OOE, CheckerContext &C) const {
if (isCallbackEnabled(C, "PreStmtOffsetOfExpr"))
llvm::errs() << "PreStmt<OffsetOfExpr>\n";
@@ -105,11 +122,25 @@ public:
llvm::errs() << "PostStmt<OffsetOfExpr>\n";
}
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "EvalCall")) {
+ llvm::errs() << "EvalCall";
+ if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Call.getDecl()))
+ llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')';
+ llvm::errs() << " {argno: " << Call.getNumArgs() << '}';
+ llvm::errs() << " [" << Call.getKindAsString() << ']';
+ llvm::errs() << '\n';
+ return true;
+ }
+ return false;
+ }
+
void checkPreCall(const CallEvent &Call, CheckerContext &C) const {
if (isCallbackEnabled(C, "PreCall")) {
llvm::errs() << "PreCall";
if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Call.getDecl()))
llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')';
+ llvm::errs() << " [" << Call.getKindAsString() << ']';
llvm::errs() << '\n';
}
}
@@ -119,6 +150,7 @@ public:
llvm::errs() << "PostCall";
if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Call.getDecl()))
llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')';
+ llvm::errs() << " [" << Call.getKindAsString() << ']';
llvm::errs() << '\n';
}
}
@@ -140,7 +172,13 @@ public:
}
}
- void checkNewAllocator(const CXXNewExpr *CNE, SVal Target,
+ void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
+ ExprEngine &Eng) const {
+ if (isCallbackEnabled(BR.getAnalyzerOptions(), "EndAnalysis"))
+ llvm::errs() << "EndAnalysis\n";
+ }
+
+ void checkNewAllocator(const CXXAllocatorCall &Call,
CheckerContext &C) const {
if (isCallbackEnabled(C, "NewAllocator"))
llvm::errs() << "NewAllocator\n";
@@ -186,6 +224,6 @@ void ento::registerAnalysisOrderChecker(CheckerManager &mgr) {
mgr.registerChecker<AnalysisOrderChecker>();
}
-bool ento::shouldRegisterAnalysisOrderChecker(const LangOptions &LO) {
+bool ento::shouldRegisterAnalysisOrderChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
index 20f3008b4a4b..c06604b6cffe 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
@@ -103,7 +103,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
NumBlocksUnreachable += unreachable;
NumBlocks += total;
- std::string NameOfRootFunction = output.str();
+ std::string NameOfRootFunction = std::string(output.str());
output << " -> Total CFGBlocks: " << total << " | Unreachable CFGBlocks: "
<< unreachable << " | Exhausted Block: "
@@ -140,6 +140,6 @@ void ento::registerAnalyzerStatsChecker(CheckerManager &mgr) {
mgr.registerChecker<AnalyzerStatsChecker>();
}
-bool ento::shouldRegisterAnalyzerStatsChecker(const LangOptions &LO) {
+bool ento::shouldRegisterAnalyzerStatsChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
index 8d4793e0802f..59163c1f31fa 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
@@ -16,6 +16,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
using namespace clang;
@@ -54,12 +55,11 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS,
ProgramStateRef state = C.getState();
// Get the size of the array.
- DefinedOrUnknownSVal NumElements
- = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(),
- ER->getValueType());
+ DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
+ state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType());
- ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true);
- ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false);
+ ProgramStateRef StInBound = state->assumeInBound(Idx, ElementCount, true);
+ ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false);
if (StOutBound && !StInBound) {
ExplodedNode *N = C.generateErrorNode(StOutBound);
if (!N)
@@ -92,6 +92,6 @@ void ento::registerArrayBoundChecker(CheckerManager &mgr) {
mgr.registerChecker<ArrayBoundChecker>();
}
-bool ento::shouldRegisterArrayBoundChecker(const LangOptions &LO) {
+bool ento::shouldRegisterArrayBoundChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
index 8f3bf138cae4..7c264bba4b6a 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
@@ -12,13 +12,14 @@
//===----------------------------------------------------------------------===//
#include "Taint.h"
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/CharUnits.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/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
@@ -175,24 +176,23 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
}
do {
- // CHECK UPPER BOUND: Is byteOffset >= extent(baseRegion)? If so,
+ // CHECK UPPER BOUND: Is byteOffset >= size(baseRegion)? If so,
// we are doing a load/store after the last valid offset.
- DefinedOrUnknownSVal extentVal =
- rawOffset.getRegion()->getExtent(svalBuilder);
- if (!extentVal.getAs<NonLoc>())
+ const MemRegion *MR = rawOffset.getRegion();
+ DefinedOrUnknownSVal Size = getDynamicSize(state, MR, svalBuilder);
+ if (!Size.getAs<NonLoc>())
break;
- if (extentVal.getAs<nonloc::ConcreteInt>()) {
+ if (Size.getAs<nonloc::ConcreteInt>()) {
std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets =
getSimplifiedOffsets(rawOffset.getByteOffset(),
- extentVal.castAs<nonloc::ConcreteInt>(),
- svalBuilder);
+ Size.castAs<nonloc::ConcreteInt>(), svalBuilder);
rawOffsetVal = simplifiedOffsets.first;
- extentVal = simplifiedOffsets.second;
+ Size = simplifiedOffsets.second;
}
SVal upperbound = svalBuilder.evalBinOpNN(state, BO_GE, rawOffsetVal,
- extentVal.castAs<NonLoc>(),
+ Size.castAs<NonLoc>(),
svalBuilder.getConditionType());
Optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>();
@@ -356,6 +356,6 @@ void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) {
mgr.registerChecker<ArrayBoundCheckerV2>();
}
-bool ento::shouldRegisterArrayBoundCheckerV2(const LangOptions &LO) {
+bool ento::shouldRegisterArrayBoundCheckerV2(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index 325952fe4ed4..918c6e361381 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -1243,7 +1243,7 @@ void ento::registerNilArgChecker(CheckerManager &mgr) {
mgr.registerChecker<NilArgChecker>();
}
-bool ento::shouldRegisterNilArgChecker(const LangOptions &LO) {
+bool ento::shouldRegisterNilArgChecker(const CheckerManager &mgr) {
return true;
}
@@ -1251,7 +1251,7 @@ void ento::registerCFNumberChecker(CheckerManager &mgr) {
mgr.registerChecker<CFNumberChecker>();
}
-bool ento::shouldRegisterCFNumberChecker(const LangOptions &LO) {
+bool ento::shouldRegisterCFNumberChecker(const CheckerManager &mgr) {
return true;
}
@@ -1259,7 +1259,7 @@ void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
mgr.registerChecker<CFRetainReleaseChecker>();
}
-bool ento::shouldRegisterCFRetainReleaseChecker(const LangOptions &LO) {
+bool ento::shouldRegisterCFRetainReleaseChecker(const CheckerManager &mgr) {
return true;
}
@@ -1267,7 +1267,7 @@ void ento::registerClassReleaseChecker(CheckerManager &mgr) {
mgr.registerChecker<ClassReleaseChecker>();
}
-bool ento::shouldRegisterClassReleaseChecker(const LangOptions &LO) {
+bool ento::shouldRegisterClassReleaseChecker(const CheckerManager &mgr) {
return true;
}
@@ -1275,7 +1275,7 @@ void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
mgr.registerChecker<VariadicMethodTypeChecker>();
}
-bool ento::shouldRegisterVariadicMethodTypeChecker(const LangOptions &LO) {
+bool ento::shouldRegisterVariadicMethodTypeChecker(const CheckerManager &mgr) {
return true;
}
@@ -1283,7 +1283,7 @@ void ento::registerObjCLoopChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCLoopChecker>();
}
-bool ento::shouldRegisterObjCLoopChecker(const LangOptions &LO) {
+bool ento::shouldRegisterObjCLoopChecker(const CheckerManager &mgr) {
return true;
}
@@ -1291,6 +1291,6 @@ void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCNonNilReturnValueChecker>();
}
-bool ento::shouldRegisterObjCNonNilReturnValueChecker(const LangOptions &LO) {
+bool ento::shouldRegisterObjCNonNilReturnValueChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 0eb3c3d1d0e6..2752b37f9b3f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -184,6 +184,6 @@ void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
mgr.registerChecker<BlockInCriticalSectionChecker>();
}
-bool ento::shouldRegisterBlockInCriticalSectionChecker(const LangOptions &LO) {
+bool ento::shouldRegisterBlockInCriticalSectionChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
index 1423b9c39b26..6c0caf3c4e78 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
@@ -70,8 +70,8 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S,
// Get the value of the right-hand side. We only care about values
// that are defined (UnknownVals and UndefinedVals are handled by other
// checkers).
- Optional<DefinedSVal> DV = val.getAs<DefinedSVal>();
- if (!DV)
+ Optional<NonLoc> NV = val.getAs<NonLoc>();
+ if (!NV)
return;
// Check if the assigned value meets our criteria for correctness. It must
@@ -79,84 +79,23 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S,
// the value is possibly < 0 (for a negative value) or greater than 1.
ProgramStateRef state = C.getState();
SValBuilder &svalBuilder = C.getSValBuilder();
+ BasicValueFactory &BVF = svalBuilder.getBasicValueFactory();
ConstraintManager &CM = C.getConstraintManager();
- // First, ensure that the value is >= 0.
- DefinedSVal zeroVal = svalBuilder.makeIntVal(0, valTy);
- SVal greaterThanOrEqualToZeroVal =
- svalBuilder.evalBinOp(state, BO_GE, *DV, zeroVal,
- svalBuilder.getConditionType());
+ llvm::APSInt Zero = BVF.getValue(0, valTy);
+ llvm::APSInt One = BVF.getValue(1, valTy);
- Optional<DefinedSVal> greaterThanEqualToZero =
- greaterThanOrEqualToZeroVal.getAs<DefinedSVal>();
+ ProgramStateRef StIn, StOut;
+ std::tie(StIn, StOut) = CM.assumeInclusiveRangeDual(state, *NV, Zero, One);
- if (!greaterThanEqualToZero) {
- // The SValBuilder cannot construct a valid SVal for this condition.
- // This means we cannot properly reason about it.
- return;
- }
-
- ProgramStateRef stateLT, stateGE;
- std::tie(stateGE, stateLT) = CM.assumeDual(state, *greaterThanEqualToZero);
-
- // Is it possible for the value to be less than zero?
- if (stateLT) {
- // It is possible for the value to be less than zero. We only
- // want to emit a warning, however, if that value is fully constrained.
- // If it it possible for the value to be >= 0, then essentially the
- // value is underconstrained and there is nothing left to be done.
- if (!stateGE)
- emitReport(stateLT, C);
-
- // In either case, we are done.
- return;
- }
-
- // If we reach here, it must be the case that the value is constrained
- // to only be >= 0.
- assert(stateGE == state);
-
- // At this point we know that the value is >= 0.
- // Now check to ensure that the value is <= 1.
- DefinedSVal OneVal = svalBuilder.makeIntVal(1, valTy);
- SVal lessThanEqToOneVal =
- svalBuilder.evalBinOp(state, BO_LE, *DV, OneVal,
- svalBuilder.getConditionType());
-
- Optional<DefinedSVal> lessThanEqToOne =
- lessThanEqToOneVal.getAs<DefinedSVal>();
-
- if (!lessThanEqToOne) {
- // The SValBuilder cannot construct a valid SVal for this condition.
- // This means we cannot properly reason about it.
- return;
- }
-
- ProgramStateRef stateGT, stateLE;
- std::tie(stateLE, stateGT) = CM.assumeDual(state, *lessThanEqToOne);
-
- // Is it possible for the value to be greater than one?
- if (stateGT) {
- // It is possible for the value to be greater than one. We only
- // want to emit a warning, however, if that value is fully constrained.
- // If it is possible for the value to be <= 1, then essentially the
- // value is underconstrained and there is nothing left to be done.
- if (!stateLE)
- emitReport(stateGT, C);
-
- // In either case, we are done.
- return;
- }
-
- // If we reach here, it must be the case that the value is constrained
- // to only be <= 1.
- assert(stateLE == state);
+ if (!StIn)
+ emitReport(StOut, C);
}
void ento::registerBoolAssignmentChecker(CheckerManager &mgr) {
mgr.registerChecker<BoolAssignmentChecker>();
}
-bool ento::shouldRegisterBoolAssignmentChecker(const LangOptions &LO) {
+bool ento::shouldRegisterBoolAssignmentChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
index 10594e331cbe..233ce57c3ac9 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
@@ -10,12 +10,13 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/Basic/Builtins.h"
+#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 "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
using namespace clang;
using namespace ento;
@@ -63,10 +64,12 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
case Builtin::BI__builtin_unpredictable:
case Builtin::BI__builtin_expect:
+ case Builtin::BI__builtin_expect_with_probability:
case Builtin::BI__builtin_assume_aligned:
case Builtin::BI__builtin_addressof: {
- // For __builtin_unpredictable, __builtin_expect, and
- // __builtin_assume_aligned, just return the value of the subexpression.
+ // For __builtin_unpredictable, __builtin_expect,
+ // __builtin_expect_with_probability and __builtin_assume_aligned,
+ // just return the value of the subexpression.
// __builtin_addressof is going from a reference to a pointer, but those
// are represented the same way in the analyzer.
assert (Call.getNumArgs() > 0);
@@ -90,10 +93,10 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
return true; // Return true to model purity.
SValBuilder& svalBuilder = C.getSValBuilder();
- DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder);
- DefinedOrUnknownSVal extentMatchesSizeArg =
- svalBuilder.evalEQ(state, Extent, Size.castAs<DefinedOrUnknownSVal>());
- state = state->assume(extentMatchesSizeArg, true);
+ DefinedOrUnknownSVal DynSize = getDynamicSize(state, R, svalBuilder);
+ DefinedOrUnknownSVal DynSizeMatchesSizeArg =
+ svalBuilder.evalEQ(state, DynSize, Size.castAs<DefinedOrUnknownSVal>());
+ state = state->assume(DynSizeMatchesSizeArg, true);
assert(state && "The region should not have any previous constraints");
C.addTransition(state->BindExpr(CE, LCtx, loc::MemRegionVal(R)));
@@ -134,6 +137,6 @@ void ento::registerBuiltinFunctionChecker(CheckerManager &mgr) {
mgr.registerChecker<BuiltinFunctionChecker>();
}
-bool ento::shouldRegisterBuiltinFunctionChecker(const LangOptions &LO) {
+bool ento::shouldRegisterBuiltinFunctionChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 21c4bbc60264..30fd62f887c4 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -11,23 +11,66 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "InterCheckerAPI.h"
#include "clang/Basic/CharInfo.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace ento;
namespace {
+struct AnyArgExpr {
+ // FIXME: Remove constructor in C++17 to turn it into an aggregate.
+ AnyArgExpr(const Expr *Expression, unsigned ArgumentIndex)
+ : Expression{Expression}, ArgumentIndex{ArgumentIndex} {}
+ const Expr *Expression;
+ unsigned ArgumentIndex;
+};
+
+struct SourceArgExpr : AnyArgExpr {
+ using AnyArgExpr::AnyArgExpr; // FIXME: Remove using in C++17.
+};
+
+struct DestinationArgExpr : AnyArgExpr {
+ using AnyArgExpr::AnyArgExpr; // FIXME: Same.
+};
+
+struct SizeArgExpr : AnyArgExpr {
+ using AnyArgExpr::AnyArgExpr; // FIXME: Same.
+};
+
+using ErrorMessage = SmallString<128>;
+enum class AccessKind { write, read };
+
+static ErrorMessage createOutOfBoundErrorMsg(StringRef FunctionDescription,
+ AccessKind Access) {
+ ErrorMessage Message;
+ llvm::raw_svector_ostream Os(Message);
+
+ // Function classification like: Memory copy function
+ Os << toUppercase(FunctionDescription.front())
+ << &FunctionDescription.data()[1];
+
+ if (Access == AccessKind::write) {
+ Os << " overflows the destination buffer";
+ } else { // read access
+ Os << " accesses out-of-bound array element";
+ }
+
+ return Message;
+}
+
enum class ConcatFnKind { none = 0, strcat = 1, strlcat = 2 };
class CStringChecker : public Checker< eval::Call,
check::PreStmt<DeclStmt>,
@@ -111,12 +154,9 @@ public:
void evalMemmove(CheckerContext &C, const CallExpr *CE) const;
void evalBcopy(CheckerContext &C, const CallExpr *CE) const;
void evalCopyCommon(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef state,
- const Expr *Size,
- const Expr *Source,
- const Expr *Dest,
- bool Restricted = false,
- bool IsMempcpy = false) const;
+ ProgramStateRef state, SizeArgExpr Size,
+ DestinationArgExpr Dest, SourceArgExpr Source,
+ bool Restricted, bool IsMempcpy) const;
void evalMemcmp(CheckerContext &C, const CallExpr *CE) const;
@@ -193,40 +233,17 @@ public:
ProgramStateRef &State);
// Re-usable checks
- ProgramStateRef checkNonNull(CheckerContext &C,
- ProgramStateRef state,
- const Expr *S,
- SVal l,
- unsigned IdxOfArg) const;
- ProgramStateRef CheckLocation(CheckerContext &C,
- ProgramStateRef state,
- const Expr *S,
- SVal l,
- const char *message = nullptr) const;
- ProgramStateRef CheckBufferAccess(CheckerContext &C,
- ProgramStateRef state,
- const Expr *Size,
- const Expr *FirstBuf,
- const Expr *SecondBuf,
- const char *firstMessage = nullptr,
- const char *secondMessage = nullptr,
- bool WarnAboutSize = false) const;
-
- ProgramStateRef CheckBufferAccess(CheckerContext &C,
- ProgramStateRef state,
- const Expr *Size,
- const Expr *Buf,
- const char *message = nullptr,
- bool WarnAboutSize = false) const {
- // This is a convenience overload.
- return CheckBufferAccess(C, state, Size, Buf, nullptr, message, nullptr,
- WarnAboutSize);
- }
- ProgramStateRef CheckOverlap(CheckerContext &C,
- ProgramStateRef state,
- const Expr *Size,
- const Expr *First,
- const Expr *Second) const;
+ ProgramStateRef checkNonNull(CheckerContext &C, ProgramStateRef State,
+ AnyArgExpr Arg, SVal l) const;
+ ProgramStateRef CheckLocation(CheckerContext &C, ProgramStateRef state,
+ AnyArgExpr Buffer, SVal Element,
+ AccessKind Access) const;
+ ProgramStateRef CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
+ AnyArgExpr Buffer, SizeArgExpr Size,
+ AccessKind Access) const;
+ ProgramStateRef CheckOverlap(CheckerContext &C, ProgramStateRef state,
+ SizeArgExpr Size, AnyArgExpr First,
+ AnyArgExpr Second) const;
void emitOverlapBug(CheckerContext &C,
ProgramStateRef state,
const Stmt *First,
@@ -275,26 +292,26 @@ CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef state, SVal V,
}
ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
- ProgramStateRef state,
- const Expr *S, SVal l,
- unsigned IdxOfArg) const {
+ ProgramStateRef State,
+ AnyArgExpr Arg, SVal l) const {
// If a previous check has failed, propagate the failure.
- if (!state)
+ if (!State)
return nullptr;
ProgramStateRef stateNull, stateNonNull;
- std::tie(stateNull, stateNonNull) = assumeZero(C, state, l, S->getType());
+ std::tie(stateNull, stateNonNull) =
+ assumeZero(C, State, l, Arg.Expression->getType());
if (stateNull && !stateNonNull) {
if (Filter.CheckCStringNullArg) {
SmallString<80> buf;
llvm::raw_svector_ostream OS(buf);
assert(CurrentFunctionDescription);
- OS << "Null pointer passed as " << IdxOfArg
- << llvm::getOrdinalSuffix(IdxOfArg) << " argument to "
+ OS << "Null pointer passed as " << (Arg.ArgumentIndex + 1)
+ << llvm::getOrdinalSuffix(Arg.ArgumentIndex + 1) << " argument to "
<< CurrentFunctionDescription;
- emitNullArgBug(C, stateNull, S, OS.str());
+ emitNullArgBug(C, stateNull, Arg.Expression, OS.str());
}
return nullptr;
}
@@ -306,19 +323,20 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
// FIXME: This was originally copied from ArrayBoundChecker.cpp. Refactor?
ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
- ProgramStateRef state,
- const Expr *S, SVal l,
- const char *warningMsg) const {
+ ProgramStateRef state,
+ AnyArgExpr Buffer, SVal Element,
+ AccessKind Access) const {
+
// If a previous check has failed, propagate the failure.
if (!state)
return nullptr;
// Check for out of bound array element access.
- const MemRegion *R = l.getAsRegion();
+ const MemRegion *R = Element.getAsRegion();
if (!R)
return state;
- const ElementRegion *ER = dyn_cast<ElementRegion>(R);
+ const auto *ER = dyn_cast<ElementRegion>(R);
if (!ER)
return state;
@@ -326,11 +344,9 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
return state;
// Get the size of the array.
- const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion());
- SValBuilder &svalBuilder = C.getSValBuilder();
- SVal Extent =
- svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder));
- DefinedOrUnknownSVal Size = Extent.castAs<DefinedOrUnknownSVal>();
+ const auto *superReg = cast<SubRegion>(ER->getSuperRegion());
+ DefinedOrUnknownSVal Size =
+ getDynamicSize(state, superReg, C.getSValBuilder());
// Get the index of the accessed element.
DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
@@ -343,20 +359,11 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
// In the latter case we only do modeling but do not emit warning.
if (!Filter.CheckCStringOutOfBounds)
return nullptr;
- // Emit a bug report.
- if (warningMsg) {
- emitOutOfBoundsBug(C, StOutBound, S, warningMsg);
- } else {
- assert(CurrentFunctionDescription);
- assert(CurrentFunctionDescription[0] != '\0');
- SmallString<80> buf;
- llvm::raw_svector_ostream os(buf);
- os << toUppercase(CurrentFunctionDescription[0])
- << &CurrentFunctionDescription[1]
- << " accesses out-of-bound array element";
- emitOutOfBoundsBug(C, StOutBound, S, os.str());
- }
+ // Emit a bug report.
+ ErrorMessage Message =
+ createOutOfBoundErrorMsg(CurrentFunctionDescription, Access);
+ emitOutOfBoundsBug(C, StOutBound, Buffer.Expression, Message);
return nullptr;
}
@@ -366,89 +373,68 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
}
ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C,
- ProgramStateRef state,
- const Expr *Size,
- const Expr *FirstBuf,
- const Expr *SecondBuf,
- const char *firstMessage,
- const char *secondMessage,
- bool WarnAboutSize) const {
+ ProgramStateRef State,
+ AnyArgExpr Buffer,
+ SizeArgExpr Size,
+ AccessKind Access) const {
// If a previous check has failed, propagate the failure.
- if (!state)
+ if (!State)
return nullptr;
SValBuilder &svalBuilder = C.getSValBuilder();
ASTContext &Ctx = svalBuilder.getContext();
- const LocationContext *LCtx = C.getLocationContext();
- QualType sizeTy = Size->getType();
+ QualType SizeTy = Size.Expression->getType();
QualType PtrTy = Ctx.getPointerType(Ctx.CharTy);
// Check that the first buffer is non-null.
- SVal BufVal = C.getSVal(FirstBuf);
- state = checkNonNull(C, state, FirstBuf, BufVal, 1);
- if (!state)
+ SVal BufVal = C.getSVal(Buffer.Expression);
+ State = checkNonNull(C, State, Buffer, BufVal);
+ if (!State)
return nullptr;
// If out-of-bounds checking is turned off, skip the rest.
if (!Filter.CheckCStringOutOfBounds)
- return state;
+ return State;
// Get the access length and make sure it is known.
// FIXME: This assumes the caller has already checked that the access length
// is positive. And that it's unsigned.
- SVal LengthVal = C.getSVal(Size);
+ SVal LengthVal = C.getSVal(Size.Expression);
Optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
if (!Length)
- return state;
+ return State;
// Compute the offset of the last element to be accessed: size-1.
- NonLoc One = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>();
- SVal Offset = svalBuilder.evalBinOpNN(state, BO_Sub, *Length, One, sizeTy);
+ NonLoc One = svalBuilder.makeIntVal(1, SizeTy).castAs<NonLoc>();
+ SVal Offset = svalBuilder.evalBinOpNN(State, BO_Sub, *Length, One, SizeTy);
if (Offset.isUnknown())
return nullptr;
NonLoc LastOffset = Offset.castAs<NonLoc>();
// Check that the first buffer is sufficiently long.
- SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType());
+ SVal BufStart =
+ svalBuilder.evalCast(BufVal, PtrTy, Buffer.Expression->getType());
if (Optional<Loc> BufLoc = BufStart.getAs<Loc>()) {
- const Expr *warningExpr = (WarnAboutSize ? Size : FirstBuf);
- SVal BufEnd = svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc,
- LastOffset, PtrTy);
- state = CheckLocation(C, state, warningExpr, BufEnd, firstMessage);
+ SVal BufEnd =
+ svalBuilder.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy);
- // If the buffer isn't large enough, abort.
- if (!state)
- return nullptr;
- }
+ State = CheckLocation(C, State, Buffer, BufEnd, Access);
- // If there's a second buffer, check it as well.
- if (SecondBuf) {
- BufVal = state->getSVal(SecondBuf, LCtx);
- state = checkNonNull(C, state, SecondBuf, BufVal, 2);
- if (!state)
+ // If the buffer isn't large enough, abort.
+ if (!State)
return nullptr;
-
- BufStart = svalBuilder.evalCast(BufVal, PtrTy, SecondBuf->getType());
- if (Optional<Loc> BufLoc = BufStart.getAs<Loc>()) {
- const Expr *warningExpr = (WarnAboutSize ? Size : SecondBuf);
-
- SVal BufEnd = svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc,
- LastOffset, PtrTy);
- state = CheckLocation(C, state, warningExpr, BufEnd, secondMessage);
- }
}
// Large enough or not, return this state!
- return state;
+ return State;
}
ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
- ProgramStateRef state,
- const Expr *Size,
- const Expr *First,
- const Expr *Second) const {
+ ProgramStateRef state,
+ SizeArgExpr Size, AnyArgExpr First,
+ AnyArgExpr Second) const {
if (!Filter.CheckCStringBufferOverlap)
return state;
@@ -464,8 +450,8 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
// Get the buffer values and make sure they're known locations.
const LocationContext *LCtx = C.getLocationContext();
- SVal firstVal = state->getSVal(First, LCtx);
- SVal secondVal = state->getSVal(Second, LCtx);
+ SVal firstVal = state->getSVal(First.Expression, LCtx);
+ SVal secondVal = state->getSVal(Second.Expression, LCtx);
Optional<Loc> firstLoc = firstVal.getAs<Loc>();
if (!firstLoc)
@@ -478,11 +464,11 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
// Are the two values the same?
SValBuilder &svalBuilder = C.getSValBuilder();
std::tie(stateTrue, stateFalse) =
- state->assume(svalBuilder.evalEQ(state, *firstLoc, *secondLoc));
+ state->assume(svalBuilder.evalEQ(state, *firstLoc, *secondLoc));
if (stateTrue && !stateFalse) {
// If the values are known to be equal, that's automatically an overlap.
- emitOverlapBug(C, stateTrue, First, Second);
+ emitOverlapBug(C, stateTrue, First.Expression, Second.Expression);
return nullptr;
}
@@ -492,8 +478,8 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
// Which value comes first?
QualType cmpTy = svalBuilder.getConditionType();
- SVal reverse = svalBuilder.evalBinOpLL(state, BO_GT,
- *firstLoc, *secondLoc, cmpTy);
+ SVal reverse =
+ svalBuilder.evalBinOpLL(state, BO_GT, *firstLoc, *secondLoc, cmpTy);
Optional<DefinedOrUnknownSVal> reverseTest =
reverse.getAs<DefinedOrUnknownSVal>();
if (!reverseTest)
@@ -514,7 +500,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
}
// Get the length, and make sure it too is known.
- SVal LengthVal = state->getSVal(Size, LCtx);
+ SVal LengthVal = state->getSVal(Size.Expression, LCtx);
Optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
if (!Length)
return state;
@@ -523,22 +509,22 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
// Bail out if the cast fails.
ASTContext &Ctx = svalBuilder.getContext();
QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy);
- SVal FirstStart = svalBuilder.evalCast(*firstLoc, CharPtrTy,
- First->getType());
+ SVal FirstStart =
+ svalBuilder.evalCast(*firstLoc, CharPtrTy, First.Expression->getType());
Optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>();
if (!FirstStartLoc)
return state;
// Compute the end of the first buffer. Bail out if THAT fails.
- SVal FirstEnd = svalBuilder.evalBinOpLN(state, BO_Add,
- *FirstStartLoc, *Length, CharPtrTy);
+ SVal FirstEnd = svalBuilder.evalBinOpLN(state, BO_Add, *FirstStartLoc,
+ *Length, CharPtrTy);
Optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>();
if (!FirstEndLoc)
return state;
// Is the end of the first buffer past the start of the second buffer?
- SVal Overlap = svalBuilder.evalBinOpLL(state, BO_GT,
- *FirstEndLoc, *secondLoc, cmpTy);
+ SVal Overlap =
+ svalBuilder.evalBinOpLL(state, BO_GT, *FirstEndLoc, *secondLoc, cmpTy);
Optional<DefinedOrUnknownSVal> OverlapTest =
Overlap.getAs<DefinedOrUnknownSVal>();
if (!OverlapTest)
@@ -548,7 +534,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
if (stateTrue && !stateFalse) {
// Overlap!
- emitOverlapBug(C, stateTrue, First, Second);
+ emitOverlapBug(C, stateTrue, First.Expression, Second.Expression);
return nullptr;
}
@@ -723,7 +709,8 @@ ProgramStateRef CStringChecker::setCStringLength(ProgramStateRef state,
case MemRegion::SymbolicRegionKind:
case MemRegion::AllocaRegionKind:
- case MemRegion::VarRegionKind:
+ case MemRegion::NonParamVarRegionKind:
+ case MemRegion::ParamVarRegionKind:
case MemRegion::FieldRegionKind:
case MemRegion::ObjCIvarRegionKind:
// These are the types we can currently track string lengths for.
@@ -828,7 +815,8 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
}
case MemRegion::SymbolicRegionKind:
case MemRegion::AllocaRegionKind:
- case MemRegion::VarRegionKind:
+ case MemRegion::NonParamVarRegionKind:
+ case MemRegion::ParamVarRegionKind:
case MemRegion::FieldRegionKind:
case MemRegion::ObjCIvarRegionKind:
return getCStringLengthForRegion(C, state, Ex, MR, hypothetical);
@@ -935,14 +923,12 @@ bool CStringChecker::IsFirstBufInBound(CheckerContext &C,
// Get the size of the array.
const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion());
- SVal Extent =
- svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder));
- DefinedOrUnknownSVal ExtentSize = Extent.castAs<DefinedOrUnknownSVal>();
+ DefinedOrUnknownSVal SizeDV = getDynamicSize(state, superReg, svalBuilder);
// Get the index of the accessed element.
DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
- ProgramStateRef StInBound = state->assumeInBound(Idx, ExtentSize, true);
+ ProgramStateRef StInBound = state->assumeInBound(Idx, SizeDV, true);
return static_cast<bool>(StInBound);
}
@@ -1025,10 +1011,14 @@ bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
os << "a C++ temp object of type "
<< cast<TypedValueRegion>(MR)->getValueType().getAsString();
return true;
- case MemRegion::VarRegionKind:
+ case MemRegion::NonParamVarRegionKind:
os << "a variable of type"
<< cast<TypedValueRegion>(MR)->getValueType().getAsString();
return true;
+ case MemRegion::ParamVarRegionKind:
+ os << "a parameter of type"
+ << cast<TypedValueRegion>(MR)->getValueType().getAsString();
+ return true;
case MemRegion::FieldRegionKind:
os << "a field of type "
<< cast<TypedValueRegion>(MR)->getValueType().getAsString();
@@ -1069,13 +1059,12 @@ bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal,
// For now we can only handle the case of offset is 0 and concrete char value.
if (Offset.isValid() && !Offset.hasSymbolicOffset() &&
Offset.getOffset() == 0) {
- // Get the base region's extent.
- auto *SubReg = cast<SubRegion>(BR);
- DefinedOrUnknownSVal Extent = SubReg->getExtent(svalBuilder);
+ // Get the base region's size.
+ DefinedOrUnknownSVal SizeDV = getDynamicSize(State, BR, svalBuilder);
ProgramStateRef StateWholeReg, StateNotWholeReg;
std::tie(StateWholeReg, StateNotWholeReg) =
- State->assume(svalBuilder.evalEQ(State, Extent, *SizeNL));
+ State->assume(svalBuilder.evalEQ(State, SizeDV, *SizeNL));
// With the semantic of 'memset()', we should convert the CharVal to
// unsigned char.
@@ -1134,25 +1123,24 @@ bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal,
// evaluation of individual function calls.
//===----------------------------------------------------------------------===//
-void CStringChecker::evalCopyCommon(CheckerContext &C,
- const CallExpr *CE,
- ProgramStateRef state,
- const Expr *Size, const Expr *Dest,
- const Expr *Source, bool Restricted,
+void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef state, SizeArgExpr Size,
+ DestinationArgExpr Dest,
+ SourceArgExpr Source, bool Restricted,
bool IsMempcpy) const {
CurrentFunctionDescription = "memory copy function";
// See if the size argument is zero.
const LocationContext *LCtx = C.getLocationContext();
- SVal sizeVal = state->getSVal(Size, LCtx);
- QualType sizeTy = Size->getType();
+ SVal sizeVal = state->getSVal(Size.Expression, LCtx);
+ QualType sizeTy = Size.Expression->getType();
ProgramStateRef stateZeroSize, stateNonZeroSize;
std::tie(stateZeroSize, stateNonZeroSize) =
- assumeZero(C, state, sizeVal, sizeTy);
+ assumeZero(C, state, sizeVal, sizeTy);
// Get the value of the Dest.
- SVal destVal = state->getSVal(Dest, LCtx);
+ SVal destVal = state->getSVal(Dest.Expression, LCtx);
// If the size is zero, there won't be any actual memory access, so
// just bind the return value to the destination buffer and return.
@@ -1168,24 +1156,23 @@ void CStringChecker::evalCopyCommon(CheckerContext &C,
// Ensure the destination is not null. If it is NULL there will be a
// NULL pointer dereference.
- state = checkNonNull(C, state, Dest, destVal, 1);
+ state = checkNonNull(C, state, Dest, destVal);
if (!state)
return;
// Get the value of the Src.
- SVal srcVal = state->getSVal(Source, LCtx);
+ SVal srcVal = state->getSVal(Source.Expression, LCtx);
// Ensure the source is not null. If it is NULL there will be a
// NULL pointer dereference.
- state = checkNonNull(C, state, Source, srcVal, 2);
+ state = checkNonNull(C, state, Source, srcVal);
if (!state)
return;
// Ensure the accesses are valid and that the buffers do not overlap.
- const char * const writeWarning =
- "Memory copy function overflows destination buffer";
- state = CheckBufferAccess(C, state, Size, Dest, Source,
- writeWarning, /* sourceWarning = */ nullptr);
+ state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write);
+ state = CheckBufferAccess(C, state, Source, Size, AccessKind::read);
+
if (Restricted)
state = CheckOverlap(C, state, Size, Dest, Source);
@@ -1200,9 +1187,9 @@ void CStringChecker::evalCopyCommon(CheckerContext &C,
ASTContext &Ctx = SvalBuilder.getContext();
QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy);
SVal DestRegCharVal =
- SvalBuilder.evalCast(destVal, CharPtrTy, Dest->getType());
+ SvalBuilder.evalCast(destVal, CharPtrTy, Dest.Expression->getType());
SVal lastElement = C.getSValBuilder().evalBinOp(
- state, BO_Add, DestRegCharVal, sizeVal, Dest->getType());
+ state, BO_Add, DestRegCharVal, sizeVal, Dest.Expression->getType());
// If we don't know how much we copied, we can at least
// conjure a return value for later.
if (lastElement.isUnknown())
@@ -1223,120 +1210,136 @@ void CStringChecker::evalCopyCommon(CheckerContext &C,
// can use LazyCompoundVals to copy the source values into the destination.
// This would probably remove any existing bindings past the end of the
// copied region, but that's still an improvement over blank invalidation.
- state = InvalidateBuffer(C, state, Dest, C.getSVal(Dest),
- /*IsSourceBuffer*/false, Size);
+ state =
+ InvalidateBuffer(C, state, Dest.Expression, C.getSVal(Dest.Expression),
+ /*IsSourceBuffer*/ false, Size.Expression);
// Invalidate the source (const-invalidation without const-pointer-escaping
// the address of the top-level region).
- state = InvalidateBuffer(C, state, Source, C.getSVal(Source),
- /*IsSourceBuffer*/true, nullptr);
+ state = InvalidateBuffer(C, state, Source.Expression,
+ C.getSVal(Source.Expression),
+ /*IsSourceBuffer*/ true, nullptr);
C.addTransition(state);
}
}
-
void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const {
// void *memcpy(void *restrict dst, const void *restrict src, size_t n);
// The return value is the address of the destination buffer.
- const Expr *Dest = CE->getArg(0);
- ProgramStateRef state = C.getState();
+ DestinationArgExpr Dest = {CE->getArg(0), 0};
+ SourceArgExpr Src = {CE->getArg(1), 1};
+ SizeArgExpr Size = {CE->getArg(2), 2};
+
+ ProgramStateRef State = C.getState();
- evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true);
+ constexpr bool IsRestricted = true;
+ constexpr bool IsMempcpy = false;
+ evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy);
}
void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {
// void *mempcpy(void *restrict dst, const void *restrict src, size_t n);
// The return value is a pointer to the byte following the last written byte.
- const Expr *Dest = CE->getArg(0);
- ProgramStateRef state = C.getState();
+ DestinationArgExpr Dest = {CE->getArg(0), 0};
+ SourceArgExpr Src = {CE->getArg(1), 1};
+ SizeArgExpr Size = {CE->getArg(2), 2};
- evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true, true);
+ constexpr bool IsRestricted = true;
+ constexpr bool IsMempcpy = true;
+ evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy);
}
void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
// void *memmove(void *dst, const void *src, size_t n);
// The return value is the address of the destination buffer.
- const Expr *Dest = CE->getArg(0);
- ProgramStateRef state = C.getState();
+ DestinationArgExpr Dest = {CE->getArg(0), 0};
+ SourceArgExpr Src = {CE->getArg(1), 1};
+ SizeArgExpr Size = {CE->getArg(2), 2};
- evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1));
+ constexpr bool IsRestricted = false;
+ constexpr bool IsMempcpy = false;
+ evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy);
}
void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const {
// void bcopy(const void *src, void *dst, size_t n);
- evalCopyCommon(C, CE, C.getState(),
- CE->getArg(2), CE->getArg(1), CE->getArg(0));
+ SourceArgExpr Src(CE->getArg(0), 0);
+ DestinationArgExpr Dest = {CE->getArg(1), 1};
+ SizeArgExpr Size = {CE->getArg(2), 2};
+
+ constexpr bool IsRestricted = false;
+ constexpr bool IsMempcpy = false;
+ evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy);
}
void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
// int memcmp(const void *s1, const void *s2, size_t n);
CurrentFunctionDescription = "memory comparison function";
- const Expr *Left = CE->getArg(0);
- const Expr *Right = CE->getArg(1);
- const Expr *Size = CE->getArg(2);
+ AnyArgExpr Left = {CE->getArg(0), 0};
+ AnyArgExpr Right = {CE->getArg(1), 1};
+ SizeArgExpr Size = {CE->getArg(2), 2};
- ProgramStateRef state = C.getState();
- SValBuilder &svalBuilder = C.getSValBuilder();
+ ProgramStateRef State = C.getState();
+ SValBuilder &Builder = C.getSValBuilder();
+ const LocationContext *LCtx = C.getLocationContext();
// See if the size argument is zero.
- const LocationContext *LCtx = C.getLocationContext();
- SVal sizeVal = state->getSVal(Size, LCtx);
- QualType sizeTy = Size->getType();
+ SVal sizeVal = State->getSVal(Size.Expression, LCtx);
+ QualType sizeTy = Size.Expression->getType();
ProgramStateRef stateZeroSize, stateNonZeroSize;
std::tie(stateZeroSize, stateNonZeroSize) =
- assumeZero(C, state, sizeVal, sizeTy);
+ assumeZero(C, State, sizeVal, sizeTy);
// If the size can be zero, the result will be 0 in that case, and we don't
// have to check either of the buffers.
if (stateZeroSize) {
- state = stateZeroSize;
- state = state->BindExpr(CE, LCtx,
- svalBuilder.makeZeroVal(CE->getType()));
- C.addTransition(state);
+ State = stateZeroSize;
+ State = State->BindExpr(CE, LCtx, Builder.makeZeroVal(CE->getType()));
+ C.addTransition(State);
}
// If the size can be nonzero, we have to check the other arguments.
if (stateNonZeroSize) {
- state = stateNonZeroSize;
+ State = stateNonZeroSize;
// If we know the two buffers are the same, we know the result is 0.
// First, get the two buffers' addresses. Another checker will have already
// made sure they're not undefined.
DefinedOrUnknownSVal LV =
- state->getSVal(Left, LCtx).castAs<DefinedOrUnknownSVal>();
+ State->getSVal(Left.Expression, LCtx).castAs<DefinedOrUnknownSVal>();
DefinedOrUnknownSVal RV =
- state->getSVal(Right, LCtx).castAs<DefinedOrUnknownSVal>();
+ State->getSVal(Right.Expression, LCtx).castAs<DefinedOrUnknownSVal>();
// See if they are the same.
- DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV);
- ProgramStateRef StSameBuf, StNotSameBuf;
- std::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf);
+ ProgramStateRef SameBuffer, NotSameBuffer;
+ std::tie(SameBuffer, NotSameBuffer) =
+ State->assume(Builder.evalEQ(State, LV, RV));
// If the two arguments are the same buffer, we know the result is 0,
// and we only need to check one size.
- if (StSameBuf && !StNotSameBuf) {
- state = StSameBuf;
- state = CheckBufferAccess(C, state, Size, Left);
- if (state) {
- state = StSameBuf->BindExpr(CE, LCtx,
- svalBuilder.makeZeroVal(CE->getType()));
- C.addTransition(state);
+ if (SameBuffer && !NotSameBuffer) {
+ State = SameBuffer;
+ State = CheckBufferAccess(C, State, Left, Size, AccessKind::read);
+ if (State) {
+ State =
+ SameBuffer->BindExpr(CE, LCtx, Builder.makeZeroVal(CE->getType()));
+ C.addTransition(State);
}
return;
}
// If the two arguments might be different buffers, we have to check
// the size of both of them.
- assert(StNotSameBuf);
- state = CheckBufferAccess(C, state, Size, Left, Right);
- if (state) {
+ assert(NotSameBuffer);
+ State = CheckBufferAccess(C, State, Right, Size, AccessKind::read);
+ State = CheckBufferAccess(C, State, Left, Size, AccessKind::read);
+ if (State) {
// The return value is the comparison result, which we don't know.
- SVal CmpV =
- svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
- state = state->BindExpr(CE, LCtx, CmpV);
- C.addTransition(state);
+ SVal CmpV = Builder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
+ State = State->BindExpr(CE, LCtx, CmpV);
+ C.addTransition(State);
}
}
}
@@ -1384,15 +1387,14 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
}
// Check that the string argument is non-null.
- const Expr *Arg = CE->getArg(0);
- SVal ArgVal = state->getSVal(Arg, LCtx);
-
- state = checkNonNull(C, state, Arg, ArgVal, 1);
+ AnyArgExpr Arg = {CE->getArg(0), 0};
+ SVal ArgVal = state->getSVal(Arg.Expression, LCtx);
+ state = checkNonNull(C, state, Arg, ArgVal);
if (!state)
return;
- SVal strLength = getCStringLength(C, state, Arg, ArgVal);
+ SVal strLength = getCStringLength(C, state, Arg.Expression, ArgVal);
// If the argument isn't a valid C string, there's no valid state to
// transition to.
@@ -1540,30 +1542,30 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
CurrentFunctionDescription = "string copy function";
else
CurrentFunctionDescription = "string concatenation function";
+
ProgramStateRef state = C.getState();
const LocationContext *LCtx = C.getLocationContext();
// Check that the destination is non-null.
- const Expr *Dst = CE->getArg(0);
- SVal DstVal = state->getSVal(Dst, LCtx);
-
- state = checkNonNull(C, state, Dst, DstVal, 1);
+ DestinationArgExpr Dst = {CE->getArg(0), 0};
+ SVal DstVal = state->getSVal(Dst.Expression, LCtx);
+ state = checkNonNull(C, state, Dst, DstVal);
if (!state)
return;
// Check that the source is non-null.
- const Expr *srcExpr = CE->getArg(1);
- SVal srcVal = state->getSVal(srcExpr, LCtx);
- state = checkNonNull(C, state, srcExpr, srcVal, 2);
+ SourceArgExpr srcExpr = {CE->getArg(1), 1};
+ SVal srcVal = state->getSVal(srcExpr.Expression, LCtx);
+ state = checkNonNull(C, state, srcExpr, srcVal);
if (!state)
return;
// Get the string length of the source.
- SVal strLength = getCStringLength(C, state, srcExpr, srcVal);
+ SVal strLength = getCStringLength(C, state, srcExpr.Expression, srcVal);
Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
// Get the string length of the destination buffer.
- SVal dstStrLength = getCStringLength(C, state, Dst, DstVal);
+ SVal dstStrLength = getCStringLength(C, state, Dst.Expression, DstVal);
Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>();
// If the source isn't a valid C string, give up.
@@ -1581,8 +1583,13 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
SVal maxLastElementIndex = UnknownVal();
const char *boundWarning = nullptr;
- state = CheckOverlap(C, state, IsBounded ? CE->getArg(2) : CE->getArg(1), Dst,
- srcExpr);
+ // FIXME: Why do we choose the srcExpr if the access has no size?
+ // Note that the 3rd argument of the call would be the size parameter.
+ SizeArgExpr SrcExprAsSizeDummy = {srcExpr.Expression, srcExpr.ArgumentIndex};
+ state = CheckOverlap(
+ C, state,
+ (IsBounded ? SizeArgExpr{CE->getArg(2), 2} : SrcExprAsSizeDummy), Dst,
+ srcExpr);
if (!state)
return;
@@ -1590,11 +1597,12 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// If the function is strncpy, strncat, etc... it is bounded.
if (IsBounded) {
// Get the max number of characters to copy.
- const Expr *lenExpr = CE->getArg(2);
- SVal lenVal = state->getSVal(lenExpr, LCtx);
+ SizeArgExpr lenExpr = {CE->getArg(2), 2};
+ SVal lenVal = state->getSVal(lenExpr.Expression, LCtx);
// Protect against misdeclared strncpy().
- lenVal = svalBuilder.evalCast(lenVal, sizeTy, lenExpr->getType());
+ lenVal =
+ svalBuilder.evalCast(lenVal, sizeTy, lenExpr.Expression->getType());
Optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>();
@@ -1837,19 +1845,17 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// record the new string length.
if (Optional<loc::MemRegionVal> dstRegVal =
DstVal.getAs<loc::MemRegionVal>()) {
- QualType ptrTy = Dst->getType();
+ QualType ptrTy = Dst.Expression->getType();
// If we have an exact value on a bounded copy, use that to check for
// overflows, rather than our estimate about how much is actually copied.
- if (boundWarning) {
- if (Optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) {
- SVal maxLastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal,
- *maxLastNL, ptrTy);
- state = CheckLocation(C, state, CE->getArg(2), maxLastElement,
- boundWarning);
- if (!state)
- return;
- }
+ if (Optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) {
+ SVal maxLastElement =
+ svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *maxLastNL, ptrTy);
+
+ state = CheckLocation(C, state, Dst, maxLastElement, AccessKind::write);
+ if (!state)
+ return;
}
// Then, if the final length is known...
@@ -1859,9 +1865,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// ...and we haven't checked the bound, we'll check the actual copy.
if (!boundWarning) {
- const char * const warningMsg =
- "String copy function overflows destination buffer";
- state = CheckLocation(C, state, Dst, lastElement, warningMsg);
+ state = CheckLocation(C, state, Dst, lastElement, AccessKind::write);
if (!state)
return;
}
@@ -1878,13 +1882,13 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// can use LazyCompoundVals to copy the source values into the destination.
// This would probably remove any existing bindings past the end of the
// string, but that's still an improvement over blank invalidation.
- state = InvalidateBuffer(C, state, Dst, *dstRegVal,
- /*IsSourceBuffer*/false, nullptr);
+ state = InvalidateBuffer(C, state, Dst.Expression, *dstRegVal,
+ /*IsSourceBuffer*/ false, nullptr);
// Invalidate the source (const-invalidation without const-pointer-escaping
// the address of the top-level region).
- state = InvalidateBuffer(C, state, srcExpr, srcVal, /*IsSourceBuffer*/true,
- nullptr);
+ state = InvalidateBuffer(C, state, srcExpr.Expression, srcVal,
+ /*IsSourceBuffer*/ true, nullptr);
// Set the C string length of the destination, if we know it.
if (IsBounded && (appendK == ConcatFnKind::none)) {
@@ -1941,34 +1945,34 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
const LocationContext *LCtx = C.getLocationContext();
// Check that the first string is non-null
- const Expr *s1 = CE->getArg(0);
- SVal s1Val = state->getSVal(s1, LCtx);
- state = checkNonNull(C, state, s1, s1Val, 1);
+ AnyArgExpr Left = {CE->getArg(0), 0};
+ SVal LeftVal = state->getSVal(Left.Expression, LCtx);
+ state = checkNonNull(C, state, Left, LeftVal);
if (!state)
return;
// Check that the second string is non-null.
- const Expr *s2 = CE->getArg(1);
- SVal s2Val = state->getSVal(s2, LCtx);
- state = checkNonNull(C, state, s2, s2Val, 2);
+ AnyArgExpr Right = {CE->getArg(1), 1};
+ SVal RightVal = state->getSVal(Right.Expression, LCtx);
+ state = checkNonNull(C, state, Right, RightVal);
if (!state)
return;
// Get the string length of the first string or give up.
- SVal s1Length = getCStringLength(C, state, s1, s1Val);
- if (s1Length.isUndef())
+ SVal LeftLength = getCStringLength(C, state, Left.Expression, LeftVal);
+ if (LeftLength.isUndef())
return;
// Get the string length of the second string or give up.
- SVal s2Length = getCStringLength(C, state, s2, s2Val);
- if (s2Length.isUndef())
+ SVal RightLength = getCStringLength(C, state, Right.Expression, RightVal);
+ if (RightLength.isUndef())
return;
// If we know the two buffers are the same, we know the result is 0.
// First, get the two buffers' addresses. Another checker will have already
// made sure they're not undefined.
- DefinedOrUnknownSVal LV = s1Val.castAs<DefinedOrUnknownSVal>();
- DefinedOrUnknownSVal RV = s2Val.castAs<DefinedOrUnknownSVal>();
+ DefinedOrUnknownSVal LV = LeftVal.castAs<DefinedOrUnknownSVal>();
+ DefinedOrUnknownSVal RV = RightVal.castAs<DefinedOrUnknownSVal>();
// See if they are the same.
SValBuilder &svalBuilder = C.getSValBuilder();
@@ -1995,15 +1999,17 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
// For now, we only do this if they're both known string literals.
// Attempt to extract string literals from both expressions.
- const StringLiteral *s1StrLiteral = getCStringLiteral(C, state, s1, s1Val);
- const StringLiteral *s2StrLiteral = getCStringLiteral(C, state, s2, s2Val);
+ const StringLiteral *LeftStrLiteral =
+ getCStringLiteral(C, state, Left.Expression, LeftVal);
+ const StringLiteral *RightStrLiteral =
+ getCStringLiteral(C, state, Right.Expression, RightVal);
bool canComputeResult = false;
SVal resultVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx,
C.blockCount());
- if (s1StrLiteral && s2StrLiteral) {
- StringRef s1StrRef = s1StrLiteral->getString();
- StringRef s2StrRef = s2StrLiteral->getString();
+ if (LeftStrLiteral && RightStrLiteral) {
+ StringRef LeftStrRef = LeftStrLiteral->getString();
+ StringRef RightStrRef = RightStrLiteral->getString();
if (IsBounded) {
// Get the max number of characters to compare.
@@ -2013,8 +2019,8 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
// If the length is known, we can get the right substrings.
if (const llvm::APSInt *len = svalBuilder.getKnownValue(state, lenVal)) {
// Create substrings of each to compare the prefix.
- s1StrRef = s1StrRef.substr(0, (size_t)len->getZExtValue());
- s2StrRef = s2StrRef.substr(0, (size_t)len->getZExtValue());
+ LeftStrRef = LeftStrRef.substr(0, (size_t)len->getZExtValue());
+ RightStrRef = RightStrRef.substr(0, (size_t)len->getZExtValue());
canComputeResult = true;
}
} else {
@@ -2024,17 +2030,17 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
if (canComputeResult) {
// Real strcmp stops at null characters.
- size_t s1Term = s1StrRef.find('\0');
+ size_t s1Term = LeftStrRef.find('\0');
if (s1Term != StringRef::npos)
- s1StrRef = s1StrRef.substr(0, s1Term);
+ LeftStrRef = LeftStrRef.substr(0, s1Term);
- size_t s2Term = s2StrRef.find('\0');
+ size_t s2Term = RightStrRef.find('\0');
if (s2Term != StringRef::npos)
- s2StrRef = s2StrRef.substr(0, s2Term);
+ RightStrRef = RightStrRef.substr(0, s2Term);
// Use StringRef's comparison methods to compute the actual result.
- int compareRes = IgnoreCase ? s1StrRef.compare_lower(s2StrRef)
- : s1StrRef.compare(s2StrRef);
+ int compareRes = IgnoreCase ? LeftStrRef.compare_lower(RightStrRef)
+ : LeftStrRef.compare(RightStrRef);
// The strcmp function returns an integer greater than, equal to, or less
// than zero, [c11, p7.24.4.2].
@@ -2064,8 +2070,9 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {
//char *strsep(char **stringp, const char *delim);
// Sanity: does the search string parameter match the return type?
- const Expr *SearchStrPtr = CE->getArg(0);
- QualType CharPtrTy = SearchStrPtr->getType()->getPointeeType();
+ SourceArgExpr SearchStrPtr = {CE->getArg(0), 0};
+
+ QualType CharPtrTy = SearchStrPtr.Expression->getType()->getPointeeType();
if (CharPtrTy.isNull() ||
CE->getType().getUnqualifiedType() != CharPtrTy.getUnqualifiedType())
return;
@@ -2076,15 +2083,15 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {
// Check that the search string pointer is non-null (though it may point to
// a null string).
- SVal SearchStrVal = State->getSVal(SearchStrPtr, LCtx);
- State = checkNonNull(C, State, SearchStrPtr, SearchStrVal, 1);
+ SVal SearchStrVal = State->getSVal(SearchStrPtr.Expression, LCtx);
+ State = checkNonNull(C, State, SearchStrPtr, SearchStrVal);
if (!State)
return;
// Check that the delimiter string is non-null.
- const Expr *DelimStr = CE->getArg(1);
- SVal DelimStrVal = State->getSVal(DelimStr, LCtx);
- State = checkNonNull(C, State, DelimStr, DelimStrVal, 2);
+ AnyArgExpr DelimStr = {CE->getArg(1), 1};
+ SVal DelimStrVal = State->getSVal(DelimStr.Expression, LCtx);
+ State = checkNonNull(C, State, DelimStr, DelimStrVal);
if (!State)
return;
@@ -2096,8 +2103,8 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {
// Invalidate the search string, representing the change of one delimiter
// character to NUL.
- State = InvalidateBuffer(C, State, SearchStrPtr, Result,
- /*IsSourceBuffer*/false, nullptr);
+ State = InvalidateBuffer(C, State, SearchStrPtr.Expression, Result,
+ /*IsSourceBuffer*/ false, nullptr);
// Overwrite the search string pointer. The new value is either an address
// further along in the same string, or NULL if there are no more tokens.
@@ -2158,65 +2165,67 @@ void CStringChecker::evalStdCopyCommon(CheckerContext &C,
}
void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const {
+ // void *memset(void *s, int c, size_t n);
CurrentFunctionDescription = "memory set function";
- const Expr *Mem = CE->getArg(0);
- const Expr *CharE = CE->getArg(1);
- const Expr *Size = CE->getArg(2);
+ DestinationArgExpr Buffer = {CE->getArg(0), 0};
+ AnyArgExpr CharE = {CE->getArg(1), 1};
+ SizeArgExpr Size = {CE->getArg(2), 2};
+
ProgramStateRef State = C.getState();
// See if the size argument is zero.
const LocationContext *LCtx = C.getLocationContext();
- SVal SizeVal = State->getSVal(Size, LCtx);
- QualType SizeTy = Size->getType();
+ SVal SizeVal = C.getSVal(Size.Expression);
+ QualType SizeTy = Size.Expression->getType();
- ProgramStateRef StateZeroSize, StateNonZeroSize;
- std::tie(StateZeroSize, StateNonZeroSize) =
- assumeZero(C, State, SizeVal, SizeTy);
+ ProgramStateRef ZeroSize, NonZeroSize;
+ std::tie(ZeroSize, NonZeroSize) = assumeZero(C, State, SizeVal, SizeTy);
// Get the value of the memory area.
- SVal MemVal = State->getSVal(Mem, LCtx);
+ SVal BufferPtrVal = C.getSVal(Buffer.Expression);
// If the size is zero, there won't be any actual memory access, so
- // just bind the return value to the Mem buffer and return.
- if (StateZeroSize && !StateNonZeroSize) {
- StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, MemVal);
- C.addTransition(StateZeroSize);
+ // just bind the return value to the buffer and return.
+ if (ZeroSize && !NonZeroSize) {
+ ZeroSize = ZeroSize->BindExpr(CE, LCtx, BufferPtrVal);
+ C.addTransition(ZeroSize);
return;
}
// Ensure the memory area is not null.
// If it is NULL there will be a NULL pointer dereference.
- State = checkNonNull(C, StateNonZeroSize, Mem, MemVal, 1);
+ State = checkNonNull(C, NonZeroSize, Buffer, BufferPtrVal);
if (!State)
return;
- State = CheckBufferAccess(C, State, Size, Mem);
+ State = CheckBufferAccess(C, State, Buffer, Size, AccessKind::write);
if (!State)
return;
// According to the values of the arguments, bind the value of the second
// argument to the destination buffer and set string length, or just
// invalidate the destination buffer.
- if (!memsetAux(Mem, C.getSVal(CharE), Size, C, State))
+ if (!memsetAux(Buffer.Expression, C.getSVal(CharE.Expression),
+ Size.Expression, C, State))
return;
- State = State->BindExpr(CE, LCtx, MemVal);
+ State = State->BindExpr(CE, LCtx, BufferPtrVal);
C.addTransition(State);
}
void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const {
CurrentFunctionDescription = "memory clearance function";
- const Expr *Mem = CE->getArg(0);
- const Expr *Size = CE->getArg(1);
+ DestinationArgExpr Buffer = {CE->getArg(0), 0};
+ SizeArgExpr Size = {CE->getArg(1), 1};
SVal Zero = C.getSValBuilder().makeZeroVal(C.getASTContext().IntTy);
ProgramStateRef State = C.getState();
// See if the size argument is zero.
- SVal SizeVal = C.getSVal(Size);
- QualType SizeTy = Size->getType();
+ SVal SizeVal = C.getSVal(Size.Expression);
+ QualType SizeTy = Size.Expression->getType();
ProgramStateRef StateZeroSize, StateNonZeroSize;
std::tie(StateZeroSize, StateNonZeroSize) =
@@ -2230,19 +2239,19 @@ void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const {
}
// Get the value of the memory area.
- SVal MemVal = C.getSVal(Mem);
+ SVal MemVal = C.getSVal(Buffer.Expression);
// Ensure the memory area is not null.
// If it is NULL there will be a NULL pointer dereference.
- State = checkNonNull(C, StateNonZeroSize, Mem, MemVal, 1);
+ State = checkNonNull(C, StateNonZeroSize, Buffer, MemVal);
if (!State)
return;
- State = CheckBufferAccess(C, State, Size, Mem);
+ State = CheckBufferAccess(C, State, Buffer, Size, AccessKind::write);
if (!State)
return;
- if (!memsetAux(Mem, Zero, Size, C, State))
+ if (!memsetAux(Buffer.Expression, Zero, Size.Expression, C, State))
return;
C.addTransition(State);
@@ -2434,7 +2443,7 @@ void ento::registerCStringModeling(CheckerManager &Mgr) {
Mgr.registerChecker<CStringChecker>();
}
-bool ento::shouldRegisterCStringModeling(const LangOptions &LO) {
+bool ento::shouldRegisterCStringModeling(const CheckerManager &mgr) {
return true;
}
@@ -2445,7 +2454,7 @@ bool ento::shouldRegisterCStringModeling(const LangOptions &LO) {
checker->Filter.CheckName##name = mgr.getCurrentCheckerName(); \
} \
\
- bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
+ bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
REGISTER_CHECKER(CStringNullArg)
REGISTER_CHECKER(CStringOutOfBounds)
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
index d84fcc69a492..888724f7ea3b 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
@@ -291,6 +291,6 @@ void ento::registerCStringSyntaxChecker(CheckerManager &mgr) {
mgr.registerChecker<CStringSyntaxChecker>();
}
-bool ento::shouldRegisterCStringSyntaxChecker(const LangOptions &LO) {
+bool ento::shouldRegisterCStringSyntaxChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp
index 01f5b9c889e3..24776338ce10 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp
@@ -53,21 +53,21 @@ void CXXSelfAssignmentChecker::checkBeginFunction(CheckerContext &C) const {
ProgramStateRef SelfAssignState = State->bindLoc(Param, ThisVal, LCtx);
const NoteTag *SelfAssignTag =
- C.getNoteTag([MD](BugReport &BR) -> std::string {
+ C.getNoteTag([MD](PathSensitiveBugReport &BR) -> std::string {
SmallString<256> Msg;
llvm::raw_svector_ostream Out(Msg);
Out << "Assuming " << MD->getParamDecl(0)->getName() << " == *this";
- return Out.str();
+ return std::string(Out.str());
});
C.addTransition(SelfAssignState, SelfAssignTag);
ProgramStateRef NonSelfAssignState = State->bindLoc(Param, ParamVal, LCtx);
const NoteTag *NonSelfAssignTag =
- C.getNoteTag([MD](BugReport &BR) -> std::string {
+ C.getNoteTag([MD](PathSensitiveBugReport &BR) -> std::string {
SmallString<256> Msg;
llvm::raw_svector_ostream Out(Msg);
Out << "Assuming " << MD->getParamDecl(0)->getName() << " != *this";
- return Out.str();
+ return std::string(Out.str());
});
C.addTransition(NonSelfAssignState, NonSelfAssignTag);
}
@@ -76,6 +76,6 @@ void ento::registerCXXSelfAssignmentChecker(CheckerManager &Mgr) {
Mgr.registerChecker<CXXSelfAssignmentChecker>();
}
-bool ento::shouldRegisterCXXSelfAssignmentChecker(const LangOptions &LO) {
+bool ento::shouldRegisterCXXSelfAssignmentChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index 2fcb765cd4ee..3e46e2372516 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -11,9 +11,10 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/AST/ParentMap.h"
#include "clang/Basic/TargetInfo.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"
@@ -21,6 +22,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
@@ -29,11 +31,8 @@ using namespace ento;
namespace {
class CallAndMessageChecker
- : public Checker< check::PreStmt<CallExpr>,
- check::PreStmt<CXXDeleteExpr>,
- check::PreObjCMessage,
- check::ObjCMessageNil,
- check::PreCall > {
+ : public Checker<check::PreObjCMessage, check::ObjCMessageNil,
+ check::PreCall> {
mutable std::unique_ptr<BugType> BT_call_null;
mutable std::unique_ptr<BugType> BT_call_undef;
mutable std::unique_ptr<BugType> BT_cxx_call_null;
@@ -48,11 +47,37 @@ class CallAndMessageChecker
mutable std::unique_ptr<BugType> BT_call_few_args;
public:
- DefaultBool Check_CallAndMessageUnInitRefArg;
- CheckerNameRef CheckName_CallAndMessageUnInitRefArg;
+ // These correspond with the checker options. Looking at other checkers such
+ // as MallocChecker and CStringChecker, this is similar as to how they pull
+ // off having a modeling class, but emitting diagnostics under a smaller
+ // checker's name that can be safely disabled without disturbing the
+ // underlaying modeling engine.
+ // The reason behind having *checker options* rather then actual *checkers*
+ // here is that CallAndMessage is among the oldest checkers out there, and can
+ // be responsible for the majority of the reports on any given project. This
+ // is obviously not ideal, but changing checker name has the consequence of
+ // changing the issue hashes associated with the reports, and databases
+ // relying on this (CodeChecker, for instance) would suffer greatly.
+ // If we ever end up making changes to the issue hash generation algorithm, or
+ // the warning messages here, we should totally jump on the opportunity to
+ // convert these to actual checkers.
+ enum CheckKind {
+ CK_FunctionPointer,
+ CK_ParameterCount,
+ CK_CXXThisMethodCall,
+ CK_CXXDeallocationArg,
+ CK_ArgInitializedness,
+ CK_ArgPointeeInitializedness,
+ CK_NilReceiver,
+ CK_UndefReceiver,
+ CK_NumCheckKinds
+ };
+
+ DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ // The original core.CallAndMessage checker name. This should rather be an
+ // array, as seen in MallocChecker and CStringChecker.
+ CheckerNameRef OriginalName;
- void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
- void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
/// Fill in the return value that results from messaging nil based on the
@@ -62,6 +87,25 @@ public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ ProgramStateRef checkFunctionPointerCall(const CallExpr *CE,
+ CheckerContext &C,
+ ProgramStateRef State) const;
+
+ ProgramStateRef checkCXXMethodCall(const CXXInstanceCall *CC,
+ CheckerContext &C,
+ ProgramStateRef State) const;
+
+ ProgramStateRef checkParameterCount(const CallEvent &Call, CheckerContext &C,
+ ProgramStateRef State) const;
+
+ ProgramStateRef checkCXXDeallocation(const CXXDeallocatorCall *DC,
+ CheckerContext &C,
+ ProgramStateRef State) const;
+
+ ProgramStateRef checkArgInitializedness(const CallEvent &Call,
+ CheckerContext &C,
+ ProgramStateRef State) const;
+
private:
bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange,
const Expr *ArgEx, int ArgumentNumber,
@@ -79,7 +123,7 @@ private:
void LazyInit_BT(const char *desc, std::unique_ptr<BugType> &BT) const {
if (!BT)
- BT.reset(new BuiltinBug(this, desc));
+ BT.reset(new BuiltinBug(OriginalName, desc));
}
bool uninitRefOrPointer(CheckerContext &C, const SVal &V,
SourceRange ArgRange, const Expr *ArgEx,
@@ -144,7 +188,10 @@ 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 (!Check_CallAndMessageUnInitRefArg)
+
+ // The pointee being uninitialized is a sign of code smell, not a bug, no need
+ // to sink here.
+ if (!ChecksEnabled[CK_ArgPointeeInitializedness])
return false;
// No parameter declaration available, i.e. variadic function argument.
@@ -246,6 +293,10 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
return true;
if (V.isUndef()) {
+ if (!ChecksEnabled[CK_ArgInitializedness]) {
+ C.addSink();
+ return true;
+ }
if (ExplodedNode *N = C.generateErrorNode()) {
LazyInit_BT(BD, BT);
// Generate a report for this bug.
@@ -272,6 +323,10 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
D->getStore());
if (F.Find(D->getRegion())) {
+ if (!ChecksEnabled[CK_ArgInitializedness]) {
+ C.addSink();
+ return true;
+ }
if (ExplodedNode *N = C.generateErrorNode()) {
LazyInit_BT(BD, BT);
SmallString<512> Str;
@@ -311,126 +366,158 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
return false;
}
-void CallAndMessageChecker::checkPreStmt(const CallExpr *CE,
- CheckerContext &C) const{
+ProgramStateRef CallAndMessageChecker::checkFunctionPointerCall(
+ const CallExpr *CE, CheckerContext &C, ProgramStateRef State) const {
const Expr *Callee = CE->getCallee()->IgnoreParens();
- ProgramStateRef State = C.getState();
const LocationContext *LCtx = C.getLocationContext();
SVal L = State->getSVal(Callee, LCtx);
if (L.isUndef()) {
+ if (!ChecksEnabled[CK_FunctionPointer]) {
+ C.addSink(State);
+ return nullptr;
+ }
if (!BT_call_undef)
BT_call_undef.reset(new BuiltinBug(
- this, "Called function pointer is an uninitialized pointer value"));
+ OriginalName,
+ "Called function pointer is an uninitialized pointer value"));
emitBadCall(BT_call_undef.get(), C, Callee);
- return;
+ return nullptr;
}
ProgramStateRef StNonNull, StNull;
std::tie(StNonNull, StNull) = State->assume(L.castAs<DefinedOrUnknownSVal>());
if (StNull && !StNonNull) {
+ if (!ChecksEnabled[CK_FunctionPointer]) {
+ C.addSink(StNull);
+ return nullptr;
+ }
if (!BT_call_null)
BT_call_null.reset(new BuiltinBug(
- this, "Called function pointer is null (null dereference)"));
+ OriginalName, "Called function pointer is null (null dereference)"));
emitBadCall(BT_call_null.get(), C, Callee);
- return;
+ return nullptr;
}
- C.addTransition(StNonNull);
+ return StNonNull;
}
-void CallAndMessageChecker::checkPreStmt(const CXXDeleteExpr *DE,
- CheckerContext &C) const {
+ProgramStateRef CallAndMessageChecker::checkParameterCount(
+ const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const {
- SVal Arg = C.getSVal(DE->getArgument());
- if (Arg.isUndef()) {
- StringRef Desc;
- ExplodedNode *N = C.generateErrorNode();
- if (!N)
- return;
- if (!BT_cxx_delete_undef)
- BT_cxx_delete_undef.reset(
- new BuiltinBug(this, "Uninitialized argument value"));
- if (DE->isArrayFormAsWritten())
- Desc = "Argument to 'delete[]' is uninitialized";
- else
- Desc = "Argument to 'delete' is uninitialized";
- BugType *BT = BT_cxx_delete_undef.get();
- auto R = std::make_unique<PathSensitiveBugReport>(*BT, Desc, N);
- bugreporter::trackExpressionValue(N, DE, *R);
- C.emitReport(std::move(R));
- return;
+ // If we have a function or block declaration, we can make sure we pass
+ // enough parameters.
+ unsigned Params = Call.parameters().size();
+ if (Call.getNumArgs() >= Params)
+ return State;
+
+ if (!ChecksEnabled[CK_ParameterCount]) {
+ C.addSink(State);
+ return nullptr;
+ }
+
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return nullptr;
+
+ LazyInit_BT("Function call with too few arguments", BT_call_few_args);
+
+ SmallString<512> Str;
+ llvm::raw_svector_ostream os(Str);
+ if (isa<AnyFunctionCall>(Call)) {
+ os << "Function ";
+ } else {
+ assert(isa<BlockCall>(Call));
+ os << "Block ";
}
+ os << "taking " << Params << " argument" << (Params == 1 ? "" : "s")
+ << " is called with fewer (" << Call.getNumArgs() << ")";
+
+ C.emitReport(
+ std::make_unique<PathSensitiveBugReport>(*BT_call_few_args, os.str(), N));
+ return nullptr;
}
-void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
- CheckerContext &C) const {
- ProgramStateRef State = C.getState();
+ProgramStateRef CallAndMessageChecker::checkCXXMethodCall(
+ const CXXInstanceCall *CC, CheckerContext &C, ProgramStateRef State) const {
- // If this is a call to a C++ method, check if the callee is null or
- // undefined.
- if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) {
- SVal V = CC->getCXXThisVal();
- if (V.isUndef()) {
- if (!BT_cxx_call_undef)
- BT_cxx_call_undef.reset(
- new BuiltinBug(this, "Called C++ object pointer is uninitialized"));
- emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr());
- return;
+ SVal V = CC->getCXXThisVal();
+ if (V.isUndef()) {
+ if (!ChecksEnabled[CK_CXXThisMethodCall]) {
+ C.addSink(State);
+ return nullptr;
}
+ if (!BT_cxx_call_undef)
+ BT_cxx_call_undef.reset(new BuiltinBug(
+ OriginalName, "Called C++ object pointer is uninitialized"));
+ emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr());
+ return nullptr;
+ }
- ProgramStateRef StNonNull, StNull;
- std::tie(StNonNull, StNull) =
- State->assume(V.castAs<DefinedOrUnknownSVal>());
+ ProgramStateRef StNonNull, StNull;
+ std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
- if (StNull && !StNonNull) {
- if (!BT_cxx_call_null)
- BT_cxx_call_null.reset(
- new BuiltinBug(this, "Called C++ object pointer is null"));
- emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr());
- return;
+ if (StNull && !StNonNull) {
+ if (!ChecksEnabled[CK_CXXThisMethodCall]) {
+ C.addSink(StNull);
+ return nullptr;
}
-
- State = StNonNull;
+ if (!BT_cxx_call_null)
+ BT_cxx_call_null.reset(
+ new BuiltinBug(OriginalName, "Called C++ object pointer is null"));
+ emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr());
+ return nullptr;
}
- const Decl *D = Call.getDecl();
- if (D && (isa<FunctionDecl>(D) || isa<BlockDecl>(D))) {
- // If we have a function or block declaration, we can make sure we pass
- // enough parameters.
- unsigned Params = Call.parameters().size();
- if (Call.getNumArgs() < Params) {
- ExplodedNode *N = C.generateErrorNode();
- if (!N)
- return;
-
- LazyInit_BT("Function call with too few arguments", BT_call_few_args);
-
- SmallString<512> Str;
- llvm::raw_svector_ostream os(Str);
- if (isa<FunctionDecl>(D)) {
- os << "Function ";
- } else {
- assert(isa<BlockDecl>(D));
- os << "Block ";
- }
- os << "taking " << Params << " argument"
- << (Params == 1 ? "" : "s") << " is called with fewer ("
- << Call.getNumArgs() << ")";
+ return StNonNull;
+}
- C.emitReport(std::make_unique<PathSensitiveBugReport>(*BT_call_few_args,
- os.str(), N));
- }
+ProgramStateRef
+CallAndMessageChecker::checkCXXDeallocation(const CXXDeallocatorCall *DC,
+ CheckerContext &C,
+ ProgramStateRef State) const {
+ const CXXDeleteExpr *DE = DC->getOriginExpr();
+ assert(DE);
+ SVal Arg = C.getSVal(DE->getArgument());
+ if (!Arg.isUndef())
+ return State;
+
+ if (!ChecksEnabled[CK_CXXDeallocationArg]) {
+ C.addSink(State);
+ return nullptr;
}
+ StringRef Desc;
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return nullptr;
+ if (!BT_cxx_delete_undef)
+ BT_cxx_delete_undef.reset(
+ new BuiltinBug(OriginalName, "Uninitialized argument value"));
+ if (DE->isArrayFormAsWritten())
+ Desc = "Argument to 'delete[]' is uninitialized";
+ else
+ Desc = "Argument to 'delete' is uninitialized";
+ BugType *BT = BT_cxx_delete_undef.get();
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, Desc, N);
+ bugreporter::trackExpressionValue(N, DE, *R);
+ C.emitReport(std::move(R));
+ return nullptr;
+}
+
+ProgramStateRef CallAndMessageChecker::checkArgInitializedness(
+ const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const {
+
+ const Decl *D = Call.getDecl();
+
// Don't check for uninitialized field values in arguments if the
// caller has a body that is available and we have the chance to inline it.
// This is a hack, but is a reasonable compromise betweens sometimes warning
// and sometimes not depending on if we decide to inline a function.
const bool checkUninitFields =
- !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody()));
+ !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody()));
std::unique_ptr<BugType> *BT;
if (isa<ObjCMethodCall>(Call))
@@ -441,13 +528,45 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) {
const ParmVarDecl *ParamDecl = nullptr;
- if(FD && i < FD->getNumParams())
+ if (FD && i < FD->getNumParams())
ParamDecl = FD->getParamDecl(i);
if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),
- Call.getArgExpr(i), i,
- checkUninitFields, Call, *BT, ParamDecl))
- return;
+ Call.getArgExpr(i), i, checkUninitFields, Call, *BT,
+ ParamDecl))
+ return nullptr;
}
+ return State;
+}
+
+void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()))
+ State = checkFunctionPointerCall(CE, C, State);
+
+ if (!State)
+ return;
+
+ if (Call.getDecl())
+ State = checkParameterCount(Call, C, State);
+
+ if (!State)
+ return;
+
+ if (const auto *CC = dyn_cast<CXXInstanceCall>(&Call))
+ State = checkCXXMethodCall(CC, C, State);
+
+ if (!State)
+ return;
+
+ if (const auto *DC = dyn_cast<CXXDeallocatorCall>(&Call))
+ State = checkCXXDeallocation(DC, C, State);
+
+ if (!State)
+ return;
+
+ State = checkArgInitializedness(Call, C, State);
// If we make it here, record our assumptions about the callee.
C.addTransition(State);
@@ -457,12 +576,16 @@ void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
CheckerContext &C) const {
SVal recVal = msg.getReceiverSVal();
if (recVal.isUndef()) {
+ if (!ChecksEnabled[CK_UndefReceiver]) {
+ C.addSink();
+ return;
+ }
if (ExplodedNode *N = C.generateErrorNode()) {
BugType *BT = nullptr;
switch (msg.getMessageKind()) {
case OCM_Message:
if (!BT_msg_undef)
- BT_msg_undef.reset(new BuiltinBug(this,
+ BT_msg_undef.reset(new BuiltinBug(OriginalName,
"Receiver in message expression "
"is an uninitialized value"));
BT = BT_msg_undef.get();
@@ -470,13 +593,15 @@ void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
case OCM_PropertyAccess:
if (!BT_objc_prop_undef)
BT_objc_prop_undef.reset(new BuiltinBug(
- this, "Property access on an uninitialized object pointer"));
+ OriginalName,
+ "Property access on an uninitialized object pointer"));
BT = BT_objc_prop_undef.get();
break;
case OCM_Subscript:
if (!BT_objc_subscript_undef)
BT_objc_subscript_undef.reset(new BuiltinBug(
- this, "Subscript access on an uninitialized object pointer"));
+ OriginalName,
+ "Subscript access on an uninitialized object pointer"));
BT = BT_objc_subscript_undef.get();
break;
}
@@ -503,10 +628,14 @@ void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg,
void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
const ObjCMethodCall &msg,
ExplodedNode *N) const {
+ if (!ChecksEnabled[CK_NilReceiver]) {
+ C.addSink();
+ return;
+ }
if (!BT_msg_ret)
- BT_msg_ret.reset(
- new BuiltinBug(this, "Receiver in message expression is 'nil'"));
+ BT_msg_ret.reset(new BuiltinBug(OriginalName,
+ "Receiver in message expression is 'nil'"));
const ObjCMessageExpr *ME = msg.getOriginExpr();
@@ -601,20 +730,34 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
C.addTransition(state);
}
-void ento::registerCallAndMessageChecker(CheckerManager &mgr) {
+void ento::registerCallAndMessageModeling(CheckerManager &mgr) {
mgr.registerChecker<CallAndMessageChecker>();
}
-bool ento::shouldRegisterCallAndMessageChecker(const LangOptions &LO) {
+bool ento::shouldRegisterCallAndMessageModeling(const CheckerManager &mgr) {
return true;
}
-void ento::registerCallAndMessageUnInitRefArg(CheckerManager &mgr) {
- CallAndMessageChecker *Checker = mgr.getChecker<CallAndMessageChecker>();
- Checker->Check_CallAndMessageUnInitRefArg = true;
- Checker->CheckName_CallAndMessageUnInitRefArg = mgr.getCurrentCheckerName();
+void ento::registerCallAndMessageChecker(CheckerManager &mgr) {
+ CallAndMessageChecker *checker = mgr.getChecker<CallAndMessageChecker>();
+
+ checker->OriginalName = mgr.getCurrentCheckerName();
+
+#define QUERY_CHECKER_OPTION(OPTION) \
+ checker->ChecksEnabled[CallAndMessageChecker::CK_##OPTION] = \
+ mgr.getAnalyzerOptions().getCheckerBooleanOption( \
+ mgr.getCurrentCheckerName(), #OPTION);
+
+ QUERY_CHECKER_OPTION(FunctionPointer)
+ QUERY_CHECKER_OPTION(ParameterCount)
+ QUERY_CHECKER_OPTION(CXXThisMethodCall)
+ QUERY_CHECKER_OPTION(CXXDeallocationArg)
+ QUERY_CHECKER_OPTION(ArgInitializedness)
+ QUERY_CHECKER_OPTION(ArgPointeeInitializedness)
+ QUERY_CHECKER_OPTION(NilReceiver)
+ QUERY_CHECKER_OPTION(UndefReceiver)
}
-bool ento::shouldRegisterCallAndMessageUnInitRefArg(const LangOptions &LO) {
+bool ento::shouldRegisterCallAndMessageChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
index 51c1d4409929..a498f252e693 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
@@ -10,12 +10,14 @@
// whether the size of the symbolic region is a multiple of the size of T.
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+
#include "clang/AST/CharUnits.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
using namespace clang;
using namespace ento;
@@ -109,12 +111,13 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
return;
SValBuilder &svalBuilder = C.getSValBuilder();
- SVal extent = SR->getExtent(svalBuilder);
- const llvm::APSInt *extentInt = svalBuilder.getKnownValue(state, extent);
- if (!extentInt)
+
+ DefinedOrUnknownSVal Size = getDynamicSize(state, SR, svalBuilder);
+ const llvm::APSInt *SizeInt = svalBuilder.getKnownValue(state, Size);
+ if (!SizeInt)
return;
- CharUnits regionSize = CharUnits::fromQuantity(extentInt->getSExtValue());
+ CharUnits regionSize = CharUnits::fromQuantity(SizeInt->getZExtValue());
CharUnits typeSize = C.getASTContext().getTypeSizeInChars(ToPointeeTy);
// Ignore void, and a few other un-sizeable types.
@@ -143,10 +146,11 @@ void ento::registerCastSizeChecker(CheckerManager &mgr) {
mgr.registerChecker<CastSizeChecker>();
}
-bool ento::shouldRegisterCastSizeChecker(const LangOptions &LO) {
+bool ento::shouldRegisterCastSizeChecker(const CheckerManager &mgr) {
// PR31226: C++ is more complicated than what this checker currently supports.
// There are derived-to-base casts, there are different rules for 0-size
// structures, no flexible arrays, etc.
// FIXME: Disabled on C++ for now.
+ const LangOptions &LO = mgr.getLangOpts();
return !LO.CPlusPlus;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
index 93665596be29..e674ec43bcd9 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
@@ -120,6 +120,6 @@ void ento::registerCastToStructChecker(CheckerManager &mgr) {
mgr.registerChecker<CastToStructChecker>();
}
-bool ento::shouldRegisterCastToStructChecker(const LangOptions &LO) {
+bool ento::shouldRegisterCastToStructChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
index cc1c9a66b90e..1ef70b650414 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
@@ -30,7 +30,7 @@ using namespace clang;
using namespace ento;
namespace {
-class CastValueChecker : public Checker<eval::Call> {
+class CastValueChecker : public Checker<check::DeadSymbols, eval::Call> {
enum class CallKind { Function, Method, InstanceOf };
using CastCheck =
@@ -51,6 +51,7 @@ public:
// 1) isa: The parameter is non-null, returns boolean.
// 2) isa_and_nonnull: The parameter is null or non-null, returns boolean.
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
private:
// These are known in the LLVM project. The pairs are in the following form:
@@ -129,7 +130,7 @@ static const NoteTag *getNoteTag(CheckerContext &C,
Out << ' ' << (CastSucceeds ? "is a" : "is not a") << " '" << CastToName
<< '\'';
- return Out.str();
+ return std::string(Out.str());
},
/*IsPrunable=*/true);
}
@@ -432,10 +433,15 @@ bool CastValueChecker::evalCall(const CallEvent &Call,
return true;
}
+void CastValueChecker::checkDeadSymbols(SymbolReaper &SR,
+ CheckerContext &C) const {
+ C.addTransition(removeDeadCasts(C.getState(), SR));
+}
+
void ento::registerCastValueChecker(CheckerManager &Mgr) {
Mgr.registerChecker<CastValueChecker>();
}
-bool ento::shouldRegisterCastValueChecker(const LangOptions &LO) {
+bool ento::shouldRegisterCastValueChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
index 50b872bd8682..13836f08a61e 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
@@ -1088,7 +1088,8 @@ void ento::registerObjCDeallocChecker(CheckerManager &Mgr) {
Mgr.registerChecker<ObjCDeallocChecker>();
}
-bool ento::shouldRegisterObjCDeallocChecker(const LangOptions &LO) {
+bool ento::shouldRegisterObjCDeallocChecker(const CheckerManager &mgr) {
// These checker only makes sense under MRR.
+ const LangOptions &LO = mgr.getLangOpts();
return LO.getGC() != LangOptions::GCOnly && !LO.ObjCAutoRefCount;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
index 1694c237cda4..175dfcef0df4 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
@@ -138,6 +138,6 @@ void ento::registerObjCMethSigsChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCMethSigsChecker>();
}
-bool ento::shouldRegisterObjCMethSigsChecker(const LangOptions &LO) {
+bool ento::shouldRegisterObjCMethSigsChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
index 48fee4a0ffb7..dc9cd717be9e 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
@@ -1,6 +1,19 @@
+//==- CheckPlacementNew.cpp - Check for placement new operation --*- 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 check for misuse of the default placement new operator.
+//
+//===----------------------------------------------------------------------===//
+
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
#include "llvm/Support/FormatVariadic.h"
using namespace clang;
@@ -12,51 +25,59 @@ public:
void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const;
private:
+ bool checkPlaceCapacityIsSufficient(const CXXNewExpr *NE,
+ CheckerContext &C) const;
+
+ bool checkPlaceIsAlignedProperly(const CXXNewExpr *NE,
+ CheckerContext &C) const;
+
// Returns the size of the target in a placement new expression.
// E.g. in "new (&s) long" it returns the size of `long`.
- SVal getExtentSizeOfNewTarget(const CXXNewExpr *NE, ProgramStateRef State,
- CheckerContext &C) const;
+ SVal getExtentSizeOfNewTarget(const CXXNewExpr *NE, CheckerContext &C,
+ bool &IsArray) const;
// Returns the size of the place in a placement new expression.
// E.g. in "new (&s) long" it returns the size of `s`.
- SVal getExtentSizeOfPlace(const Expr *NE, ProgramStateRef State,
- CheckerContext &C) const;
- BugType BT{this, "Insufficient storage for placement new",
- categories::MemoryError};
+ SVal getExtentSizeOfPlace(const CXXNewExpr *NE, CheckerContext &C) const;
+
+ void emitBadAlignReport(const Expr *P, CheckerContext &C,
+ unsigned AllocatedTAlign,
+ unsigned StorageTAlign) const;
+ unsigned getStorageAlign(CheckerContext &C, const ValueDecl *VD) const;
+
+ void checkElementRegionAlign(const ElementRegion *R, CheckerContext &C,
+ const Expr *P, unsigned AllocatedTAlign) const;
+
+ void checkFieldRegionAlign(const FieldRegion *R, CheckerContext &C,
+ const Expr *P, unsigned AllocatedTAlign) const;
+
+ bool isVarRegionAlignedProperly(const VarRegion *R, CheckerContext &C,
+ const Expr *P,
+ unsigned AllocatedTAlign) const;
+
+ BugType SBT{this, "Insufficient storage for placement new",
+ categories::MemoryError};
+ BugType ABT{this, "Bad align storage for placement new",
+ categories::MemoryError};
};
} // namespace
-SVal PlacementNewChecker::getExtentSizeOfPlace(const Expr *Place,
- ProgramStateRef State,
+SVal PlacementNewChecker::getExtentSizeOfPlace(const CXXNewExpr *NE,
CheckerContext &C) const {
- const MemRegion *MRegion = C.getSVal(Place).getAsRegion();
- if (!MRegion)
- return UnknownVal();
- RegionOffset Offset = MRegion->getAsOffset();
- if (Offset.hasSymbolicOffset())
- return UnknownVal();
- const MemRegion *BaseRegion = MRegion->getBaseRegion();
- if (!BaseRegion)
- return UnknownVal();
-
- SValBuilder &SvalBuilder = C.getSValBuilder();
- NonLoc OffsetInBytes = SvalBuilder.makeArrayIndex(
- Offset.getOffset() / C.getASTContext().getCharWidth());
- DefinedOrUnknownSVal ExtentInBytes =
- BaseRegion->castAs<SubRegion>()->getExtent(SvalBuilder);
-
- return SvalBuilder.evalBinOp(State, BinaryOperator::Opcode::BO_Sub,
- ExtentInBytes, OffsetInBytes,
- SvalBuilder.getArrayIndexType());
+ const Expr *Place = NE->getPlacementArg(0);
+ return getDynamicSizeWithOffset(C.getState(), C.getSVal(Place));
}
SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE,
- ProgramStateRef State,
- CheckerContext &C) const {
+ CheckerContext &C,
+ bool &IsArray) const {
+ ProgramStateRef State = C.getState();
SValBuilder &SvalBuilder = C.getSValBuilder();
QualType ElementType = NE->getAllocatedType();
ASTContext &AstContext = C.getASTContext();
CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType);
+ IsArray = false;
if (NE->isArray()) {
+ IsArray = true;
const Expr *SizeExpr = *NE->getArraySize();
SVal ElementCount = C.getSVal(SizeExpr);
if (auto ElementCountNL = ElementCount.getAs<NonLoc>()) {
@@ -78,44 +99,218 @@ SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE,
return UnknownVal();
}
-void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,
- CheckerContext &C) const {
- // Check only the default placement new.
- if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator())
- return;
- if (NE->getNumPlacementArgs() == 0)
- return;
-
- ProgramStateRef State = C.getState();
- SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, State, C);
- const Expr *Place = NE->getPlacementArg(0);
- SVal SizeOfPlace = getExtentSizeOfPlace(Place, State, C);
+bool PlacementNewChecker::checkPlaceCapacityIsSufficient(
+ const CXXNewExpr *NE, CheckerContext &C) const {
+ bool IsArrayTypeAllocated;
+ SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, C, IsArrayTypeAllocated);
+ SVal SizeOfPlace = getExtentSizeOfPlace(NE, C);
const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>();
if (!SizeOfTargetCI)
- return;
+ return true;
const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>();
if (!SizeOfPlaceCI)
- return;
+ return true;
- if (SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) {
- if (ExplodedNode *N = C.generateErrorNode(State)) {
- std::string Msg =
- llvm::formatv("Storage provided to placement new is only {0} bytes, "
- "whereas the allocated type requires {1} bytes",
- SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue());
+ if ((SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) ||
+ (IsArrayTypeAllocated &&
+ SizeOfPlaceCI->getValue() >= SizeOfTargetCI->getValue())) {
+ if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
+ std::string Msg;
+ // TODO: use clang constant
+ if (IsArrayTypeAllocated &&
+ SizeOfPlaceCI->getValue() > SizeOfTargetCI->getValue())
+ Msg = std::string(llvm::formatv(
+ "{0} bytes is possibly not enough for array allocation which "
+ "requires {1} bytes. Current overhead requires the size of {2} "
+ "bytes",
+ SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue(),
+ SizeOfPlaceCI->getValue() - SizeOfTargetCI->getValue()));
+ else if (IsArrayTypeAllocated &&
+ SizeOfPlaceCI->getValue() == SizeOfTargetCI->getValue())
+ Msg = std::string(llvm::formatv(
+ "Storage provided to placement new is only {0} bytes, "
+ "whereas the allocated array type requires more space for "
+ "internal needs",
+ SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
+ else
+ Msg = std::string(llvm::formatv(
+ "Storage provided to placement new is only {0} bytes, "
+ "whereas the allocated type requires {1} bytes",
+ SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
- auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
- bugreporter::trackExpressionValue(N, Place, *R);
+ auto R = std::make_unique<PathSensitiveBugReport>(SBT, Msg, N);
+ bugreporter::trackExpressionValue(N, NE->getPlacementArg(0), *R);
C.emitReport(std::move(R));
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void PlacementNewChecker::emitBadAlignReport(const Expr *P, CheckerContext &C,
+ unsigned AllocatedTAlign,
+ unsigned StorageTAlign) const {
+ ProgramStateRef State = C.getState();
+ if (ExplodedNode *N = C.generateErrorNode(State)) {
+ std::string Msg(llvm::formatv("Storage type is aligned to {0} bytes but "
+ "allocated type is aligned to {1} bytes",
+ StorageTAlign, AllocatedTAlign));
+
+ auto R = std::make_unique<PathSensitiveBugReport>(ABT, Msg, N);
+ bugreporter::trackExpressionValue(N, P, *R);
+ C.emitReport(std::move(R));
+ }
+}
+
+unsigned PlacementNewChecker::getStorageAlign(CheckerContext &C,
+ const ValueDecl *VD) const {
+ unsigned StorageTAlign = C.getASTContext().getTypeAlign(VD->getType());
+ if (unsigned SpecifiedAlignment = VD->getMaxAlignment())
+ StorageTAlign = SpecifiedAlignment;
+
+ return StorageTAlign / C.getASTContext().getCharWidth();
+}
+
+void PlacementNewChecker::checkElementRegionAlign(
+ const ElementRegion *R, CheckerContext &C, const Expr *P,
+ unsigned AllocatedTAlign) const {
+ auto IsBaseRegionAlignedProperly = [this, R, &C, P,
+ AllocatedTAlign]() -> bool {
+ // Unwind nested ElementRegion`s to get the type.
+ const MemRegion *SuperRegion = R;
+ while (true) {
+ if (SuperRegion->getKind() == MemRegion::ElementRegionKind) {
+ SuperRegion = cast<SubRegion>(SuperRegion)->getSuperRegion();
+ continue;
+ }
+
+ break;
+ }
+
+ const DeclRegion *TheElementDeclRegion = SuperRegion->getAs<DeclRegion>();
+ if (!TheElementDeclRegion)
+ return false;
+
+ const DeclRegion *BaseDeclRegion = R->getBaseRegion()->getAs<DeclRegion>();
+ if (!BaseDeclRegion)
+ return false;
+
+ unsigned BaseRegionAlign = 0;
+ // We must use alignment TheElementDeclRegion if it has its own alignment
+ // specifier
+ if (TheElementDeclRegion->getDecl()->getMaxAlignment())
+ BaseRegionAlign = getStorageAlign(C, TheElementDeclRegion->getDecl());
+ else
+ BaseRegionAlign = getStorageAlign(C, BaseDeclRegion->getDecl());
+
+ if (AllocatedTAlign > BaseRegionAlign) {
+ emitBadAlignReport(P, C, AllocatedTAlign, BaseRegionAlign);
+ return false;
+ }
+
+ return true;
+ };
+
+ auto CheckElementRegionOffset = [this, R, &C, P, AllocatedTAlign]() -> void {
+ RegionOffset TheOffsetRegion = R->getAsOffset();
+ if (TheOffsetRegion.hasSymbolicOffset())
return;
+
+ unsigned Offset =
+ TheOffsetRegion.getOffset() / C.getASTContext().getCharWidth();
+ unsigned AddressAlign = Offset % AllocatedTAlign;
+ if (AddressAlign != 0) {
+ emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);
+ return;
+ }
+ };
+
+ if (IsBaseRegionAlignedProperly()) {
+ CheckElementRegionOffset();
+ }
+}
+
+void PlacementNewChecker::checkFieldRegionAlign(
+ const FieldRegion *R, CheckerContext &C, const Expr *P,
+ unsigned AllocatedTAlign) const {
+ const MemRegion *BaseRegion = R->getBaseRegion();
+ if (!BaseRegion)
+ return;
+
+ if (const VarRegion *TheVarRegion = BaseRegion->getAs<VarRegion>()) {
+ if (isVarRegionAlignedProperly(TheVarRegion, C, P, AllocatedTAlign)) {
+ // We've checked type align but, unless FieldRegion
+ // offset is zero, we also need to check its own
+ // align.
+ RegionOffset Offset = R->getAsOffset();
+ if (Offset.hasSymbolicOffset())
+ return;
+
+ int64_t OffsetValue =
+ Offset.getOffset() / C.getASTContext().getCharWidth();
+ unsigned AddressAlign = OffsetValue % AllocatedTAlign;
+ if (AddressAlign != 0)
+ emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);
}
}
}
+bool PlacementNewChecker::isVarRegionAlignedProperly(
+ const VarRegion *R, CheckerContext &C, const Expr *P,
+ unsigned AllocatedTAlign) const {
+ const VarDecl *TheVarDecl = R->getDecl();
+ unsigned StorageTAlign = getStorageAlign(C, TheVarDecl);
+ if (AllocatedTAlign > StorageTAlign) {
+ emitBadAlignReport(P, C, AllocatedTAlign, StorageTAlign);
+
+ return false;
+ }
+
+ return true;
+}
+
+bool PlacementNewChecker::checkPlaceIsAlignedProperly(const CXXNewExpr *NE,
+ CheckerContext &C) const {
+ const Expr *Place = NE->getPlacementArg(0);
+
+ QualType AllocatedT = NE->getAllocatedType();
+ unsigned AllocatedTAlign = C.getASTContext().getTypeAlign(AllocatedT) /
+ C.getASTContext().getCharWidth();
+
+ SVal PlaceVal = C.getSVal(Place);
+ if (const MemRegion *MRegion = PlaceVal.getAsRegion()) {
+ if (const ElementRegion *TheElementRegion = MRegion->getAs<ElementRegion>())
+ checkElementRegionAlign(TheElementRegion, C, Place, AllocatedTAlign);
+ else if (const FieldRegion *TheFieldRegion = MRegion->getAs<FieldRegion>())
+ checkFieldRegionAlign(TheFieldRegion, C, Place, AllocatedTAlign);
+ else if (const VarRegion *TheVarRegion = MRegion->getAs<VarRegion>())
+ isVarRegionAlignedProperly(TheVarRegion, C, Place, AllocatedTAlign);
+ }
+
+ return true;
+}
+
+void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,
+ CheckerContext &C) const {
+ // Check only the default placement new.
+ if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator())
+ return;
+
+ if (NE->getNumPlacementArgs() == 0)
+ return;
+
+ if (!checkPlaceCapacityIsSufficient(NE, C))
+ return;
+
+ checkPlaceIsAlignedProperly(NE, C);
+}
+
void ento::registerPlacementNewChecker(CheckerManager &mgr) {
mgr.registerChecker<PlacementNewChecker>();
}
-bool ento::shouldRegisterPlacementNewChecker(const LangOptions &LO) {
+bool ento::shouldRegisterPlacementNewChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
index d9ffa562c0aa..d06c87631bfb 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
@@ -1076,7 +1076,7 @@ void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
mgr.registerChecker<SecuritySyntaxChecker>();
}
-bool ento::shouldRegisterSecuritySyntaxChecker(const LangOptions &LO) {
+bool ento::shouldRegisterSecuritySyntaxChecker(const CheckerManager &mgr) {
return true;
}
@@ -1087,7 +1087,7 @@ bool ento::shouldRegisterSecuritySyntaxChecker(const LangOptions &LO) {
checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \
} \
\
- bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
+ bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
REGISTER_CHECKER(bcmp)
REGISTER_CHECKER(bcopy)
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp
index ec401cfa8985..0d2551f11583 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp
@@ -91,6 +91,6 @@ void ento::registerSizeofPointerChecker(CheckerManager &mgr) {
mgr.registerChecker<SizeofPointerChecker>();
}
-bool ento::shouldRegisterSizeofPointerChecker(const LangOptions &LO) {
+bool ento::shouldRegisterSizeofPointerChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
index 7a41a7b6b216..fd53c04f4bbf 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
@@ -136,6 +136,6 @@ void ento::registerChrootChecker(CheckerManager &mgr) {
mgr.registerChecker<ChrootChecker>();
}
-bool ento::shouldRegisterChrootChecker(const LangOptions &LO) {
+bool ento::shouldRegisterChrootChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
index ce45b5be34c9..7968aed85e1b 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
@@ -208,6 +208,6 @@ void ento::registerCloneChecker(CheckerManager &Mgr) {
.getCheckerStringOption(Checker, "IgnoredFilesPattern");
}
-bool ento::shouldRegisterCloneChecker(const LangOptions &LO) {
+bool ento::shouldRegisterCloneChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
new file mode 100644
index 000000000000..73c6517fd0eb
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
@@ -0,0 +1,1083 @@
+//===-- ContainerModeling.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines a modeling-checker for modeling STL container-like containers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/Driver/DriverDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
+
+#include "Iterator.h"
+
+#include <utility>
+
+using namespace clang;
+using namespace ento;
+using namespace iterator;
+
+namespace {
+
+class ContainerModeling
+ : public Checker<check::PostCall, check::LiveSymbols, check::DeadSymbols> {
+
+ void handleBegin(CheckerContext &C, const Expr *CE, SVal RetVal,
+ SVal Cont) const;
+ void handleEnd(CheckerContext &C, const Expr *CE, SVal RetVal,
+ SVal Cont) const;
+ void handleAssignment(CheckerContext &C, SVal Cont, const Expr *CE = nullptr,
+ SVal OldCont = UndefinedVal()) const;
+ void handleAssign(CheckerContext &C, SVal Cont, const Expr *ContE) const;
+ void handleClear(CheckerContext &C, SVal Cont, const Expr *ContE) const;
+ void handlePushBack(CheckerContext &C, SVal Cont, const Expr *ContE) const;
+ void handlePopBack(CheckerContext &C, SVal Cont, const Expr *ContE) const;
+ void handlePushFront(CheckerContext &C, SVal Cont, const Expr *ContE) const;
+ void handlePopFront(CheckerContext &C, SVal Cont, const Expr *ContE) const;
+ void handleInsert(CheckerContext &C, SVal Cont, SVal Iter) const;
+ void handleErase(CheckerContext &C, SVal Cont, SVal Iter) const;
+ void handleErase(CheckerContext &C, SVal Cont, SVal Iter1, SVal Iter2) const;
+ void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter) const;
+ void handleEraseAfter(CheckerContext &C, SVal Cont, SVal Iter1,
+ SVal Iter2) const;
+ const NoteTag *getChangeTag(CheckerContext &C, StringRef Text,
+ const MemRegion *ContReg,
+ const Expr *ContE) const;
+ void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+ const char *Sep) const override;
+
+public:
+ ContainerModeling() = default;
+
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
+ void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
+
+ using NoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal,
+ const Expr *) const;
+ using OneItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal,
+ SVal) const;
+ using TwoItParamFn = void (ContainerModeling::*)(CheckerContext &, SVal, SVal,
+ SVal) const;
+
+ CallDescriptionMap<NoItParamFn> NoIterParamFunctions = {
+ {{0, "clear", 0},
+ &ContainerModeling::handleClear},
+ {{0, "assign", 2},
+ &ContainerModeling::handleAssign},
+ {{0, "push_back", 1},
+ &ContainerModeling::handlePushBack},
+ {{0, "emplace_back", 1},
+ &ContainerModeling::handlePushBack},
+ {{0, "pop_back", 0},
+ &ContainerModeling::handlePopBack},
+ {{0, "push_front", 1},
+ &ContainerModeling::handlePushFront},
+ {{0, "emplace_front", 1},
+ &ContainerModeling::handlePushFront},
+ {{0, "pop_front", 0},
+ &ContainerModeling::handlePopFront},
+ };
+
+ CallDescriptionMap<OneItParamFn> OneIterParamFunctions = {
+ {{0, "insert", 2},
+ &ContainerModeling::handleInsert},
+ {{0, "emplace", 2},
+ &ContainerModeling::handleInsert},
+ {{0, "erase", 1},
+ &ContainerModeling::handleErase},
+ {{0, "erase_after", 1},
+ &ContainerModeling::handleEraseAfter},
+ };
+
+ CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = {
+ {{0, "erase", 2},
+ &ContainerModeling::handleErase},
+ {{0, "erase_after", 2},
+ &ContainerModeling::handleEraseAfter},
+ };
+
+};
+
+bool isBeginCall(const FunctionDecl *Func);
+bool isEndCall(const FunctionDecl *Func);
+bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);
+bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);
+bool backModifiable(ProgramStateRef State, const MemRegion *Reg);
+SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
+SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
+ProgramStateRef createContainerBegin(ProgramStateRef State,
+ const MemRegion *Cont, const Expr *E,
+ QualType T, const LocationContext *LCtx,
+ unsigned BlockCount);
+ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
+ const Expr *E, QualType T,
+ const LocationContext *LCtx,
+ unsigned BlockCount);
+ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
+ const ContainerData &CData);
+ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
+ const MemRegion *Cont);
+ProgramStateRef
+invalidateAllIteratorPositionsExcept(ProgramStateRef State,
+ const MemRegion *Cont, SymbolRef Offset,
+ BinaryOperator::Opcode Opc);
+ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
+ SymbolRef Offset,
+ BinaryOperator::Opcode Opc);
+ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
+ SymbolRef Offset1,
+ BinaryOperator::Opcode Opc1,
+ SymbolRef Offset2,
+ BinaryOperator::Opcode Opc2);
+ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
+ const MemRegion *Cont,
+ const MemRegion *NewCont);
+ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
+ const MemRegion *Cont,
+ const MemRegion *NewCont,
+ SymbolRef Offset,
+ BinaryOperator::Opcode Opc);
+ProgramStateRef rebaseSymbolInIteratorPositionsIf(
+ ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
+ SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
+SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
+ SymbolRef OldSym, SymbolRef NewSym);
+bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
+
+} // namespace
+
+void ContainerModeling::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!Func)
+ return;
+
+ if (Func->isOverloadedOperator()) {
+ const auto Op = Func->getOverloadedOperator();
+ if (Op == OO_Equal) {
+ // Overloaded 'operator=' must be a non-static member function.
+ const auto *InstCall = cast<CXXInstanceCall>(&Call);
+ if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
+ handleAssignment(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
+ Call.getArgSVal(0));
+ return;
+ }
+
+ handleAssignment(C, InstCall->getCXXThisVal());
+ return;
+ }
+ } else {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ const NoItParamFn *Handler0 = NoIterParamFunctions.lookup(Call);
+ if (Handler0) {
+ (this->**Handler0)(C, InstCall->getCXXThisVal(),
+ InstCall->getCXXThisExpr());
+ return;
+ }
+
+ const OneItParamFn *Handler1 = OneIterParamFunctions.lookup(Call);
+ if (Handler1) {
+ (this->**Handler1)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
+ return;
+ }
+
+ const TwoItParamFn *Handler2 = TwoIterParamFunctions.lookup(Call);
+ if (Handler2) {
+ (this->**Handler2)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0),
+ Call.getArgSVal(1));
+ return;
+ }
+
+ const auto *OrigExpr = Call.getOriginExpr();
+ if (!OrigExpr)
+ return;
+
+ if (isBeginCall(Func)) {
+ handleBegin(C, OrigExpr, Call.getReturnValue(),
+ InstCall->getCXXThisVal());
+ return;
+ }
+
+ if (isEndCall(Func)) {
+ handleEnd(C, OrigExpr, Call.getReturnValue(),
+ InstCall->getCXXThisVal());
+ return;
+ }
+ }
+ }
+}
+
+void ContainerModeling::checkLiveSymbols(ProgramStateRef State,
+ SymbolReaper &SR) const {
+ // Keep symbolic expressions of container begins and ends alive
+ auto ContMap = State->get<ContainerMap>();
+ for (const auto &Cont : ContMap) {
+ const auto CData = Cont.second;
+ if (CData.getBegin()) {
+ SR.markLive(CData.getBegin());
+ if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
+ SR.markLive(SIE->getLHS());
+ }
+ if (CData.getEnd()) {
+ SR.markLive(CData.getEnd());
+ if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
+ SR.markLive(SIE->getLHS());
+ }
+ }
+}
+
+void ContainerModeling::checkDeadSymbols(SymbolReaper &SR,
+ CheckerContext &C) const {
+ // Cleanup
+ auto State = C.getState();
+
+ auto ContMap = State->get<ContainerMap>();
+ for (const auto &Cont : ContMap) {
+ if (!SR.isLiveRegion(Cont.first)) {
+ // We must keep the container data while it has live iterators to be able
+ // to compare them to the begin and the end of the container.
+ if (!hasLiveIterators(State, Cont.first)) {
+ State = State->remove<ContainerMap>(Cont.first);
+ }
+ }
+ }
+
+ C.addTransition(State);
+}
+
+void ContainerModeling::handleBegin(CheckerContext &C, const Expr *CE,
+ SVal RetVal, SVal Cont) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ // If the container already has a begin symbol then use it. Otherwise first
+ // create a new one.
+ auto State = C.getState();
+ auto BeginSym = getContainerBegin(State, ContReg);
+ if (!BeginSym) {
+ State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
+ C.getLocationContext(), C.blockCount());
+ BeginSym = getContainerBegin(State, ContReg);
+ }
+ State = setIteratorPosition(State, RetVal,
+ IteratorPosition::getPosition(ContReg, BeginSym));
+ C.addTransition(State);
+}
+
+void ContainerModeling::handleEnd(CheckerContext &C, const Expr *CE,
+ SVal RetVal, SVal Cont) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ // If the container already has an end symbol then use it. Otherwise first
+ // create a new one.
+ auto State = C.getState();
+ auto EndSym = getContainerEnd(State, ContReg);
+ if (!EndSym) {
+ State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
+ C.getLocationContext(), C.blockCount());
+ EndSym = getContainerEnd(State, ContReg);
+ }
+ State = setIteratorPosition(State, RetVal,
+ IteratorPosition::getPosition(ContReg, EndSym));
+ C.addTransition(State);
+}
+
+void ContainerModeling::handleAssignment(CheckerContext &C, SVal Cont,
+ const Expr *CE, SVal OldCont) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ // Assignment of a new value to a container always invalidates all its
+ // iterators
+ auto State = C.getState();
+ const auto CData = getContainerData(State, ContReg);
+ if (CData) {
+ State = invalidateAllIteratorPositions(State, ContReg);
+ }
+
+ // In case of move, iterators of the old container (except the past-end
+ // iterators) remain valid but refer to the new container
+ if (!OldCont.isUndef()) {
+ const auto *OldContReg = OldCont.getAsRegion();
+ if (OldContReg) {
+ OldContReg = OldContReg->getMostDerivedObjectRegion();
+ const auto OldCData = getContainerData(State, OldContReg);
+ if (OldCData) {
+ if (const auto OldEndSym = OldCData->getEnd()) {
+ // If we already assigned an "end" symbol to the old container, then
+ // first reassign all iterator positions to the new container which
+ // are not past the container (thus not greater or equal to the
+ // current "end" symbol).
+ State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
+ OldEndSym, BO_GE);
+ auto &SymMgr = C.getSymbolManager();
+ auto &SVB = C.getSValBuilder();
+ // Then generate and assign a new "end" symbol for the new container.
+ auto NewEndSym =
+ SymMgr.conjureSymbol(CE, C.getLocationContext(),
+ C.getASTContext().LongTy, C.blockCount());
+ State = assumeNoOverflow(State, NewEndSym, 4);
+ if (CData) {
+ State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
+ } else {
+ State = setContainerData(State, ContReg,
+ ContainerData::fromEnd(NewEndSym));
+ }
+ // Finally, replace the old "end" symbol in the already reassigned
+ // iterator positions with the new "end" symbol.
+ State = rebaseSymbolInIteratorPositionsIf(
+ State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
+ } else {
+ // There was no "end" symbol assigned yet to the old container,
+ // so reassign all iterator positions to the new container.
+ State = reassignAllIteratorPositions(State, OldContReg, ContReg);
+ }
+ if (const auto OldBeginSym = OldCData->getBegin()) {
+ // If we already assigned a "begin" symbol to the old container, then
+ // assign it to the new container and remove it from the old one.
+ if (CData) {
+ State =
+ setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
+ } else {
+ State = setContainerData(State, ContReg,
+ ContainerData::fromBegin(OldBeginSym));
+ }
+ State =
+ setContainerData(State, OldContReg, OldCData->newBegin(nullptr));
+ }
+ } else {
+ // There was neither "begin" nor "end" symbol assigned yet to the old
+ // container, so reassign all iterator positions to the new container.
+ State = reassignAllIteratorPositions(State, OldContReg, ContReg);
+ }
+ }
+ }
+ C.addTransition(State);
+}
+
+void ContainerModeling::handleAssign(CheckerContext &C, SVal Cont,
+ const Expr *ContE) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ // The assign() operation invalidates all the iterators
+ auto State = C.getState();
+ State = invalidateAllIteratorPositions(State, ContReg);
+ C.addTransition(State);
+}
+
+void ContainerModeling::handleClear(CheckerContext &C, SVal Cont,
+ const Expr *ContE) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ // The clear() operation invalidates all the iterators, except the past-end
+ // iterators of list-like containers
+ auto State = C.getState();
+ if (!hasSubscriptOperator(State, ContReg) ||
+ !backModifiable(State, ContReg)) {
+ const auto CData = getContainerData(State, ContReg);
+ if (CData) {
+ if (const auto EndSym = CData->getEnd()) {
+ State =
+ invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
+ C.addTransition(State);
+ return;
+ }
+ }
+ }
+ const NoteTag *ChangeTag =
+ getChangeTag(C, "became empty", ContReg, ContE);
+ State = invalidateAllIteratorPositions(State, ContReg);
+ C.addTransition(State, ChangeTag);
+}
+
+void ContainerModeling::handlePushBack(CheckerContext &C, SVal Cont,
+ const Expr *ContE) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ // For deque-like containers invalidate all iterator positions
+ auto State = C.getState();
+ if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
+ State = invalidateAllIteratorPositions(State, ContReg);
+ C.addTransition(State);
+ return;
+ }
+
+ const auto CData = getContainerData(State, ContReg);
+ if (!CData)
+ return;
+
+ // For vector-like containers invalidate the past-end iterator positions
+ if (const auto EndSym = CData->getEnd()) {
+ if (hasSubscriptOperator(State, ContReg)) {
+ State = invalidateIteratorPositions(State, EndSym, BO_GE);
+ }
+ auto &SymMgr = C.getSymbolManager();
+ auto &BVF = SymMgr.getBasicVals();
+ auto &SVB = C.getSValBuilder();
+ const auto newEndSym =
+ SVB.evalBinOp(State, BO_Add,
+ nonloc::SymbolVal(EndSym),
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+ SymMgr.getType(EndSym)).getAsSymbol();
+ const NoteTag *ChangeTag =
+ getChangeTag(C, "extended to the back by 1 position", ContReg, ContE);
+ State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
+ C.addTransition(State, ChangeTag);
+ }
+}
+
+void ContainerModeling::handlePopBack(CheckerContext &C, SVal Cont,
+ const Expr *ContE) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ auto State = C.getState();
+ const auto CData = getContainerData(State, ContReg);
+ if (!CData)
+ return;
+
+ if (const auto EndSym = CData->getEnd()) {
+ auto &SymMgr = C.getSymbolManager();
+ auto &BVF = SymMgr.getBasicVals();
+ auto &SVB = C.getSValBuilder();
+ const auto BackSym =
+ SVB.evalBinOp(State, BO_Sub,
+ nonloc::SymbolVal(EndSym),
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+ SymMgr.getType(EndSym)).getAsSymbol();
+ const NoteTag *ChangeTag =
+ getChangeTag(C, "shrank from the back by 1 position", ContReg, ContE);
+ // For vector-like and deque-like containers invalidate the last and the
+ // past-end iterator positions. For list-like containers only invalidate
+ // the last position
+ if (hasSubscriptOperator(State, ContReg) &&
+ backModifiable(State, ContReg)) {
+ State = invalidateIteratorPositions(State, BackSym, BO_GE);
+ State = setContainerData(State, ContReg, CData->newEnd(nullptr));
+ } else {
+ State = invalidateIteratorPositions(State, BackSym, BO_EQ);
+ }
+ auto newEndSym = BackSym;
+ State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
+ C.addTransition(State, ChangeTag);
+ }
+}
+
+void ContainerModeling::handlePushFront(CheckerContext &C, SVal Cont,
+ const Expr *ContE) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ // For deque-like containers invalidate all iterator positions
+ auto State = C.getState();
+ if (hasSubscriptOperator(State, ContReg)) {
+ State = invalidateAllIteratorPositions(State, ContReg);
+ C.addTransition(State);
+ } else {
+ const auto CData = getContainerData(State, ContReg);
+ if (!CData)
+ return;
+
+ if (const auto BeginSym = CData->getBegin()) {
+ auto &SymMgr = C.getSymbolManager();
+ auto &BVF = SymMgr.getBasicVals();
+ auto &SVB = C.getSValBuilder();
+ const auto newBeginSym =
+ SVB.evalBinOp(State, BO_Sub,
+ nonloc::SymbolVal(BeginSym),
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+ SymMgr.getType(BeginSym)).getAsSymbol();
+ const NoteTag *ChangeTag =
+ getChangeTag(C, "extended to the front by 1 position", ContReg, ContE);
+ State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
+ C.addTransition(State, ChangeTag);
+ }
+ }
+}
+
+void ContainerModeling::handlePopFront(CheckerContext &C, SVal Cont,
+ const Expr *ContE) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ auto State = C.getState();
+ const auto CData = getContainerData(State, ContReg);
+ if (!CData)
+ return;
+
+ // For deque-like containers invalidate all iterator positions. For list-like
+ // iterators only invalidate the first position
+ if (const auto BeginSym = CData->getBegin()) {
+ if (hasSubscriptOperator(State, ContReg)) {
+ State = invalidateIteratorPositions(State, BeginSym, BO_LE);
+ } else {
+ State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
+ }
+ auto &SymMgr = C.getSymbolManager();
+ auto &BVF = SymMgr.getBasicVals();
+ auto &SVB = C.getSValBuilder();
+ const auto newBeginSym =
+ SVB.evalBinOp(State, BO_Add,
+ nonloc::SymbolVal(BeginSym),
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+ SymMgr.getType(BeginSym)).getAsSymbol();
+ const NoteTag *ChangeTag =
+ getChangeTag(C, "shrank from the front by 1 position", ContReg, ContE);
+ State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
+ C.addTransition(State, ChangeTag);
+ }
+}
+
+void ContainerModeling::handleInsert(CheckerContext &C, SVal Cont,
+ SVal Iter) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Iter);
+ if (!Pos)
+ return;
+
+ // For deque-like containers invalidate all iterator positions. For
+ // vector-like containers invalidate iterator positions after the insertion.
+ if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
+ if (frontModifiable(State, ContReg)) {
+ State = invalidateAllIteratorPositions(State, ContReg);
+ } else {
+ State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
+ }
+ if (const auto *CData = getContainerData(State, ContReg)) {
+ if (const auto EndSym = CData->getEnd()) {
+ State = invalidateIteratorPositions(State, EndSym, BO_GE);
+ State = setContainerData(State, ContReg, CData->newEnd(nullptr));
+ }
+ }
+ C.addTransition(State);
+ }
+}
+
+void ContainerModeling::handleErase(CheckerContext &C, SVal Cont,
+ SVal Iter) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Iter);
+ if (!Pos)
+ return;
+
+ // For deque-like containers invalidate all iterator positions. For
+ // vector-like containers invalidate iterator positions at and after the
+ // deletion. For list-like containers only invalidate the deleted position.
+ if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
+ if (frontModifiable(State, ContReg)) {
+ State = invalidateAllIteratorPositions(State, ContReg);
+ } else {
+ State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
+ }
+ if (const auto *CData = getContainerData(State, ContReg)) {
+ if (const auto EndSym = CData->getEnd()) {
+ State = invalidateIteratorPositions(State, EndSym, BO_GE);
+ State = setContainerData(State, ContReg, CData->newEnd(nullptr));
+ }
+ }
+ } else {
+ State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
+ }
+ C.addTransition(State);
+}
+
+void ContainerModeling::handleErase(CheckerContext &C, SVal Cont, SVal Iter1,
+ SVal Iter2) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+ auto State = C.getState();
+ const auto *Pos1 = getIteratorPosition(State, Iter1);
+ const auto *Pos2 = getIteratorPosition(State, Iter2);
+ if (!Pos1 || !Pos2)
+ return;
+
+ // For deque-like containers invalidate all iterator positions. For
+ // vector-like containers invalidate iterator positions at and after the
+ // deletion range. For list-like containers only invalidate the deleted
+ // position range [first..last].
+ if (hasSubscriptOperator(State, ContReg) && backModifiable(State, ContReg)) {
+ if (frontModifiable(State, ContReg)) {
+ State = invalidateAllIteratorPositions(State, ContReg);
+ } else {
+ State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
+ }
+ if (const auto *CData = getContainerData(State, ContReg)) {
+ if (const auto EndSym = CData->getEnd()) {
+ State = invalidateIteratorPositions(State, EndSym, BO_GE);
+ State = setContainerData(State, ContReg, CData->newEnd(nullptr));
+ }
+ }
+ } else {
+ State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
+ Pos2->getOffset(), BO_LT);
+ }
+ C.addTransition(State);
+}
+
+void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
+ SVal Iter) const {
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Iter);
+ if (!Pos)
+ return;
+
+ // Invalidate the deleted iterator position, which is the position of the
+ // parameter plus one.
+ auto &SymMgr = C.getSymbolManager();
+ auto &BVF = SymMgr.getBasicVals();
+ auto &SVB = C.getSValBuilder();
+ const auto NextSym =
+ SVB.evalBinOp(State, BO_Add,
+ nonloc::SymbolVal(Pos->getOffset()),
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+ SymMgr.getType(Pos->getOffset())).getAsSymbol();
+ State = invalidateIteratorPositions(State, NextSym, BO_EQ);
+ C.addTransition(State);
+}
+
+void ContainerModeling::handleEraseAfter(CheckerContext &C, SVal Cont,
+ SVal Iter1, SVal Iter2) const {
+ auto State = C.getState();
+ const auto *Pos1 = getIteratorPosition(State, Iter1);
+ const auto *Pos2 = getIteratorPosition(State, Iter2);
+ if (!Pos1 || !Pos2)
+ return;
+
+ // Invalidate the deleted iterator position range (first..last)
+ State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
+ Pos2->getOffset(), BO_LT);
+ C.addTransition(State);
+}
+
+const NoteTag *ContainerModeling::getChangeTag(CheckerContext &C,
+ StringRef Text,
+ const MemRegion *ContReg,
+ const Expr *ContE) const {
+ StringRef Name;
+ // First try to get the name of the variable from the region
+ if (const auto *DR = dyn_cast<DeclRegion>(ContReg)) {
+ Name = DR->getDecl()->getName();
+ // If the region is not a `DeclRegion` then use the expression instead
+ } else if (const auto *DRE =
+ dyn_cast<DeclRefExpr>(ContE->IgnoreParenCasts())) {
+ Name = DRE->getDecl()->getName();
+ }
+
+ return C.getNoteTag(
+ [Text, Name, ContReg](PathSensitiveBugReport &BR) -> std::string {
+ if (!BR.isInteresting(ContReg))
+ return "";
+
+ SmallString<256> Msg;
+ llvm::raw_svector_ostream Out(Msg);
+ Out << "Container " << (!Name.empty() ? ("'" + Name.str() + "' ") : "" )
+ << Text;
+ return std::string(Out.str());
+ });
+}
+
+void ContainerModeling::printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const {
+ auto ContMap = State->get<ContainerMap>();
+
+ if (!ContMap.isEmpty()) {
+ Out << Sep << "Container Data :" << NL;
+ for (const auto &Cont : ContMap) {
+ Cont.first->dumpToStream(Out);
+ Out << " : [ ";
+ const auto CData = Cont.second;
+ if (CData.getBegin())
+ CData.getBegin()->dumpToStream(Out);
+ else
+ Out << "<Unknown>";
+ Out << " .. ";
+ if (CData.getEnd())
+ CData.getEnd()->dumpToStream(Out);
+ else
+ Out << "<Unknown>";
+ Out << " ]";
+ }
+ }
+}
+
+namespace {
+
+bool isBeginCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ return IdInfo->getName().endswith_lower("begin");
+}
+
+bool isEndCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ return IdInfo->getName().endswith_lower("end");
+}
+
+const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
+ const MemRegion *Reg) {
+ auto TI = getDynamicTypeInfo(State, Reg);
+ if (!TI.isValid())
+ return nullptr;
+
+ auto Type = TI.getType();
+ if (const auto *RefT = Type->getAs<ReferenceType>()) {
+ Type = RefT->getPointeeType();
+ }
+
+ return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
+}
+
+bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
+ const auto *CRD = getCXXRecordDecl(State, Reg);
+ if (!CRD)
+ return false;
+
+ for (const auto *Method : CRD->methods()) {
+ if (!Method->isOverloadedOperator())
+ continue;
+ const auto OPK = Method->getOverloadedOperator();
+ if (OPK == OO_Subscript) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
+ const auto *CRD = getCXXRecordDecl(State, Reg);
+ if (!CRD)
+ return false;
+
+ for (const auto *Method : CRD->methods()) {
+ if (!Method->getDeclName().isIdentifier())
+ continue;
+ if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
+ const auto *CRD = getCXXRecordDecl(State, Reg);
+ if (!CRD)
+ return false;
+
+ for (const auto *Method : CRD->methods()) {
+ if (!Method->getDeclName().isIdentifier())
+ continue;
+ if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
+ return true;
+ }
+ }
+ return false;
+}
+
+SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
+ const auto *CDataPtr = getContainerData(State, Cont);
+ if (!CDataPtr)
+ return nullptr;
+
+ return CDataPtr->getBegin();
+}
+
+SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
+ const auto *CDataPtr = getContainerData(State, Cont);
+ if (!CDataPtr)
+ return nullptr;
+
+ return CDataPtr->getEnd();
+}
+
+ProgramStateRef createContainerBegin(ProgramStateRef State,
+ const MemRegion *Cont, const 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) {
+ 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 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) {
+ const auto CData = CDataPtr->newEnd(Sym);
+ return setContainerData(State, Cont, CData);
+ }
+
+ const auto CData = ContainerData::fromEnd(Sym);
+ return setContainerData(State, Cont, CData);
+}
+
+ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
+ const ContainerData &CData) {
+ return State->set<ContainerMap>(Cont, CData);
+}
+
+template <typename Condition, typename Process>
+ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
+ Process Proc) {
+ auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
+ auto RegionMap = State->get<IteratorRegionMap>();
+ bool Changed = false;
+ for (const auto &Reg : RegionMap) {
+ if (Cond(Reg.second)) {
+ RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
+ Changed = true;
+ }
+ }
+
+ if (Changed)
+ State = State->set<IteratorRegionMap>(RegionMap);
+
+ auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
+ auto SymbolMap = State->get<IteratorSymbolMap>();
+ Changed = false;
+ for (const auto &Sym : SymbolMap) {
+ if (Cond(Sym.second)) {
+ SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
+ Changed = true;
+ }
+ }
+
+ if (Changed)
+ State = State->set<IteratorSymbolMap>(SymbolMap);
+
+ return State;
+}
+
+ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
+ const MemRegion *Cont) {
+ auto MatchCont = [&](const IteratorPosition &Pos) {
+ return Pos.getContainer() == Cont;
+ };
+ auto Invalidate = [&](const IteratorPosition &Pos) {
+ return Pos.invalidate();
+ };
+ return processIteratorPositions(State, MatchCont, Invalidate);
+}
+
+ProgramStateRef
+invalidateAllIteratorPositionsExcept(ProgramStateRef State,
+ const MemRegion *Cont, SymbolRef Offset,
+ BinaryOperator::Opcode Opc) {
+ auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
+ return Pos.getContainer() == Cont &&
+ !compare(State, Pos.getOffset(), Offset, Opc);
+ };
+ auto Invalidate = [&](const IteratorPosition &Pos) {
+ return Pos.invalidate();
+ };
+ return processIteratorPositions(State, MatchContAndCompare, Invalidate);
+}
+
+ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
+ SymbolRef Offset,
+ BinaryOperator::Opcode Opc) {
+ auto Compare = [&](const IteratorPosition &Pos) {
+ return compare(State, Pos.getOffset(), Offset, Opc);
+ };
+ auto Invalidate = [&](const IteratorPosition &Pos) {
+ return Pos.invalidate();
+ };
+ return processIteratorPositions(State, Compare, Invalidate);
+}
+
+ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
+ SymbolRef Offset1,
+ BinaryOperator::Opcode Opc1,
+ SymbolRef Offset2,
+ BinaryOperator::Opcode Opc2) {
+ auto Compare = [&](const IteratorPosition &Pos) {
+ return compare(State, Pos.getOffset(), Offset1, Opc1) &&
+ compare(State, Pos.getOffset(), Offset2, Opc2);
+ };
+ auto Invalidate = [&](const IteratorPosition &Pos) {
+ return Pos.invalidate();
+ };
+ return processIteratorPositions(State, Compare, Invalidate);
+}
+
+ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
+ const MemRegion *Cont,
+ const MemRegion *NewCont) {
+ auto MatchCont = [&](const IteratorPosition &Pos) {
+ return Pos.getContainer() == Cont;
+ };
+ auto ReAssign = [&](const IteratorPosition &Pos) {
+ return Pos.reAssign(NewCont);
+ };
+ return processIteratorPositions(State, MatchCont, ReAssign);
+}
+
+ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
+ const MemRegion *Cont,
+ const MemRegion *NewCont,
+ SymbolRef Offset,
+ BinaryOperator::Opcode Opc) {
+ auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
+ return Pos.getContainer() == Cont &&
+ !compare(State, Pos.getOffset(), Offset, Opc);
+ };
+ auto ReAssign = [&](const IteratorPosition &Pos) {
+ return Pos.reAssign(NewCont);
+ };
+ return processIteratorPositions(State, MatchContAndCompare, ReAssign);
+}
+
+// This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
+// `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator
+// position offsets where `CondSym` is true.
+ProgramStateRef rebaseSymbolInIteratorPositionsIf(
+ ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
+ SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
+ auto LessThanEnd = [&](const IteratorPosition &Pos) {
+ return compare(State, Pos.getOffset(), CondSym, Opc);
+ };
+ auto RebaseSymbol = [&](const IteratorPosition &Pos) {
+ return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
+ NewSym));
+ };
+ return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
+}
+
+// This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
+// `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression
+// `OrigExpr`.
+SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
+ SymbolRef OrigExpr, SymbolRef OldExpr,
+ SymbolRef NewSym) {
+ auto &SymMgr = SVB.getSymbolManager();
+ auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
+ nonloc::SymbolVal(OldExpr),
+ SymMgr.getType(OrigExpr));
+
+ const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
+ if (!DiffInt)
+ return OrigExpr;
+
+ return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
+ SymMgr.getType(OrigExpr)).getAsSymbol();
+}
+
+bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
+ auto RegionMap = State->get<IteratorRegionMap>();
+ for (const auto &Reg : RegionMap) {
+ if (Reg.second.getContainer() == Cont)
+ return true;
+ }
+
+ auto SymbolMap = State->get<IteratorSymbolMap>();
+ for (const auto &Sym : SymbolMap) {
+ if (Sym.second.getContainer() == Cont)
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace
+
+void ento::registerContainerModeling(CheckerManager &mgr) {
+ mgr.registerChecker<ContainerModeling>();
+}
+
+bool ento::shouldRegisterContainerModeling(const CheckerManager &mgr) {
+ if (!mgr.getLangOpts().CPlusPlus)
+ return false;
+
+ if (!mgr.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation) {
+ mgr.getASTContext().getDiagnostics().Report(
+ diag::err_analyzer_checker_incompatible_analyzer_option)
+ << "aggressive-binary-operation-simplification" << "false";
+ return false;
+ }
+
+ return true;
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
index 8dd3132f07e2..4216a6883119 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
@@ -196,6 +196,6 @@ void ento::registerConversionChecker(CheckerManager &mgr) {
mgr.registerChecker<ConversionChecker>();
}
-bool ento::shouldRegisterConversionChecker(const LangOptions &LO) {
+bool ento::shouldRegisterConversionChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
index 61441889fc64..6bc186aa2755 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
@@ -540,6 +540,6 @@ void ento::registerDeadStoresChecker(CheckerManager &Mgr) {
AnOpts.getCheckerBooleanOption(Chk, "ShowFixIts");
}
-bool ento::shouldRegisterDeadStoresChecker(const LangOptions &LO) {
+bool ento::shouldRegisterDeadStoresChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
index 0cb4be2c7fdc..03b7cbd1c833 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
@@ -47,7 +47,7 @@ void ento::registerDominatorsTreeDumper(CheckerManager &mgr) {
mgr.registerChecker<DominatorsTreeDumper>();
}
-bool ento::shouldRegisterDominatorsTreeDumper(const LangOptions &LO) {
+bool ento::shouldRegisterDominatorsTreeDumper(const CheckerManager &mgr) {
return true;
}
@@ -73,7 +73,7 @@ void ento::registerPostDominatorsTreeDumper(CheckerManager &mgr) {
mgr.registerChecker<PostDominatorsTreeDumper>();
}
-bool ento::shouldRegisterPostDominatorsTreeDumper(const LangOptions &LO) {
+bool ento::shouldRegisterPostDominatorsTreeDumper(const CheckerManager &mgr) {
return true;
}
@@ -98,7 +98,7 @@ void ento::registerControlDependencyTreeDumper(CheckerManager &mgr) {
mgr.registerChecker<ControlDependencyTreeDumper>();
}
-bool ento::shouldRegisterControlDependencyTreeDumper(const LangOptions &LO) {
+bool ento::shouldRegisterControlDependencyTreeDumper(const CheckerManager &mgr) {
return true;
}
@@ -122,7 +122,7 @@ void ento::registerLiveVariablesDumper(CheckerManager &mgr) {
mgr.registerChecker<LiveVariablesDumper>();
}
-bool ento::shouldRegisterLiveVariablesDumper(const LangOptions &LO) {
+bool ento::shouldRegisterLiveVariablesDumper(const CheckerManager &mgr) {
return true;
}
@@ -145,7 +145,7 @@ void ento::registerLiveStatementsDumper(CheckerManager &mgr) {
mgr.registerChecker<LiveStatementsDumper>();
}
-bool ento::shouldRegisterLiveStatementsDumper(const LangOptions &LO) {
+bool ento::shouldRegisterLiveStatementsDumper(const CheckerManager &mgr) {
return true;
}
@@ -169,7 +169,7 @@ void ento::registerCFGViewer(CheckerManager &mgr) {
mgr.registerChecker<CFGViewer>();
}
-bool ento::shouldRegisterCFGViewer(const LangOptions &LO) {
+bool ento::shouldRegisterCFGViewer(const CheckerManager &mgr) {
return true;
}
@@ -199,7 +199,7 @@ void ento::registerCFGDumper(CheckerManager &mgr) {
mgr.registerChecker<CFGDumper>();
}
-bool ento::shouldRegisterCFGDumper(const LangOptions &LO) {
+bool ento::shouldRegisterCFGDumper(const CheckerManager &mgr) {
return true;
}
@@ -223,7 +223,7 @@ void ento::registerCallGraphViewer(CheckerManager &mgr) {
mgr.registerChecker<CallGraphViewer>();
}
-bool ento::shouldRegisterCallGraphViewer(const LangOptions &LO) {
+bool ento::shouldRegisterCallGraphViewer(const CheckerManager &mgr) {
return true;
}
@@ -247,7 +247,7 @@ void ento::registerCallGraphDumper(CheckerManager &mgr) {
mgr.registerChecker<CallGraphDumper>();
}
-bool ento::shouldRegisterCallGraphDumper(const LangOptions &LO) {
+bool ento::shouldRegisterCallGraphDumper(const CheckerManager &mgr) {
return true;
}
@@ -281,8 +281,6 @@ public:
llvm::errs() << Keys[I]->getKey() << " = "
<< (Keys[I]->second.empty() ? "\"\"" : Keys[I]->second)
<< '\n';
-
- llvm::errs() << "[stats]\n" << "num-entries = " << Keys.size() << '\n';
}
};
}
@@ -291,7 +289,7 @@ void ento::registerConfigDumper(CheckerManager &mgr) {
mgr.registerChecker<ConfigDumper>();
}
-bool ento::shouldRegisterConfigDumper(const LangOptions &LO) {
+bool ento::shouldRegisterConfigDumper(const CheckerManager &mgr) {
return true;
}
@@ -314,7 +312,7 @@ void ento::registerExplodedGraphViewer(CheckerManager &mgr) {
mgr.registerChecker<ExplodedGraphViewer>();
}
-bool ento::shouldRegisterExplodedGraphViewer(const LangOptions &LO) {
+bool ento::shouldRegisterExplodedGraphViewer(const CheckerManager &mgr) {
return true;
}
@@ -346,6 +344,6 @@ void ento::registerReportStmts(CheckerManager &mgr) {
mgr.registerChecker<ReportStmts>();
}
-bool ento::shouldRegisterReportStmts(const LangOptions &LO) {
+bool ento::shouldRegisterReportStmts(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp
new file mode 100644
index 000000000000..6fed999ffc80
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp
@@ -0,0 +1,150 @@
+//==-- DebugContainerModeling.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines a checker for debugging iterator modeling.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+#include "Iterator.h"
+
+using namespace clang;
+using namespace ento;
+using namespace iterator;
+
+namespace {
+
+class DebugContainerModeling
+ : public Checker<eval::Call> {
+
+ std::unique_ptr<BugType> DebugMsgBugType;
+
+ template <typename Getter>
+ void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C,
+ Getter get) const;
+ void analyzerContainerBegin(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerContainerEnd(const CallExpr *CE, CheckerContext &C) const;
+ ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const;
+
+ typedef void (DebugContainerModeling::*FnCheck)(const CallExpr *,
+ CheckerContext &) const;
+
+ CallDescriptionMap<FnCheck> Callbacks = {
+ {{0, "clang_analyzer_container_begin", 1},
+ &DebugContainerModeling::analyzerContainerBegin},
+ {{0, "clang_analyzer_container_end", 1},
+ &DebugContainerModeling::analyzerContainerEnd},
+ };
+
+public:
+ DebugContainerModeling();
+
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const;
+};
+
+} //namespace
+
+DebugContainerModeling::DebugContainerModeling() {
+ DebugMsgBugType.reset(
+ new BugType(this, "Checking analyzer assumptions", "debug",
+ /*SuppressOnSink=*/true));
+}
+
+bool DebugContainerModeling::evalCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return false;
+
+ const FnCheck *Handler = Callbacks.lookup(Call);
+ if (!Handler)
+ return false;
+
+ (this->**Handler)(CE, C);
+ return true;
+}
+
+template <typename Getter>
+void DebugContainerModeling::analyzerContainerDataField(const CallExpr *CE,
+ CheckerContext &C,
+ Getter get) const {
+ if (CE->getNumArgs() == 0) {
+ reportDebugMsg("Missing container argument", C);
+ return;
+ }
+
+ auto State = C.getState();
+ const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
+ if (Cont) {
+ const auto *Data = getContainerData(State, Cont);
+ if (Data) {
+ SymbolRef Field = get(Data);
+ if (Field) {
+ State = State->BindExpr(CE, C.getLocationContext(),
+ nonloc::SymbolVal(Field));
+
+ // Progpagate interestingness from the container's data (marked
+ // interesting by an `ExprInspection` debug call to the container
+ // itself.
+ const NoteTag *InterestingTag =
+ C.getNoteTag(
+ [Cont, Field](PathSensitiveBugReport &BR) -> std::string {
+ if (BR.isInteresting(Field)) {
+ BR.markInteresting(Cont);
+ }
+ return "";
+ });
+ C.addTransition(State, InterestingTag);
+ return;
+ }
+ }
+ }
+
+ auto &BVF = C.getSValBuilder().getBasicValueFactory();
+ State = State->BindExpr(CE, C.getLocationContext(),
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
+}
+
+void DebugContainerModeling::analyzerContainerBegin(const CallExpr *CE,
+ CheckerContext &C) const {
+ analyzerContainerDataField(CE, C, [](const ContainerData *D) {
+ return D->getBegin();
+ });
+}
+
+void DebugContainerModeling::analyzerContainerEnd(const CallExpr *CE,
+ CheckerContext &C) const {
+ analyzerContainerDataField(CE, C, [](const ContainerData *D) {
+ return D->getEnd();
+ });
+}
+
+ExplodedNode *DebugContainerModeling::reportDebugMsg(llvm::StringRef Msg,
+ CheckerContext &C) const {
+ ExplodedNode *N = C.generateNonFatalErrorNode();
+ if (!N)
+ return nullptr;
+
+ auto &BR = C.getBugReporter();
+ BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
+ Msg, N));
+ return N;
+}
+
+void ento::registerDebugContainerModeling(CheckerManager &mgr) {
+ mgr.registerChecker<DebugContainerModeling>();
+}
+
+bool ento::shouldRegisterDebugContainerModeling(const CheckerManager &mgr) {
+ return true;
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp
index 4717fef96341..5833eea56da8 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp
@@ -30,11 +30,6 @@ class DebugIteratorModeling
std::unique_ptr<BugType> DebugMsgBugType;
template <typename Getter>
- void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C,
- Getter get) const;
- void analyzerContainerBegin(const CallExpr *CE, CheckerContext &C) const;
- void analyzerContainerEnd(const CallExpr *CE, CheckerContext &C) const;
- template <typename Getter>
void analyzerIteratorDataField(const CallExpr *CE, CheckerContext &C,
Getter get, SVal Default) const;
void analyzerIteratorPosition(const CallExpr *CE, CheckerContext &C) const;
@@ -46,10 +41,6 @@ class DebugIteratorModeling
CheckerContext &) const;
CallDescriptionMap<FnCheck> Callbacks = {
- {{0, "clang_analyzer_container_begin", 1},
- &DebugIteratorModeling::analyzerContainerBegin},
- {{0, "clang_analyzer_container_end", 1},
- &DebugIteratorModeling::analyzerContainerEnd},
{{0, "clang_analyzer_iterator_position", 1},
&DebugIteratorModeling::analyzerIteratorPosition},
{{0, "clang_analyzer_iterator_container", 1},
@@ -87,49 +78,6 @@ bool DebugIteratorModeling::evalCall(const CallEvent &Call,
}
template <typename Getter>
-void DebugIteratorModeling::analyzerContainerDataField(const CallExpr *CE,
- CheckerContext &C,
- Getter get) const {
- if (CE->getNumArgs() == 0) {
- reportDebugMsg("Missing container argument", C);
- return;
- }
-
- auto State = C.getState();
- const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
- if (Cont) {
- const auto *Data = getContainerData(State, Cont);
- if (Data) {
- SymbolRef Field = get(Data);
- if (Field) {
- State = State->BindExpr(CE, C.getLocationContext(),
- nonloc::SymbolVal(Field));
- C.addTransition(State);
- return;
- }
- }
- }
-
- auto &BVF = C.getSValBuilder().getBasicValueFactory();
- State = State->BindExpr(CE, C.getLocationContext(),
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
-}
-
-void DebugIteratorModeling::analyzerContainerBegin(const CallExpr *CE,
- CheckerContext &C) const {
- analyzerContainerDataField(CE, C, [](const ContainerData *D) {
- return D->getBegin();
- });
-}
-
-void DebugIteratorModeling::analyzerContainerEnd(const CallExpr *CE,
- CheckerContext &C) const {
- analyzerContainerDataField(CE, C, [](const ContainerData *D) {
- return D->getEnd();
- });
-}
-
-template <typename Getter>
void DebugIteratorModeling::analyzerIteratorDataField(const CallExpr *CE,
CheckerContext &C,
Getter get,
@@ -191,6 +139,6 @@ void ento::registerDebugIteratorModeling(CheckerManager &mgr) {
mgr.registerChecker<DebugIteratorModeling>();
}
-bool ento::shouldRegisterDebugIteratorModeling(const LangOptions &LO) {
+bool ento::shouldRegisterDebugIteratorModeling(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
index 45c1984c5e15..7c5833762008 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
@@ -92,6 +92,8 @@ void DeleteWithNonVirtualDtorChecker::checkPreStmt(const CXXDeleteExpr *DE,
"Logic error"));
ExplodedNode *N = C.generateNonFatalErrorNode();
+ if (!N)
+ return;
auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
// Mark region of problematic base class for later use in the BugVisitor.
@@ -148,6 +150,6 @@ void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) {
}
bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker(
- const LangOptions &LO) {
+ const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
index 46100cd1dace..2411f0e2d058 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
@@ -304,6 +304,6 @@ void ento::registerDereferenceChecker(CheckerManager &mgr) {
mgr.registerChecker<DereferenceChecker>();
}
-bool ento::shouldRegisterDereferenceChecker(const LangOptions &LO) {
+bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
index 0c46447e1985..df88b71ff063 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
@@ -219,19 +219,12 @@ static bool AttrFilter(const ObjCMethodDecl *M) {
// 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>();
+ auto Chk = mgr.registerChecker<DirectIvarAssignment>();
+ if (mgr.getAnalyzerOptions().getCheckerBooleanOption(Chk,
+ "AnnotatedFunctions"))
+ Chk->ShouldSkipMethod = &AttrFilter;
}
-bool ento::shouldRegisterDirectIvarAssignment(const LangOptions &LO) {
- return true;
-}
-
-void ento::registerDirectIvarAssignmentForAnnotatedFunctions(
- CheckerManager &mgr) {
- mgr.getChecker<DirectIvarAssignment>()->ShouldSkipMethod = &AttrFilter;
-}
-
-bool ento::shouldRegisterDirectIvarAssignmentForAnnotatedFunctions(
- const LangOptions &LO) {
+bool ento::shouldRegisterDirectIvarAssignment(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
index 8798bde88dcd..2b3164ba4a2c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
@@ -101,6 +101,6 @@ void ento::registerDivZeroChecker(CheckerManager &mgr) {
mgr.registerChecker<DivZeroChecker>();
}
-bool ento::shouldRegisterDivZeroChecker(const LangOptions &LO) {
+bool ento::shouldRegisterDivZeroChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
index 8cc38f9735f3..dbc930d7d37b 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
@@ -203,6 +203,6 @@ void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
mgr.registerChecker<DynamicTypeChecker>();
}
-bool ento::shouldRegisterDynamicTypeChecker(const LangOptions &LO) {
+bool ento::shouldRegisterDynamicTypeChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
index cce3449b8873..14ba5d769969 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -55,6 +55,7 @@ class DynamicTypePropagation:
check::PostStmt<CXXNewExpr>,
check::PreObjCMessage,
check::PostObjCMessage > {
+
const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
CheckerContext &C) const;
@@ -69,8 +70,8 @@ class DynamicTypePropagation:
mutable std::unique_ptr<BugType> ObjCGenericsBugType;
void initBugType() const {
if (!ObjCGenericsBugType)
- ObjCGenericsBugType.reset(
- new BugType(this, "Generics", categories::CoreFoundationObjectiveC));
+ ObjCGenericsBugType.reset(new BugType(
+ GenericCheckName, "Generics", categories::CoreFoundationObjectiveC));
}
class GenericsBugVisitor : public BugReporterVisitor {
@@ -108,12 +109,129 @@ public:
/// This value is set to true, when the Generics checker is turned on.
DefaultBool CheckGenerics;
+ CheckerNameRef GenericCheckName;
+};
+
+bool isObjCClassType(QualType Type) {
+ if (const auto *PointerType = dyn_cast<ObjCObjectPointerType>(Type)) {
+ return PointerType->getObjectType()->isObjCClass();
+ }
+ return false;
+}
+
+struct RuntimeType {
+ const ObjCObjectType *Type = nullptr;
+ bool Precise = false;
+
+ operator bool() const { return Type != nullptr; }
};
+
+RuntimeType inferReceiverType(const ObjCMethodCall &Message,
+ CheckerContext &C) {
+ const ObjCMessageExpr *MessageExpr = Message.getOriginExpr();
+
+ // Check if we can statically infer the actual type precisely.
+ //
+ // 1. Class is written directly in the message:
+ // \code
+ // [ActualClass classMethod];
+ // \endcode
+ if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Class) {
+ return {MessageExpr->getClassReceiver()->getAs<ObjCObjectType>(),
+ /*Precise=*/true};
+ }
+
+ // 2. Receiver is 'super' from a class method (a.k.a 'super' is a
+ // class object).
+ // \code
+ // [super classMethod];
+ // \endcode
+ if (MessageExpr->getReceiverKind() == ObjCMessageExpr::SuperClass) {
+ return {MessageExpr->getSuperType()->getAs<ObjCObjectType>(),
+ /*Precise=*/true};
+ }
+
+ // 3. Receiver is 'super' from an instance method (a.k.a 'super' is an
+ // instance of a super class).
+ // \code
+ // [super instanceMethod];
+ // \encode
+ if (MessageExpr->getReceiverKind() == ObjCMessageExpr::SuperInstance) {
+ if (const auto *ObjTy =
+ MessageExpr->getSuperType()->getAs<ObjCObjectPointerType>())
+ return {ObjTy->getObjectType(), /*Precise=*/true};
+ }
+
+ const Expr *RecE = MessageExpr->getInstanceReceiver();
+
+ if (!RecE)
+ return {};
+
+ // Otherwise, let's try to get type information from our estimations of
+ // runtime types.
+ QualType InferredType;
+ SVal ReceiverSVal = C.getSVal(RecE);
+ ProgramStateRef State = C.getState();
+
+ if (const MemRegion *ReceiverRegion = ReceiverSVal.getAsRegion()) {
+ if (DynamicTypeInfo DTI = getDynamicTypeInfo(State, ReceiverRegion)) {
+ InferredType = DTI.getType().getCanonicalType();
+ }
+ }
+
+ if (SymbolRef ReceiverSymbol = ReceiverSVal.getAsSymbol()) {
+ if (InferredType.isNull()) {
+ InferredType = ReceiverSymbol->getType();
+ }
+
+ // If receiver is a Class object, we want to figure out the type it
+ // represents.
+ if (isObjCClassType(InferredType)) {
+ // We actually might have some info on what type is contained in there.
+ if (DynamicTypeInfo DTI =
+ getClassObjectDynamicTypeInfo(State, ReceiverSymbol)) {
+
+ // Types in Class objects can be ONLY Objective-C types
+ return {cast<ObjCObjectType>(DTI.getType()), !DTI.canBeASubClass()};
+ }
+
+ SVal SelfSVal = State->getSelfSVal(C.getLocationContext());
+
+ // Another way we can guess what is in Class object, is when it is a
+ // 'self' variable of the current class method.
+ if (ReceiverSVal == SelfSVal) {
+ // In this case, we should return the type of the enclosing class
+ // declaration.
+ if (const ObjCMethodDecl *MD =
+ dyn_cast<ObjCMethodDecl>(C.getStackFrame()->getDecl()))
+ if (const ObjCObjectType *ObjTy = dyn_cast<ObjCObjectType>(
+ MD->getClassInterface()->getTypeForDecl()))
+ return {ObjTy};
+ }
+ }
+ }
+
+ // Unfortunately, it seems like we have no idea what that type is.
+ if (InferredType.isNull()) {
+ return {};
+ }
+
+ // We can end up here if we got some dynamic type info and the
+ // receiver is not one of the known Class objects.
+ if (const auto *ReceiverInferredType =
+ dyn_cast<ObjCObjectPointerType>(InferredType)) {
+ return {ReceiverInferredType->getObjectType()};
+ }
+
+ // Any other type (like 'Class') is not really useful at this point.
+ return {};
+}
} // end anonymous namespace
void DynamicTypePropagation::checkDeadSymbols(SymbolReaper &SR,
CheckerContext &C) const {
ProgramStateRef State = removeDeadTypes(C.getState(), SR);
+ State = removeDeadClassObjectTypes(State, SR);
MostSpecializedTypeArgsMapTy TyArgMap =
State->get<MostSpecializedTypeArgsMap>();
@@ -209,12 +327,21 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call,
case OMF_alloc:
case OMF_new: {
// Get the type of object that will get created.
- const ObjCMessageExpr *MsgE = Msg->getOriginExpr();
- const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C);
+ RuntimeType ObjTy = inferReceiverType(*Msg, C);
+
if (!ObjTy)
return;
+
QualType DynResTy =
- C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0));
+ C.getASTContext().getObjCObjectPointerType(QualType(ObjTy.Type, 0));
+ // We used to assume that whatever type we got from inferring the
+ // type is actually precise (and it is not exactly correct).
+ // A big portion of the existing behavior depends on that assumption
+ // (e.g. certain inlining won't take place). For this reason, we don't
+ // use ObjTy.Precise flag here.
+ //
+ // TODO: We should mitigate this problem some time in the future
+ // and replace hardcoded 'false' with '!ObjTy.Precise'.
C.addTransition(setDynamicTypeInfo(State, RetReg, DynResTy, false));
break;
}
@@ -303,40 +430,6 @@ void DynamicTypePropagation::checkPostStmt(const CXXNewExpr *NewE,
/*CanBeSubClassed=*/false));
}
-const ObjCObjectType *
-DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
- CheckerContext &C) const {
- if (MsgE->getReceiverKind() == ObjCMessageExpr::Class) {
- if (const ObjCObjectType *ObjTy
- = MsgE->getClassReceiver()->getAs<ObjCObjectType>())
- return ObjTy;
- }
-
- if (MsgE->getReceiverKind() == ObjCMessageExpr::SuperClass) {
- if (const ObjCObjectType *ObjTy
- = MsgE->getSuperType()->getAs<ObjCObjectType>())
- return ObjTy;
- }
-
- const Expr *RecE = MsgE->getInstanceReceiver();
- if (!RecE)
- return nullptr;
-
- RecE= RecE->IgnoreParenImpCasts();
- if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RecE)) {
- const StackFrameContext *SFCtx = C.getStackFrame();
- // Are we calling [self alloc]? If this is self, get the type of the
- // enclosing ObjC class.
- if (DRE->getDecl() == SFCtx->getSelfDecl()) {
- if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(SFCtx->getDecl()))
- if (const ObjCObjectType *ObjTy =
- dyn_cast<ObjCObjectType>(MD->getClassInterface()->getTypeForDecl()))
- return ObjTy;
- }
- }
- return nullptr;
-}
-
// Return a better dynamic type if one can be derived from the cast.
// Compare the current dynamic type of the region and the new type to which we
// are casting. If the new type is lower in the inheritance hierarchy, pick it.
@@ -821,25 +914,56 @@ void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M,
Selector Sel = MessageExpr->getSelector();
ProgramStateRef State = C.getState();
- // Inference for class variables.
- // We are only interested in cases where the class method is invoked on a
- // class. This method is provided by the runtime and available on all classes.
- if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Class &&
- Sel.getAsString() == "class") {
- QualType ReceiverType = MessageExpr->getClassReceiver();
- const auto *ReceiverClassType = ReceiverType->castAs<ObjCObjectType>();
- if (!ReceiverClassType->isSpecialized())
- return;
- QualType ReceiverClassPointerType =
- C.getASTContext().getObjCObjectPointerType(
- QualType(ReceiverClassType, 0));
- const auto *InferredType =
- ReceiverClassPointerType->castAs<ObjCObjectPointerType>();
+ // Here we try to propagate information on Class objects.
+ if (Sel.getAsString() == "class") {
+ // We try to figure out the type from the receiver of the 'class' message.
+ if (RuntimeType ReceiverRuntimeType = inferReceiverType(M, C)) {
+
+ ReceiverRuntimeType.Type->getSuperClassType();
+ QualType ReceiverClassType(ReceiverRuntimeType.Type, 0);
+
+ // We want to consider only precise information on generics.
+ if (ReceiverRuntimeType.Type->isSpecialized() &&
+ ReceiverRuntimeType.Precise) {
+ QualType ReceiverClassPointerType =
+ C.getASTContext().getObjCObjectPointerType(ReceiverClassType);
+ const auto *InferredType =
+ ReceiverClassPointerType->castAs<ObjCObjectPointerType>();
+ State = State->set<MostSpecializedTypeArgsMap>(RetSym, InferredType);
+ }
- State = State->set<MostSpecializedTypeArgsMap>(RetSym, InferredType);
- C.addTransition(State);
- return;
+ // Constrain the resulting class object to the inferred type.
+ State = setClassObjectDynamicTypeInfo(State, RetSym, ReceiverClassType,
+ !ReceiverRuntimeType.Precise);
+
+ C.addTransition(State);
+ return;
+ }
+ }
+
+ if (Sel.getAsString() == "superclass") {
+ // We try to figure out the type from the receiver of the 'superclass'
+ // message.
+ if (RuntimeType ReceiverRuntimeType = inferReceiverType(M, C)) {
+
+ // Result type would be a super class of the receiver's type.
+ QualType ReceiversSuperClass =
+ ReceiverRuntimeType.Type->getSuperClassType();
+
+ // Check if it really had super class.
+ //
+ // TODO: we can probably pay closer attention to cases when the class
+ // object can be 'nil' as the result of such message.
+ if (!ReceiversSuperClass.isNull()) {
+ // Constrain the resulting class object to the inferred type.
+ State = setClassObjectDynamicTypeInfo(
+ State, RetSym, ReceiversSuperClass, !ReceiverRuntimeType.Precise);
+
+ C.addTransition(State);
+ }
+ return;
+ }
}
// Tracking for return types.
@@ -979,9 +1103,10 @@ PathDiagnosticPieceRef DynamicTypePropagation::GenericsBugVisitor::VisitNode(
void ento::registerObjCGenericsChecker(CheckerManager &mgr) {
DynamicTypePropagation *checker = mgr.getChecker<DynamicTypePropagation>();
checker->CheckGenerics = true;
+ checker->GenericCheckName = mgr.getCurrentCheckerName();
}
-bool ento::shouldRegisterObjCGenericsChecker(const LangOptions &LO) {
+bool ento::shouldRegisterObjCGenericsChecker(const CheckerManager &mgr) {
return true;
}
@@ -989,6 +1114,6 @@ void ento::registerDynamicTypePropagation(CheckerManager &mgr) {
mgr.registerChecker<DynamicTypePropagation>();
}
-bool ento::shouldRegisterDynamicTypePropagation(const LangOptions &LO) {
+bool ento::shouldRegisterDynamicTypePropagation(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
index 481a5685a71f..0e94b915a468 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
@@ -142,6 +142,6 @@ void ento::registerEnumCastOutOfRangeChecker(CheckerManager &mgr) {
mgr.registerChecker<EnumCastOutOfRangeChecker>();
}
-bool ento::shouldRegisterEnumCastOutOfRangeChecker(const LangOptions &LO) {
+bool ento::shouldRegisterEnumCastOutOfRangeChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
index 17c813962a23..4225d890c47a 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include "Taint.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Checkers/SValExplainer.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
@@ -13,6 +14,7 @@
#include "clang/StaticAnalyzer/Core/IssueHash.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/ScopedPrinter.h"
@@ -45,13 +47,17 @@ class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols,
void analyzerHashDump(const CallExpr *CE, CheckerContext &C) const;
void analyzerDenote(const CallExpr *CE, CheckerContext &C) const;
void analyzerExpress(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerIsTainted(const CallExpr *CE, CheckerContext &C) const;
typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
CheckerContext &C) const;
- ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C) const;
+ // Optional parameter `ExprVal` for expression value to be marked interesting.
+ ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C,
+ Optional<SVal> ExprVal = None) const;
ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR,
- ExplodedNode *N) const;
+ ExplodedNode *N,
+ Optional<SVal> ExprVal = None) const;
public:
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
@@ -72,26 +78,34 @@ bool ExprInspectionChecker::evalCall(const CallEvent &Call,
// These checks should have no effect on the surrounding environment
// (globals should not be invalidated, etc), hence the use of evalCall.
- FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
- .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
- .Case("clang_analyzer_checkInlined",
- &ExprInspectionChecker::analyzerCheckInlined)
- .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
- .Case("clang_analyzer_warnIfReached",
- &ExprInspectionChecker::analyzerWarnIfReached)
- .Case("clang_analyzer_warnOnDeadSymbol",
- &ExprInspectionChecker::analyzerWarnOnDeadSymbol)
- .StartsWith("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain)
- .StartsWith("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump)
- .Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent)
- .Case("clang_analyzer_printState",
- &ExprInspectionChecker::analyzerPrintState)
- .Case("clang_analyzer_numTimesReached",
- &ExprInspectionChecker::analyzerNumTimesReached)
- .Case("clang_analyzer_hashDump", &ExprInspectionChecker::analyzerHashDump)
- .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote)
- .Case("clang_analyzer_express", &ExprInspectionChecker::analyzerExpress)
- .Default(nullptr);
+ FnCheck Handler =
+ llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
+ .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
+ .Case("clang_analyzer_checkInlined",
+ &ExprInspectionChecker::analyzerCheckInlined)
+ .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
+ .Case("clang_analyzer_warnIfReached",
+ &ExprInspectionChecker::analyzerWarnIfReached)
+ .Case("clang_analyzer_warnOnDeadSymbol",
+ &ExprInspectionChecker::analyzerWarnOnDeadSymbol)
+ .StartsWith("clang_analyzer_explain",
+ &ExprInspectionChecker::analyzerExplain)
+ .StartsWith("clang_analyzer_dump",
+ &ExprInspectionChecker::analyzerDump)
+ .Case("clang_analyzer_getExtent",
+ &ExprInspectionChecker::analyzerGetExtent)
+ .Case("clang_analyzer_printState",
+ &ExprInspectionChecker::analyzerPrintState)
+ .Case("clang_analyzer_numTimesReached",
+ &ExprInspectionChecker::analyzerNumTimesReached)
+ .Case("clang_analyzer_hashDump",
+ &ExprInspectionChecker::analyzerHashDump)
+ .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote)
+ .Case("clang_analyzer_express",
+ &ExprInspectionChecker::analyzerExpress)
+ .StartsWith("clang_analyzer_isTainted",
+ &ExprInspectionChecker::analyzerIsTainted)
+ .Default(nullptr);
if (!Handler)
return false;
@@ -133,22 +147,28 @@ static const char *getArgumentValueString(const CallExpr *CE,
}
ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
- CheckerContext &C) const {
+ CheckerContext &C,
+ Optional<SVal> ExprVal) const {
ExplodedNode *N = C.generateNonFatalErrorNode();
- reportBug(Msg, C.getBugReporter(), N);
+ reportBug(Msg, C.getBugReporter(), N, ExprVal);
return N;
}
ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
BugReporter &BR,
- ExplodedNode *N) const {
+ ExplodedNode *N,
+ Optional<SVal> ExprVal) const {
if (!N)
return nullptr;
if (!BT)
BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
- BR.emitReport(std::make_unique<PathSensitiveBugReport>(*BT, Msg, N));
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
+ if (ExprVal) {
+ R->markInteresting(*ExprVal);
+ }
+ BR.emitReport(std::move(R));
return N;
}
@@ -234,8 +254,9 @@ void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE,
}
ProgramStateRef State = C.getState();
- State = State->BindExpr(CE, C.getLocationContext(),
- MR->getExtent(C.getSValBuilder()));
+ DefinedOrUnknownSVal Size = getDynamicSize(State, MR, C.getSValBuilder());
+
+ State = State->BindExpr(CE, C.getLocationContext(), Size);
C.addTransition(State);
}
@@ -394,7 +415,8 @@ void ExprInspectionChecker::analyzerExpress(const CallExpr *CE,
return;
}
- SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol();
+ SVal ArgVal = C.getSVal(CE->getArg(0));
+ SymbolRef Sym = ArgVal.getAsSymbol();
if (!Sym) {
reportBug("Not a symbol", C);
return;
@@ -407,13 +429,24 @@ void ExprInspectionChecker::analyzerExpress(const CallExpr *CE,
return;
}
- reportBug(*Str, C);
+ reportBug(*Str, C, ArgVal);
+}
+
+void ExprInspectionChecker::analyzerIsTainted(const CallExpr *CE,
+ CheckerContext &C) const {
+ if (CE->getNumArgs() != 1) {
+ reportBug("clang_analyzer_isTainted() requires exactly one argument", C);
+ return;
+ }
+ const bool IsTainted =
+ taint::isTainted(C.getState(), CE->getArg(0), C.getLocationContext());
+ reportBug(IsTainted ? "YES" : "NO", C);
}
void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
Mgr.registerChecker<ExprInspectionChecker>();
}
-bool ento::shouldRegisterExprInspectionChecker(const LangOptions &LO) {
+bool ento::shouldRegisterExprInspectionChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
index b315a8452285..6275e49e51ae 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
@@ -66,6 +66,6 @@ void ento::registerFixedAddressChecker(CheckerManager &mgr) {
mgr.registerChecker<FixedAddressChecker>();
}
-bool ento::shouldRegisterFixedAddressChecker(const LangOptions &LO) {
+bool ento::shouldRegisterFixedAddressChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
index 3c04983df443..fc35082705fa 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
@@ -90,6 +90,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+#include "llvm/ADT/StringExtras.h"
using namespace clang;
using namespace ento;
@@ -149,6 +150,10 @@ public:
CASE(Kind::Released)
CASE(Kind::Escaped)
}
+ if (ErrorSym) {
+ OS << " ErrorSym: ";
+ ErrorSym->dumpToStream(OS);
+ }
}
LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }
@@ -314,6 +319,17 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
// Function returns an open handle.
if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) {
SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
+ Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string {
+ auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
+ if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) {
+ std::string SBuf;
+ llvm::raw_string_ostream OS(SBuf);
+ OS << "Function '" << FuncDecl->getNameAsString()
+ << "' returns an open handle";
+ return OS.str();
+ } else
+ return "";
+ });
State =
State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr));
}
@@ -322,6 +338,7 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
if (Arg >= FuncDecl->getNumParams())
break;
const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
+ unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1;
SymbolRef Handle =
getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State);
if (!Handle)
@@ -335,20 +352,28 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C);
return;
} else {
- Notes.push_back([Handle](BugReport &BR) {
+ Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
- return "Handle released here.";
+ std::string SBuf;
+ llvm::raw_string_ostream OS(SBuf);
+ OS << "Handle released through " << ParamDiagIdx
+ << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
+ return OS.str();
} else
return "";
});
State = State->set<HStateMap>(Handle, HandleState::getReleased());
}
} else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
- Notes.push_back([Handle](BugReport &BR) {
+ Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
- return "Handle allocated here.";
+ std::string SBuf;
+ llvm::raw_string_ostream OS(SBuf);
+ OS << "Handle allocated through " << ParamDiagIdx
+ << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
+ return OS.str();
} else
return "";
});
@@ -358,8 +383,8 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
}
const NoteTag *T = nullptr;
if (!Notes.empty()) {
- T = C.getNoteTag(
- [this, Notes{std::move(Notes)}](BugReport &BR) -> std::string {
+ T = C.getNoteTag([this, Notes{std::move(Notes)}](
+ PathSensitiveBugReport &BR) -> std::string {
if (&BR.getBugType() != &UseAfterReleaseBugType &&
&BR.getBugType() != &LeakBugType &&
&BR.getBugType() != &DoubleReleaseBugType)
@@ -381,7 +406,13 @@ void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper,
SmallVector<SymbolRef, 2> LeakedSyms;
HStateMapTy TrackedHandles = State->get<HStateMap>();
for (auto &CurItem : TrackedHandles) {
- if (!SymReaper.isDead(CurItem.first))
+ SymbolRef ErrorSym = CurItem.second.getErrorSym();
+ // Keeping zombie handle symbols. In case the error symbol is dying later
+ // than the handle symbol we might produce spurious leak warnings (in case
+ // we find out later from the status code that the handle allocation failed
+ // in the first place).
+ if (!SymReaper.isDead(CurItem.first) ||
+ (ErrorSym && !SymReaper.isDead(ErrorSym)))
continue;
if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated())
LeakedSyms.push_back(CurItem.first);
@@ -535,7 +566,7 @@ void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) {
mgr.registerChecker<FuchsiaHandleChecker>();
}
-bool ento::shouldRegisterFuchsiaHandleChecker(const LangOptions &LO) {
+bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp
index d471c23b83bf..63fbe75fd498 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp
@@ -52,18 +52,16 @@ public:
BugReporter &BR) const;
};
-auto callsName(const char *FunctionName)
- -> decltype(callee(functionDecl())) {
+decltype(auto) callsName(const char *FunctionName) {
return callee(functionDecl(hasName(FunctionName)));
}
-auto equalsBoundArgDecl(int ArgIdx, const char *DeclName)
- -> decltype(hasArgument(0, expr())) {
+decltype(auto) equalsBoundArgDecl(int ArgIdx, const char *DeclName) {
return hasArgument(ArgIdx, ignoringParenCasts(declRefExpr(
to(varDecl(equalsBoundNode(DeclName))))));
}
-auto bindAssignmentToDecl(const char *DeclName) -> decltype(hasLHS(expr())) {
+decltype(auto) bindAssignmentToDecl(const char *DeclName) {
return hasLHS(ignoringParenImpCasts(
declRefExpr(to(varDecl().bind(DeclName)))));
}
@@ -227,6 +225,6 @@ void ento::registerGCDAntipattern(CheckerManager &Mgr) {
Mgr.registerChecker<GCDAntipatternChecker>();
}
-bool ento::shouldRegisterGCDAntipattern(const LangOptions &LO) {
+bool ento::shouldRegisterGCDAntipattern(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
index f4308f510f0b..8d9afbe88aa8 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
@@ -291,8 +291,9 @@ void ento::registerGTestChecker(CheckerManager &Mgr) {
Mgr.registerChecker<GTestChecker>();
}
-bool ento::shouldRegisterGTestChecker(const LangOptions &LO) {
+bool ento::shouldRegisterGTestChecker(const CheckerManager &mgr) {
// gtest is a C++ API so there is no sense running the checker
// if not compiling for C++.
+ const LangOptions &LO = mgr.getLangOpts();
return LO.CPlusPlus;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
index 302d5bb1bea8..c06d2fcd8e7d 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -22,11 +22,14 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/Support/YAMLTraits.h"
+
#include <algorithm>
#include <limits>
+#include <memory>
#include <unordered_map>
#include <utility>
@@ -35,17 +38,15 @@ using namespace ento;
using namespace taint;
namespace {
-class GenericTaintChecker
- : public Checker<check::PostStmt<CallExpr>, check::PreStmt<CallExpr>> {
+class GenericTaintChecker : public Checker<check::PreCall, check::PostCall> {
public:
static void *getTag() {
static int Tag;
return &Tag;
}
- void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
-
- void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
const char *Sep) const override;
@@ -81,7 +82,7 @@ public:
/// Convert SignedArgVector to ArgVector.
ArgVector convertToArgVector(CheckerManager &Mgr, const std::string &Option,
- SignedArgVector Args);
+ const SignedArgVector &Args);
/// Parse the config.
void parseConfiguration(CheckerManager &Mgr, const std::string &Option,
@@ -96,7 +97,8 @@ private:
mutable std::unique_ptr<BugType> BT;
void initBugType() const {
if (!BT)
- BT.reset(new BugType(this, "Use of Untrusted Data", "Untrusted Data"));
+ BT = std::make_unique<BugType>(this, "Use of Untrusted Data",
+ "Untrusted Data");
}
struct FunctionData {
@@ -106,9 +108,12 @@ private:
FunctionData &operator=(const FunctionData &) = delete;
FunctionData &operator=(FunctionData &&) = delete;
- static Optional<FunctionData> create(const CallExpr *CE,
+ static Optional<FunctionData> create(const CallEvent &Call,
const CheckerContext &C) {
- const FunctionDecl *FDecl = C.getCalleeDecl(CE);
+ if (!Call.getDecl())
+ return None;
+
+ const FunctionDecl *FDecl = Call.getDecl()->getAsFunction();
if (!FDecl || (FDecl->getKind() != Decl::Function &&
FDecl->getKind() != Decl::CXXMethod))
return None;
@@ -132,33 +137,33 @@ private:
/// Catch taint related bugs. Check if tainted data is passed to a
/// system call etc. Returns true on matching.
- bool checkPre(const CallExpr *CE, const FunctionData &FData,
+ bool checkPre(const CallEvent &Call, const FunctionData &FData,
CheckerContext &C) const;
/// Add taint sources on a pre-visit. Returns true on matching.
- bool addSourcesPre(const CallExpr *CE, const FunctionData &FData,
+ bool addSourcesPre(const CallEvent &Call, const FunctionData &FData,
CheckerContext &C) const;
/// Mark filter's arguments not tainted on a pre-visit. Returns true on
/// matching.
- bool addFiltersPre(const CallExpr *CE, const FunctionData &FData,
+ bool addFiltersPre(const CallEvent &Call, const FunctionData &FData,
CheckerContext &C) const;
/// Propagate taint generated at pre-visit. Returns true on matching.
- bool propagateFromPre(const CallExpr *CE, CheckerContext &C) const;
+ static bool propagateFromPre(const CallEvent &Call, CheckerContext &C);
/// Check if the region the expression evaluates to is the standard input,
/// and thus, is tainted.
static bool isStdin(const Expr *E, CheckerContext &C);
/// Given a pointer argument, return the value it points to.
- static Optional<SVal> getPointedToSVal(CheckerContext &C, const Expr *Arg);
+ static Optional<SVal> getPointeeOf(CheckerContext &C, const Expr *Arg);
/// Check for CWE-134: Uncontrolled Format String.
static constexpr llvm::StringLiteral MsgUncontrolledFormatString =
"Untrusted data is used as a format string "
"(CWE-134: Uncontrolled Format String)";
- bool checkUncontrolledFormatString(const CallExpr *CE,
+ bool checkUncontrolledFormatString(const CallEvent &Call,
CheckerContext &C) const;
/// Check for:
@@ -167,7 +172,7 @@ private:
static constexpr llvm::StringLiteral MsgSanitizeSystemArgs =
"Untrusted data is passed to a system call "
"(CERT/STR02-C. Sanitize data passed to complex subsystems)";
- bool checkSystemCall(const CallExpr *CE, StringRef Name,
+ bool checkSystemCall(const CallEvent &Call, StringRef Name,
CheckerContext &C) const;
/// Check if tainted data is used as a buffer size ins strn.. functions,
@@ -176,13 +181,12 @@ private:
"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)";
- bool checkTaintedBufferSize(const CallExpr *CE, const FunctionDecl *FDecl,
- CheckerContext &C) const;
+ bool checkTaintedBufferSize(const CallEvent &Call, CheckerContext &C) const;
/// Check if tainted data is used as a custom sink's parameter.
static constexpr llvm::StringLiteral MsgCustomSink =
"Untrusted data is passed to a user-defined sink";
- bool checkCustomSinks(const CallExpr *CE, const FunctionData &FData,
+ bool checkCustomSinks(const CallEvent &Call, const FunctionData &FData,
CheckerContext &C) const;
/// Generate a report if the expression is tainted or points to tainted data.
@@ -212,7 +216,7 @@ private:
/// ReturnValueIndex is added to the dst list, the return value will be
/// tainted.
struct TaintPropagationRule {
- using PropagationFuncType = bool (*)(bool IsTainted, const CallExpr *,
+ using PropagationFuncType = bool (*)(bool IsTainted, const CallEvent &Call,
CheckerContext &C);
/// List of arguments which can be taint sources and should be checked.
@@ -256,7 +260,8 @@ private:
return (llvm::find(DstArgs, ArgNum) != DstArgs.end());
}
- static bool isTaintedOrPointsToTainted(const Expr *E, ProgramStateRef State,
+ static bool isTaintedOrPointsToTainted(const Expr *E,
+ const ProgramStateRef &State,
CheckerContext &C) {
if (isTainted(State, E, C.getLocationContext()) || isStdin(E, C))
return true;
@@ -264,16 +269,16 @@ private:
if (!E->getType().getTypePtr()->isPointerType())
return false;
- Optional<SVal> V = getPointedToSVal(C, E);
+ Optional<SVal> V = getPointeeOf(C, E);
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;
+ ProgramStateRef process(const CallEvent &Call, CheckerContext &C) const;
// Functions for custom taintedness propagation.
- static bool postSocket(bool IsTainted, const CallExpr *CE,
+ static bool postSocket(bool IsTainted, const CallEvent &Call,
CheckerContext &C);
};
@@ -351,8 +356,10 @@ template <> struct MappingTraits<TaintConfig::NameScopeArgs> {
/// points to data, which should be tainted on return.
REGISTER_SET_WITH_PROGRAMSTATE(TaintArgsOnPostVisit, unsigned)
-GenericTaintChecker::ArgVector GenericTaintChecker::convertToArgVector(
- CheckerManager &Mgr, const std::string &Option, SignedArgVector Args) {
+GenericTaintChecker::ArgVector
+GenericTaintChecker::convertToArgVector(CheckerManager &Mgr,
+ const std::string &Option,
+ const SignedArgVector &Args) {
ArgVector Result;
for (int Arg : Args) {
if (Arg == -1)
@@ -396,7 +403,7 @@ void GenericTaintChecker::parseConfiguration(CheckerManager &Mgr,
template <typename T>
auto GenericTaintChecker::findFunctionInConfig(const ConfigDataMap<T> &Map,
const FunctionData &FData) {
- auto Range = Map.equal_range(FData.Name);
+ auto Range = Map.equal_range(std::string(FData.Name));
auto It =
std::find_if(Range.first, Range.second, [&FData](const auto &Entry) {
const auto &Value = Entry.second;
@@ -419,125 +426,125 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
llvm::StringSwitch<TaintPropagationRule>(FData.FullName)
// 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}))
+ .Case("fdopen", {{}, {ReturnValueIndex}})
+ .Case("fopen", {{}, {ReturnValueIndex}})
+ .Case("freopen", {{}, {ReturnValueIndex}})
+ .Case("getch", {{}, {ReturnValueIndex}})
+ .Case("getchar", {{}, {ReturnValueIndex}})
+ .Case("getchar_unlocked", {{}, {ReturnValueIndex}})
+ .Case("getenv", {{}, {ReturnValueIndex}})
+ .Case("gets", {{}, {0, ReturnValueIndex}})
+ .Case("scanf", {{}, {}, VariadicType::Dst, 1})
+ .Case("socket", {{},
+ {ReturnValueIndex},
+ VariadicType::None,
+ InvalidArgIndex,
+ &TaintPropagationRule::postSocket})
+ .Case("wgetch", {{}, {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());
+ .Case("atoi", {{0}, {ReturnValueIndex}})
+ .Case("atol", {{0}, {ReturnValueIndex}})
+ .Case("atoll", {{0}, {ReturnValueIndex}})
+ .Case("fgetc", {{0}, {ReturnValueIndex}})
+ .Case("fgetln", {{0}, {ReturnValueIndex}})
+ .Case("fgets", {{2}, {0, ReturnValueIndex}})
+ .Case("fscanf", {{0}, {}, VariadicType::Dst, 2})
+ .Case("sscanf", {{0}, {}, VariadicType::Dst, 2})
+ .Case("getc", {{0}, {ReturnValueIndex}})
+ .Case("getc_unlocked", {{0}, {ReturnValueIndex}})
+ .Case("getdelim", {{3}, {0}})
+ .Case("getline", {{2}, {0}})
+ .Case("getw", {{0}, {ReturnValueIndex}})
+ .Case("pread", {{0, 1, 2, 3}, {1, ReturnValueIndex}})
+ .Case("read", {{0, 2}, {1, ReturnValueIndex}})
+ .Case("strchr", {{0}, {ReturnValueIndex}})
+ .Case("strrchr", {{0}, {ReturnValueIndex}})
+ .Case("tolower", {{0}, {ReturnValueIndex}})
+ .Case("toupper", {{0}, {ReturnValueIndex}})
+ .Default({});
if (!Rule.isNull())
return Rule;
+ assert(FData.FDecl);
// Check if it's one of the memory setting/copying functions.
// This check is specialized but faster then calling isCLibraryFunction.
const FunctionDecl *FDecl = FData.FDecl;
unsigned BId = 0;
- if ((BId = FDecl->getMemoryFunctionKind()))
+ if ((BId = FDecl->getMemoryFunctionKind())) {
switch (BId) {
case Builtin::BImemcpy:
case Builtin::BImemmove:
case Builtin::BIstrncpy:
case Builtin::BIstrncat:
- return TaintPropagationRule({1, 2}, {0, ReturnValueIndex});
+ return {{1, 2}, {0, ReturnValueIndex}};
case Builtin::BIstrlcpy:
case Builtin::BIstrlcat:
- return TaintPropagationRule({1, 2}, {0});
+ return {{1, 2}, {0}};
case Builtin::BIstrndup:
- return TaintPropagationRule({0, 1}, {ReturnValueIndex});
+ return {{0, 1}, {ReturnValueIndex}};
default:
break;
- };
+ }
+ }
// Process all other functions which could be defined as builtins.
if (Rule.isNull()) {
- 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, ReturnValueIndex});
- else if (C.isCLibraryFunction(FDecl, "bcopy"))
- return TaintPropagationRule({0, 2}, {1});
- else if (C.isCLibraryFunction(FDecl, "strdup") ||
- C.isCLibraryFunction(FDecl, "strdupa"))
- return TaintPropagationRule({0}, {ReturnValueIndex});
- else if (C.isCLibraryFunction(FDecl, "wcsdup"))
- return TaintPropagationRule({0}, {ReturnValueIndex});
+ const auto OneOf = [FDecl](const auto &... Name) {
+ // FIXME: use fold expression in C++17
+ using unused = int[];
+ bool ret = false;
+ static_cast<void>(unused{
+ 0, (ret |= CheckerContext::isCLibraryFunction(FDecl, Name), 0)...});
+ return ret;
+ };
+ if (OneOf("snprintf"))
+ return {{1}, {0, ReturnValueIndex}, VariadicType::Src, 3};
+ if (OneOf("sprintf"))
+ return {{}, {0, ReturnValueIndex}, VariadicType::Src, 2};
+ if (OneOf("strcpy", "stpcpy", "strcat"))
+ return {{1}, {0, ReturnValueIndex}};
+ if (OneOf("bcopy"))
+ return {{0, 2}, {1}};
+ if (OneOf("strdup", "strdupa", "wcsdup"))
+ return {{0}, {ReturnValueIndex}};
}
- // Skipping the following functions, since they might be used for cleansing
- // or smart memory copy:
+ // Skipping the following functions, since they might be used for cleansing or
+ // smart memory copy:
// - memccpy - copying until hitting a special character.
auto It = findFunctionInConfig(CustomPropagations, FData);
- if (It != CustomPropagations.end()) {
- const auto &Value = It->second;
- return Value.second;
- }
-
- return TaintPropagationRule();
+ if (It != CustomPropagations.end())
+ return It->second.second;
+ return {};
}
-void GenericTaintChecker::checkPreStmt(const CallExpr *CE,
+void GenericTaintChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
- Optional<FunctionData> FData = FunctionData::create(CE, C);
+ Optional<FunctionData> FData = FunctionData::create(Call, C);
if (!FData)
return;
// Check for taintedness related errors first: system call, uncontrolled
// format string, tainted buffer size.
- if (checkPre(CE, *FData, C))
+ if (checkPre(Call, *FData, C))
return;
// Marks the function's arguments and/or return value tainted if it present in
// the list.
- if (addSourcesPre(CE, *FData, C))
+ if (addSourcesPre(Call, *FData, C))
return;
- addFiltersPre(CE, *FData, C);
+ addFiltersPre(Call, *FData, C);
}
-void GenericTaintChecker::checkPostStmt(const CallExpr *CE,
+void GenericTaintChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
// Set the marked values as tainted. The return value only accessible from
// checkPostStmt.
- propagateFromPre(CE, C);
+ propagateFromPre(Call, C);
}
void GenericTaintChecker::printState(raw_ostream &Out, ProgramStateRef State,
@@ -545,14 +552,14 @@ void GenericTaintChecker::printState(raw_ostream &Out, ProgramStateRef State,
printTaint(State, Out, NL, Sep);
}
-bool GenericTaintChecker::addSourcesPre(const CallExpr *CE,
+bool GenericTaintChecker::addSourcesPre(const CallEvent &Call,
const FunctionData &FData,
CheckerContext &C) const {
// First, try generating a propagation rule for this function.
TaintPropagationRule Rule = TaintPropagationRule::getTaintPropagationRule(
this->CustomPropagations, FData, C);
if (!Rule.isNull()) {
- ProgramStateRef State = Rule.process(CE, C);
+ ProgramStateRef State = Rule.process(Call, C);
if (State) {
C.addTransition(State);
return true;
@@ -561,7 +568,7 @@ bool GenericTaintChecker::addSourcesPre(const CallExpr *CE,
return false;
}
-bool GenericTaintChecker::addFiltersPre(const CallExpr *CE,
+bool GenericTaintChecker::addFiltersPre(const CallEvent &Call,
const FunctionData &FData,
CheckerContext &C) const {
auto It = findFunctionInConfig(CustomFilters, FData);
@@ -572,11 +579,11 @@ bool GenericTaintChecker::addFiltersPre(const CallExpr *CE,
const auto &Value = It->second;
const ArgVector &Args = Value.second;
for (unsigned ArgNum : Args) {
- if (ArgNum >= CE->getNumArgs())
+ if (ArgNum >= Call.getNumArgs())
continue;
- const Expr *Arg = CE->getArg(ArgNum);
- Optional<SVal> V = getPointedToSVal(C, Arg);
+ const Expr *Arg = Call.getArgExpr(ArgNum);
+ Optional<SVal> V = getPointeeOf(C, Arg);
if (V)
State = removeTaint(State, *V);
}
@@ -588,8 +595,8 @@ bool GenericTaintChecker::addFiltersPre(const CallExpr *CE,
return false;
}
-bool GenericTaintChecker::propagateFromPre(const CallExpr *CE,
- CheckerContext &C) const {
+bool GenericTaintChecker::propagateFromPre(const CallEvent &Call,
+ CheckerContext &C) {
ProgramStateRef State = C.getState();
// Depending on what was tainted at pre-visit, we determined a set of
@@ -602,16 +609,16 @@ bool GenericTaintChecker::propagateFromPre(const CallExpr *CE,
for (unsigned ArgNum : TaintArgs) {
// Special handling for the tainted return value.
if (ArgNum == ReturnValueIndex) {
- State = addTaint(State, CE, C.getLocationContext());
+ State = addTaint(State, Call.getReturnValue());
continue;
}
// The arguments are pointer arguments. The data they are pointing at is
// tainted after the call.
- if (CE->getNumArgs() < (ArgNum + 1))
+ if (Call.getNumArgs() < (ArgNum + 1))
return false;
- const Expr *Arg = CE->getArg(ArgNum);
- Optional<SVal> V = getPointedToSVal(C, Arg);
+ const Expr *Arg = Call.getArgExpr(ArgNum);
+ Optional<SVal> V = getPointeeOf(C, Arg);
if (V)
State = addTaint(State, *V);
}
@@ -626,27 +633,23 @@ bool GenericTaintChecker::propagateFromPre(const CallExpr *CE,
return false;
}
-bool GenericTaintChecker::checkPre(const CallExpr *CE,
+bool GenericTaintChecker::checkPre(const CallEvent &Call,
const FunctionData &FData,
CheckerContext &C) const {
-
- if (checkUncontrolledFormatString(CE, C))
- return true;
-
- if (checkSystemCall(CE, FData.Name, C))
+ if (checkUncontrolledFormatString(Call, C))
return true;
- if (checkTaintedBufferSize(CE, FData.FDecl, C))
+ if (checkSystemCall(Call, FData.Name, C))
return true;
- if (checkCustomSinks(CE, FData, C))
+ if (checkTaintedBufferSize(Call, C))
return true;
- return false;
+ return checkCustomSinks(Call, FData, C);
}
-Optional<SVal> GenericTaintChecker::getPointedToSVal(CheckerContext &C,
- const Expr *Arg) {
+Optional<SVal> GenericTaintChecker::getPointeeOf(CheckerContext &C,
+ const Expr *Arg) {
ProgramStateRef State = C.getState();
SVal AddrVal = C.getSVal(Arg->IgnoreParens());
if (AddrVal.isUnknownOrUndef())
@@ -671,31 +674,33 @@ Optional<SVal> GenericTaintChecker::getPointedToSVal(CheckerContext &C,
}
ProgramStateRef
-GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE,
+GenericTaintChecker::TaintPropagationRule::process(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
// Check for taint in arguments.
bool IsTainted = true;
for (unsigned ArgNum : SrcArgs) {
- if (ArgNum >= CE->getNumArgs())
+ if (ArgNum >= Call.getNumArgs())
continue;
- if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(ArgNum), State, C)))
+ if ((IsTainted =
+ isTaintedOrPointsToTainted(Call.getArgExpr(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 i = VariadicIndex; i < CE->getNumArgs(); ++i) {
- if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(i), State, C)))
+ for (unsigned i = VariadicIndex; i < Call.getNumArgs(); ++i) {
+ if ((IsTainted =
+ isTaintedOrPointsToTainted(Call.getArgExpr(i), State, C)))
break;
}
}
if (PropagationFunc)
- IsTainted = PropagationFunc(IsTainted, CE, C);
+ IsTainted = PropagationFunc(IsTainted, Call, C);
if (!IsTainted)
return State;
@@ -708,7 +713,7 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE,
continue;
}
- if (ArgNum >= CE->getNumArgs())
+ if (ArgNum >= Call.getNumArgs())
continue;
// Mark the given argument.
@@ -721,14 +726,15 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE,
// 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 i = VariadicIndex; i < CE->getNumArgs(); ++i) {
- const Expr *Arg = CE->getArg(i);
+ for (unsigned i = VariadicIndex; i < Call.getNumArgs(); ++i) {
+ const Expr *Arg = Call.getArgExpr(i);
// Process pointer argument.
const Type *ArgTy = Arg->getType().getTypePtr();
QualType PType = ArgTy->getPointeeType();
if ((!PType.isNull() && !PType.isConstQualified()) ||
- (ArgTy->isReferenceType() && !Arg->getType().isConstQualified()))
+ (ArgTy->isReferenceType() && !Arg->getType().isConstQualified())) {
State = State->add<TaintArgsOnPostVisit>(i);
+ }
}
}
@@ -736,16 +742,14 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE,
}
// If argument 0(protocol domain) is network, the return value should get taint.
-bool GenericTaintChecker::TaintPropagationRule::postSocket(bool /*IsTainted*/,
- const CallExpr *CE,
- CheckerContext &C) {
- SourceLocation DomLoc = CE->getArg(0)->getExprLoc();
+bool GenericTaintChecker::TaintPropagationRule::postSocket(
+ bool /*IsTainted*/, const CallEvent &Call, CheckerContext &C) {
+ SourceLocation DomLoc = Call.getArgExpr(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 false;
-
return true;
}
@@ -757,16 +761,15 @@ bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) {
const MemRegion *MemReg = Val.getAsRegion();
// The region should be symbolic, we do not know it's value.
- const SymbolicRegion *SymReg = dyn_cast_or_null<SymbolicRegion>(MemReg);
+ const auto *SymReg = dyn_cast_or_null<SymbolicRegion>(MemReg);
if (!SymReg)
return false;
// Get it's symbol and find the declaration region it's pointing to.
- const SymbolRegionValue *Sm =
- dyn_cast<SymbolRegionValue>(SymReg->getSymbol());
+ const auto *Sm = dyn_cast<SymbolRegionValue>(SymReg->getSymbol());
if (!Sm)
return false;
- const DeclRegion *DeclReg = dyn_cast_or_null<DeclRegion>(Sm->getRegion());
+ const auto *DeclReg = dyn_cast_or_null<DeclRegion>(Sm->getRegion());
if (!DeclReg)
return false;
@@ -784,23 +787,24 @@ bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) {
return false;
}
-static bool getPrintfFormatArgumentNum(const CallExpr *CE,
+static bool getPrintfFormatArgumentNum(const CallEvent &Call,
const CheckerContext &C,
unsigned &ArgNum) {
// Find if the function contains a format string argument.
// Handles: fprintf, printf, sprintf, snprintf, vfprintf, vprintf, vsprintf,
// vsnprintf, syslog, custom annotated functions.
- const FunctionDecl *FDecl = C.getCalleeDecl(CE);
+ const FunctionDecl *FDecl = Call.getDecl()->getAsFunction();
if (!FDecl)
return false;
for (const auto *Format : FDecl->specific_attrs<FormatAttr>()) {
ArgNum = Format->getFormatIdx() - 1;
- if ((Format->getType()->getName() == "printf") && CE->getNumArgs() > ArgNum)
+ if ((Format->getType()->getName() == "printf") &&
+ Call.getNumArgs() > ArgNum)
return true;
}
// Or if a function is named setproctitle (this is a heuristic).
- if (C.getCalleeName(CE).find("setproctitle") != StringRef::npos) {
+ if (C.getCalleeName(FDecl).find("setproctitle") != StringRef::npos) {
ArgNum = 0;
return true;
}
@@ -814,7 +818,7 @@ bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg,
// Check for taint.
ProgramStateRef State = C.getState();
- Optional<SVal> PointedToSVal = getPointedToSVal(C, E);
+ Optional<SVal> PointedToSVal = getPointeeOf(C, E);
SVal TaintedSVal;
if (PointedToSVal && isTainted(State, *PointedToSVal))
TaintedSVal = *PointedToSVal;
@@ -836,19 +840,19 @@ bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg,
}
bool GenericTaintChecker::checkUncontrolledFormatString(
- const CallExpr *CE, CheckerContext &C) const {
+ const CallEvent &Call, CheckerContext &C) const {
// Check if the function contains a format string argument.
unsigned ArgNum = 0;
- if (!getPrintfFormatArgumentNum(CE, C, ArgNum))
+ if (!getPrintfFormatArgumentNum(Call, C, ArgNum))
return false;
// If either the format string content or the pointer itself are tainted,
// warn.
- return generateReportIfTainted(CE->getArg(ArgNum),
+ return generateReportIfTainted(Call.getArgExpr(ArgNum),
MsgUncontrolledFormatString, C);
}
-bool GenericTaintChecker::checkSystemCall(const CallExpr *CE, StringRef Name,
+bool GenericTaintChecker::checkSystemCall(const CallEvent &Call, StringRef Name,
CheckerContext &C) const {
// TODO: It might make sense to run this check on demand. In some cases,
// we should check if the environment has been cleansed here. We also might
@@ -866,21 +870,22 @@ bool GenericTaintChecker::checkSystemCall(const CallExpr *CE, StringRef Name,
.Case("dlopen", 0)
.Default(InvalidArgIndex);
- if (ArgNum == InvalidArgIndex || CE->getNumArgs() < (ArgNum + 1))
+ if (ArgNum == InvalidArgIndex || Call.getNumArgs() < (ArgNum + 1))
return false;
- return generateReportIfTainted(CE->getArg(ArgNum), MsgSanitizeSystemArgs, C);
+ return generateReportIfTainted(Call.getArgExpr(ArgNum), MsgSanitizeSystemArgs,
+ C);
}
// TODO: Should this check be a part of the CString checker?
// If yes, should taint be a global setting?
-bool GenericTaintChecker::checkTaintedBufferSize(const CallExpr *CE,
- const FunctionDecl *FDecl,
+bool GenericTaintChecker::checkTaintedBufferSize(const CallEvent &Call,
CheckerContext &C) const {
+ const auto *FDecl = Call.getDecl()->getAsFunction();
// If the function has a buffer size argument, set ArgNum.
unsigned ArgNum = InvalidArgIndex;
unsigned BId = 0;
- if ((BId = FDecl->getMemoryFunctionKind()))
+ if ((BId = FDecl->getMemoryFunctionKind())) {
switch (BId) {
case Builtin::BImemcpy:
case Builtin::BImemmove:
@@ -892,26 +897,29 @@ bool GenericTaintChecker::checkTaintedBufferSize(const CallExpr *CE,
break;
default:
break;
- };
+ }
+ }
if (ArgNum == InvalidArgIndex) {
- if (C.isCLibraryFunction(FDecl, "malloc") ||
- C.isCLibraryFunction(FDecl, "calloc") ||
- C.isCLibraryFunction(FDecl, "alloca"))
+ using CCtx = CheckerContext;
+ if (CCtx::isCLibraryFunction(FDecl, "malloc") ||
+ CCtx::isCLibraryFunction(FDecl, "calloc") ||
+ CCtx::isCLibraryFunction(FDecl, "alloca"))
ArgNum = 0;
- else if (C.isCLibraryFunction(FDecl, "memccpy"))
+ else if (CCtx::isCLibraryFunction(FDecl, "memccpy"))
ArgNum = 3;
- else if (C.isCLibraryFunction(FDecl, "realloc"))
+ else if (CCtx::isCLibraryFunction(FDecl, "realloc"))
ArgNum = 1;
- else if (C.isCLibraryFunction(FDecl, "bcopy"))
+ else if (CCtx::isCLibraryFunction(FDecl, "bcopy"))
ArgNum = 2;
}
- return ArgNum != InvalidArgIndex && CE->getNumArgs() > ArgNum &&
- generateReportIfTainted(CE->getArg(ArgNum), MsgTaintedBufferSize, C);
+ return ArgNum != InvalidArgIndex && Call.getNumArgs() > ArgNum &&
+ generateReportIfTainted(Call.getArgExpr(ArgNum), MsgTaintedBufferSize,
+ C);
}
-bool GenericTaintChecker::checkCustomSinks(const CallExpr *CE,
+bool GenericTaintChecker::checkCustomSinks(const CallEvent &Call,
const FunctionData &FData,
CheckerContext &C) const {
auto It = findFunctionInConfig(CustomSinks, FData);
@@ -921,10 +929,10 @@ bool GenericTaintChecker::checkCustomSinks(const CallExpr *CE,
const auto &Value = It->second;
const GenericTaintChecker::ArgVector &Args = Value.second;
for (unsigned ArgNum : Args) {
- if (ArgNum >= CE->getNumArgs())
+ if (ArgNum >= Call.getNumArgs())
continue;
- if (generateReportIfTainted(CE->getArg(ArgNum), MsgCustomSink, C))
+ if (generateReportIfTainted(Call.getArgExpr(ArgNum), MsgCustomSink, C))
return true;
}
@@ -942,6 +950,6 @@ void ento::registerGenericTaintChecker(CheckerManager &Mgr) {
Checker->parseConfiguration(Mgr, Option, std::move(Config.getValue()));
}
-bool ento::shouldRegisterGenericTaintChecker(const LangOptions &LO) {
+bool ento::shouldRegisterGenericTaintChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp
index cc2cfb774227..1cf81b54e77d 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp
@@ -351,6 +351,8 @@ static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
case Stmt::CallExprClass:
case Stmt::ArraySubscriptExprClass:
case Stmt::OMPArraySectionExprClass:
+ case Stmt::OMPArrayShapingExprClass:
+ case Stmt::OMPIteratorExprClass:
case Stmt::ImplicitCastExprClass:
case Stmt::ParenExprClass:
case Stmt::BreakStmtClass:
@@ -513,6 +515,6 @@ void ento::registerIdenticalExprChecker(CheckerManager &Mgr) {
Mgr.registerChecker<FindIdenticalExprChecker>();
}
-bool ento::shouldRegisterIdenticalExprChecker(const LangOptions &LO) {
+bool ento::shouldRegisterIdenticalExprChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
index dd89c53478e8..65e52e139ee4 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -307,6 +307,6 @@ void ento::registerInnerPointerChecker(CheckerManager &Mgr) {
Mgr.registerChecker<InnerPointerChecker>();
}
-bool ento::shouldRegisterInnerPointerChecker(const LangOptions &LO) {
+bool ento::shouldRegisterInnerPointerChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
index 9642588d6a41..99731d6044a0 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
@@ -11,13 +11,19 @@
#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_INTERCHECKERAPI_H
#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_INTERCHECKERAPI_H
-namespace clang {
-class CheckerManager;
+// FIXME: This file goes against how a checker should be implemented either in
+// a single file, or be exposed in a header file. Let's try to get rid of it!
+
+namespace clang {
namespace ento {
+class CheckerManager;
+
/// Register the part of MallocChecker connected to InnerPointerChecker.
void registerInnerPointerCheckerAux(CheckerManager &Mgr);
-}}
+} // namespace ento
+} // namespace clang
+
#endif /* INTERCHECKERAPI_H_ */
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp
index d1a9a7df071d..6955ba11a28f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp
@@ -26,7 +26,10 @@ using namespace iterator;
namespace {
class InvalidatedIteratorChecker
- : public Checker<check::PreCall> {
+ : public Checker<check::PreCall, check::PreStmt<UnaryOperator>,
+ check::PreStmt<BinaryOperator>,
+ check::PreStmt<ArraySubscriptExpr>,
+ check::PreStmt<MemberExpr>> {
std::unique_ptr<BugType> InvalidatedBugType;
@@ -37,6 +40,10 @@ public:
InvalidatedIteratorChecker();
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPreStmt(const UnaryOperator *UO, CheckerContext &C) const;
+ void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const;
+ void checkPreStmt(const ArraySubscriptExpr *ASE, CheckerContext &C) const;
+ void checkPreStmt(const MemberExpr *ME, CheckerContext &C) const;
};
@@ -65,6 +72,48 @@ void InvalidatedIteratorChecker::checkPreCall(const CallEvent &Call,
}
}
+void InvalidatedIteratorChecker::checkPreStmt(const UnaryOperator *UO,
+ CheckerContext &C) const {
+ if (isa<CXXThisExpr>(UO->getSubExpr()))
+ return;
+
+ ProgramStateRef State = C.getState();
+ UnaryOperatorKind OK = UO->getOpcode();
+ SVal SubVal = State->getSVal(UO->getSubExpr(), C.getLocationContext());
+
+ if (isAccessOperator(OK)) {
+ verifyAccess(C, SubVal);
+ }
+}
+
+void InvalidatedIteratorChecker::checkPreStmt(const BinaryOperator *BO,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ BinaryOperatorKind OK = BO->getOpcode();
+ SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext());
+
+ if (isAccessOperator(OK)) {
+ verifyAccess(C, LVal);
+ }
+}
+
+void InvalidatedIteratorChecker::checkPreStmt(const ArraySubscriptExpr *ASE,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SVal LVal = State->getSVal(ASE->getLHS(), C.getLocationContext());
+ verifyAccess(C, LVal);
+}
+
+void InvalidatedIteratorChecker::checkPreStmt(const MemberExpr *ME,
+ CheckerContext &C) const {
+ if (!ME->isArrow() || ME->isImplicitAccess())
+ return;
+
+ ProgramStateRef State = C.getState();
+ SVal BaseVal = State->getSVal(ME->getBase(), C.getLocationContext());
+ verifyAccess(C, BaseVal);
+}
+
void InvalidatedIteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const {
auto State = C.getState();
const auto *Pos = getIteratorPosition(State, Val);
@@ -90,6 +139,6 @@ void ento::registerInvalidatedIteratorChecker(CheckerManager &mgr) {
mgr.registerChecker<InvalidatedIteratorChecker>();
}
-bool ento::shouldRegisterInvalidatedIteratorChecker(const LangOptions &LO) {
+bool ento::shouldRegisterInvalidatedIteratorChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
index 6bca5515724c..ac0f24603dd9 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
@@ -128,24 +128,54 @@ bool isAccessOperator(OverloadedOperatorKind OK) {
isDecrementOperator(OK) || isRandomIncrOrDecrOperator(OK);
}
+bool isAccessOperator(UnaryOperatorKind OK) {
+ return isDereferenceOperator(OK) || isIncrementOperator(OK) ||
+ isDecrementOperator(OK);
+}
+
+bool isAccessOperator(BinaryOperatorKind OK) {
+ return isDereferenceOperator(OK) || isRandomIncrOrDecrOperator(OK);
+}
+
bool isDereferenceOperator(OverloadedOperatorKind OK) {
return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
OK == OO_Subscript;
}
+bool isDereferenceOperator(UnaryOperatorKind OK) {
+ return OK == UO_Deref;
+}
+
+bool isDereferenceOperator(BinaryOperatorKind OK) {
+ return OK == BO_PtrMemI;
+}
+
bool isIncrementOperator(OverloadedOperatorKind OK) {
return OK == OO_PlusPlus;
}
+bool isIncrementOperator(UnaryOperatorKind OK) {
+ return OK == UO_PreInc || OK == UO_PostInc;
+}
+
bool isDecrementOperator(OverloadedOperatorKind OK) {
return OK == OO_MinusMinus;
}
+bool isDecrementOperator(UnaryOperatorKind OK) {
+ return OK == UO_PreDec || OK == UO_PostDec;
+}
+
bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
OK == OO_MinusEqual;
}
+bool isRandomIncrOrDecrOperator(BinaryOperatorKind OK) {
+ return OK == BO_Add || OK == BO_AddAssign ||
+ OK == BO_Sub || OK == BO_SubAssign;
+}
+
const ContainerData *getContainerData(ProgramStateRef State,
const MemRegion *Cont) {
return State->get<ContainerMap>(Cont);
@@ -177,6 +207,20 @@ ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
return nullptr;
}
+ProgramStateRef createIteratorPosition(ProgramStateRef State, const SVal &Val,
+ const MemRegion *Cont, const Stmt* S,
+ const LocationContext *LCtx,
+ unsigned blockCount) {
+ auto &StateMgr = State->getStateManager();
+ auto &SymMgr = StateMgr.getSymbolManager();
+ auto &ACtx = StateMgr.getContext();
+
+ auto Sym = SymMgr.conjureSymbol(S, LCtx, ACtx.LongTy, blockCount);
+ State = assumeNoOverflow(State, Sym, 4);
+ return setIteratorPosition(State, Val,
+ IteratorPosition::getPosition(Cont, Sym));
+}
+
ProgramStateRef advancePosition(ProgramStateRef State, const SVal &Iter,
OverloadedOperatorKind Op,
const SVal &Distance) {
@@ -186,22 +230,70 @@ ProgramStateRef advancePosition(ProgramStateRef State, const SVal &Iter,
auto &SymMgr = State->getStateManager().getSymbolManager();
auto &SVB = State->getStateManager().getSValBuilder();
+ auto &BVF = State->getStateManager().getBasicVals();
assert ((Op == OO_Plus || Op == OO_PlusEqual ||
Op == OO_Minus || Op == OO_MinusEqual) &&
"Advance operator must be one of +, -, += and -=.");
auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
- if (const auto IntDist = Distance.getAs<nonloc::ConcreteInt>()) {
- // For concrete integers we can calculate the new position
- const auto NewPos =
- Pos->setTo(SVB.evalBinOp(State, BinOp,
- nonloc::SymbolVal(Pos->getOffset()),
- *IntDist, SymMgr.getType(Pos->getOffset()))
- .getAsSymbol());
- return setIteratorPosition(State, Iter, NewPos);
+ const auto IntDistOp = Distance.getAs<nonloc::ConcreteInt>();
+ if (!IntDistOp)
+ return nullptr;
+
+ // For concrete integers we can calculate the new position
+ nonloc::ConcreteInt IntDist = *IntDistOp;
+
+ if (IntDist.getValue().isNegative()) {
+ IntDist = nonloc::ConcreteInt(BVF.getValue(-IntDist.getValue()));
+ BinOp = (BinOp == BO_Add) ? BO_Sub : BO_Add;
}
+ const auto NewPos =
+ Pos->setTo(SVB.evalBinOp(State, BinOp,
+ nonloc::SymbolVal(Pos->getOffset()),
+ IntDist, SymMgr.getType(Pos->getOffset()))
+ .getAsSymbol());
+ return setIteratorPosition(State, Iter, NewPos);
+}
- return nullptr;
+// 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;
}
bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.h
index c10d86691693..37157492fe3e 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.h
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.h
@@ -115,9 +115,12 @@ class IteratorSymbolMap {};
class IteratorRegionMap {};
class ContainerMap {};
-using IteratorSymbolMapTy = CLANG_ENTO_PROGRAMSTATE_MAP(SymbolRef, IteratorPosition);
-using IteratorRegionMapTy = CLANG_ENTO_PROGRAMSTATE_MAP(const MemRegion *, IteratorPosition);
-using ContainerMapTy = CLANG_ENTO_PROGRAMSTATE_MAP(const MemRegion *, ContainerData);
+using IteratorSymbolMapTy =
+ CLANG_ENTO_PROGRAMSTATE_MAP(SymbolRef, IteratorPosition);
+using IteratorRegionMapTy =
+ CLANG_ENTO_PROGRAMSTATE_MAP(const MemRegion *, IteratorPosition);
+using ContainerMapTy =
+ CLANG_ENTO_PROGRAMSTATE_MAP(const MemRegion *, ContainerData);
} // namespace iterator
@@ -149,20 +152,33 @@ bool isEraseCall(const FunctionDecl *Func);
bool isEraseAfterCall(const FunctionDecl *Func);
bool isEmplaceCall(const FunctionDecl *Func);
bool isAccessOperator(OverloadedOperatorKind OK);
+bool isAccessOperator(UnaryOperatorKind OK);
+bool isAccessOperator(BinaryOperatorKind OK);
bool isDereferenceOperator(OverloadedOperatorKind OK);
+bool isDereferenceOperator(UnaryOperatorKind OK);
+bool isDereferenceOperator(BinaryOperatorKind OK);
bool isIncrementOperator(OverloadedOperatorKind OK);
+bool isIncrementOperator(UnaryOperatorKind OK);
bool isDecrementOperator(OverloadedOperatorKind OK);
+bool isDecrementOperator(UnaryOperatorKind OK);
bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK);
+bool isRandomIncrOrDecrOperator(BinaryOperatorKind OK);
const ContainerData *getContainerData(ProgramStateRef State,
const MemRegion *Cont);
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
const SVal &Val);
ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
const IteratorPosition &Pos);
+ProgramStateRef createIteratorPosition(ProgramStateRef State, const SVal &Val,
+ const MemRegion *Cont, const Stmt* S,
+ const LocationContext *LCtx,
+ unsigned blockCount);
ProgramStateRef advancePosition(ProgramStateRef State,
const SVal &Iter,
OverloadedOperatorKind Op,
const SVal &Distance);
+ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
+ long Scale);
bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
BinaryOperator::Opcode Opc);
bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
index eb962a2ffd9e..fd8cbd694b24 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
@@ -6,8 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Defines a checker for using iterators outside their range (past end). Usage
-// means here dereferencing, incrementing etc.
+// Defines a modeling-checker for modeling STL iterator-like iterators.
//
//===----------------------------------------------------------------------===//
//
@@ -84,9 +83,20 @@ using namespace iterator;
namespace {
class IteratorModeling
- : public Checker<check::PostCall, check::PostStmt<MaterializeTemporaryExpr>,
+ : public Checker<check::PostCall, check::PostStmt<UnaryOperator>,
+ check::PostStmt<BinaryOperator>,
+ check::PostStmt<MaterializeTemporaryExpr>,
check::Bind, check::LiveSymbols, check::DeadSymbols> {
+ using AdvanceFn = void (IteratorModeling::*)(CheckerContext &, const Expr *,
+ SVal, SVal, SVal) const;
+
+ void handleOverloadedOperator(CheckerContext &C, const CallEvent &Call,
+ OverloadedOperatorKind Op) const;
+ void handleAdvanceLikeFunction(CheckerContext &C, const CallEvent &Call,
+ const Expr *OrigExpr,
+ const AdvanceFn *Handler) const;
+
void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal,
const SVal &LVal, const SVal &RVal,
OverloadedOperatorKind Op) const;
@@ -100,35 +110,46 @@ class IteratorModeling
void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
OverloadedOperatorKind Op, const SVal &RetVal,
const SVal &LHS, const SVal &RHS) const;
- void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal,
- const SVal &Cont) const;
- void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
- const SVal &Cont) const;
+ void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator,
+ OverloadedOperatorKind OK, SVal Offset) const;
+ void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
+ SVal Amount) const;
+ void handlePrev(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
+ SVal Amount) const;
+ void handleNext(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
+ SVal Amount) const;
void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
const MemRegion *Cont) const;
- void handleAssign(CheckerContext &C, const SVal &Cont,
- const Expr *CE = nullptr,
- const SVal &OldCont = UndefinedVal()) const;
- void handleClear(CheckerContext &C, const SVal &Cont) const;
- void handlePushBack(CheckerContext &C, const SVal &Cont) const;
- void handlePopBack(CheckerContext &C, const SVal &Cont) const;
- void handlePushFront(CheckerContext &C, const SVal &Cont) const;
- void handlePopFront(CheckerContext &C, const SVal &Cont) const;
- void handleInsert(CheckerContext &C, const SVal &Iter) const;
- void handleErase(CheckerContext &C, const SVal &Iter) const;
- void handleErase(CheckerContext &C, const SVal &Iter1,
- const SVal &Iter2) const;
- void handleEraseAfter(CheckerContext &C, const SVal &Iter) const;
- void handleEraseAfter(CheckerContext &C, const SVal &Iter1,
- const SVal &Iter2) const;
+ bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const;
void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
const char *Sep) const override;
+ // std::advance, std::prev & std::next
+ CallDescriptionMap<AdvanceFn> AdvanceLikeFunctions = {
+ // template<class InputIt, class Distance>
+ // void advance(InputIt& it, Distance n);
+ {{{"std", "advance"}, 2}, &IteratorModeling::handleAdvance},
+
+ // template<class BidirIt>
+ // BidirIt prev(
+ // BidirIt it,
+ // typename std::iterator_traits<BidirIt>::difference_type n = 1);
+ {{{"std", "prev"}, 2}, &IteratorModeling::handlePrev},
+
+ // template<class ForwardIt>
+ // ForwardIt next(
+ // ForwardIt it,
+ // typename std::iterator_traits<ForwardIt>::difference_type n = 1);
+ {{{"std", "next"}, 2}, &IteratorModeling::handleNext},
+ };
+
public:
- IteratorModeling() {}
+ IteratorModeling() = default;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
+ void checkPostStmt(const UnaryOperator *UO, CheckerContext &C) const;
+ void checkPostStmt(const BinaryOperator *BO, CheckerContext &C) const;
void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
void checkPostStmt(const MaterializeTemporaryExpr *MTE,
@@ -137,68 +158,14 @@ public:
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
};
-bool isBeginCall(const FunctionDecl *Func);
-bool isEndCall(const FunctionDecl *Func);
-bool isAssignCall(const FunctionDecl *Func);
-bool isClearCall(const FunctionDecl *Func);
-bool isPushBackCall(const FunctionDecl *Func);
-bool isEmplaceBackCall(const FunctionDecl *Func);
-bool isPopBackCall(const FunctionDecl *Func);
-bool isPushFrontCall(const FunctionDecl *Func);
-bool isEmplaceFrontCall(const FunctionDecl *Func);
-bool isPopFrontCall(const FunctionDecl *Func);
-bool isAssignmentOperator(OverloadedOperatorKind OK);
bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
-bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);
-bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);
-bool backModifiable(ProgramStateRef State, const MemRegion *Reg);
-SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
-SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
-ProgramStateRef createContainerBegin(ProgramStateRef State,
- const MemRegion *Cont, const Expr *E,
- QualType T, const LocationContext *LCtx,
- unsigned BlockCount);
-ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
- const Expr *E, QualType T,
- const LocationContext *LCtx,
- unsigned BlockCount);
-ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
- const ContainerData &CData);
+bool isSimpleComparisonOperator(BinaryOperatorKind OK);
ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
-ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
- long Scale);
-ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
- const MemRegion *Cont);
-ProgramStateRef
-invalidateAllIteratorPositionsExcept(ProgramStateRef State,
- const MemRegion *Cont, SymbolRef Offset,
- BinaryOperator::Opcode Opc);
-ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
- SymbolRef Offset,
- BinaryOperator::Opcode Opc);
-ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
- SymbolRef Offset1,
- BinaryOperator::Opcode Opc1,
- SymbolRef Offset2,
- BinaryOperator::Opcode Opc2);
-ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
- const MemRegion *Cont,
- const MemRegion *NewCont);
-ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
- const MemRegion *Cont,
- const MemRegion *NewCont,
- SymbolRef Offset,
- BinaryOperator::Opcode Opc);
-ProgramStateRef rebaseSymbolInIteratorPositionsIf(
- ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
- SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
SymbolRef Sym2, bool Equal);
-SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
- SymbolRef OldSym, SymbolRef NewSym);
-bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
bool isBoundThroughLazyCompoundVal(const Environment &Env,
const MemRegion *Reg);
+const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call);
} // namespace
@@ -211,189 +178,57 @@ void IteratorModeling::checkPostCall(const CallEvent &Call,
if (Func->isOverloadedOperator()) {
const auto Op = Func->getOverloadedOperator();
- if (isAssignmentOperator(Op)) {
- // Overloaded 'operator=' must be a non-static member function.
- const auto *InstCall = cast<CXXInstanceCall>(&Call);
- if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
- handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
- Call.getArgSVal(0));
- 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, 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())) {
- const auto *OrigExpr = Call.getOriginExpr();
- if (!OrigExpr)
- return;
-
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- if (Call.getNumArgs() >= 1 &&
- Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
- handleRandomIncrOrDecr(C, OrigExpr, Func->getOverloadedOperator(),
- Call.getReturnValue(),
- InstCall->getCXXThisVal(), Call.getArgSVal(0));
- return;
- }
- } else {
- if (Call.getNumArgs() >= 2 &&
- Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
- handleRandomIncrOrDecr(C, OrigExpr, 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());
- 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());
- 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());
- return;
- }
-
- if (isClearCall(Func)) {
- handleClear(C, InstCall->getCXXThisVal());
- return;
- }
-
- if (isPushBackCall(Func) || isEmplaceBackCall(Func)) {
- handlePushBack(C, InstCall->getCXXThisVal());
- return;
- }
-
- if (isPopBackCall(Func)) {
- handlePopBack(C, InstCall->getCXXThisVal());
- return;
- }
-
- if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) {
- handlePushFront(C, InstCall->getCXXThisVal());
- return;
- }
+ handleOverloadedOperator(C, Call, Op);
+ return;
+ }
- if (isPopFrontCall(Func)) {
- handlePopFront(C, InstCall->getCXXThisVal());
- return;
- }
+ const auto *OrigExpr = Call.getOriginExpr();
+ if (!OrigExpr)
+ return;
- if (isInsertCall(Func) || isEmplaceCall(Func)) {
- handleInsert(C, Call.getArgSVal(0));
- return;
- }
+ const AdvanceFn *Handler = AdvanceLikeFunctions.lookup(Call);
+ if (Handler) {
+ handleAdvanceLikeFunction(C, Call, OrigExpr, Handler);
+ return;
+ }
- if (isEraseCall(Func)) {
- if (Call.getNumArgs() == 1) {
- handleErase(C, Call.getArgSVal(0));
- return;
- }
+ if (!isIteratorType(Call.getResultType()))
+ return;
- if (Call.getNumArgs() == 2) {
- handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1));
- return;
- }
- }
+ auto State = C.getState();
- if (isEraseAfterCall(Func)) {
- if (Call.getNumArgs() == 1) {
- handleEraseAfter(C, Call.getArgSVal(0));
- return;
- }
+ // Already bound to container?
+ if (getIteratorPosition(State, Call.getReturnValue()))
+ return;
- if (Call.getNumArgs() == 2) {
- handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1));
- return;
- }
+ // Copy-like and move constructors
+ if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
+ if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
+ State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
+ if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
+ State = removeIteratorPosition(State, Call.getArgSVal(0));
}
- }
-
- const auto *OrigExpr = Call.getOriginExpr();
- if (!OrigExpr)
- return;
-
- if (!isIteratorType(Call.getResultType()))
+ C.addTransition(State);
return;
-
- auto State = C.getState();
-
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- if (isBeginCall(Func)) {
- handleBegin(C, OrigExpr, Call.getReturnValue(),
- InstCall->getCXXThisVal());
- return;
- }
-
- if (isEndCall(Func)) {
- handleEnd(C, OrigExpr, Call.getReturnValue(),
- InstCall->getCXXThisVal());
- return;
- }
}
+ }
- // Already bound to container?
- if (getIteratorPosition(State, Call.getReturnValue()))
- return;
-
- // Copy-like and move constructors
- if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
- if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
- State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
- if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
- State = removeIteratorPosition(State, Call.getArgSVal(0));
- }
- C.addTransition(State);
+ // Assumption: if return value is an iterator which is not yet bound to a
+ // container, then look for the first iterator argument of the
+ // same type as the return value and bind the return value to
+ // the same container. This approach works for STL algorithms.
+ // FIXME: Add a more conservative mode
+ for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
+ if (isIteratorType(Call.getArgExpr(i)->getType()) &&
+ Call.getArgExpr(i)->getType().getNonReferenceType().getDesugaredType(
+ C.getASTContext()).getTypePtr() ==
+ Call.getResultType().getDesugaredType(C.getASTContext()).getTypePtr()) {
+ if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
+ assignToContainer(C, OrigExpr, Call.getReturnValue(),
+ Pos->getContainer());
return;
}
}
-
- // Assumption: if return value is an iterator which is not yet bound to a
- // container, then look for the first iterator argument, and
- // bind the return value to the same container. This approach
- // works for STL algorithms.
- // FIXME: Add a more conservative mode
- for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
- if (isIteratorType(Call.getArgExpr(i)->getType())) {
- if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
- assignToContainer(C, OrigExpr, Call.getReturnValue(),
- Pos->getContainer());
- return;
- }
- }
- }
}
}
@@ -413,6 +248,35 @@ void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S,
}
}
+void IteratorModeling::checkPostStmt(const UnaryOperator *UO,
+ CheckerContext &C) const {
+ UnaryOperatorKind OK = UO->getOpcode();
+ if (!isIncrementOperator(OK) && !isDecrementOperator(OK))
+ return;
+
+ auto &SVB = C.getSValBuilder();
+ handlePtrIncrOrDecr(C, UO->getSubExpr(),
+ isIncrementOperator(OK) ? OO_Plus : OO_Minus,
+ SVB.makeArrayIndex(1));
+}
+
+void IteratorModeling::checkPostStmt(const BinaryOperator *BO,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ BinaryOperatorKind OK = BO->getOpcode();
+ SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext());
+
+ if (isSimpleComparisonOperator(BO->getOpcode())) {
+ SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext());
+ SVal Result = State->getSVal(BO, C.getLocationContext());
+ handleComparison(C, BO, Result, LVal, RVal,
+ BinaryOperator::getOverloadedOperator(OK));
+ } else if (isRandomIncrOrDecrOperator(OK)) {
+ handlePtrIncrOrDecr(C, BO->getLHS(),
+ BinaryOperator::getOverloadedOperator(OK), RVal);
+ }
+}
+
void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE,
CheckerContext &C) const {
/* Transfer iterator state to temporary objects */
@@ -426,8 +290,7 @@ void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE,
void IteratorModeling::checkLiveSymbols(ProgramStateRef State,
SymbolReaper &SR) const {
- // Keep symbolic expressions of iterator positions, container begins and ends
- // alive
+ // Keep symbolic expressions of iterator positions alive
auto RegionMap = State->get<IteratorRegionMap>();
for (const auto &Reg : RegionMap) {
const auto Offset = Reg.second.getOffset();
@@ -444,20 +307,6 @@ void IteratorModeling::checkLiveSymbols(ProgramStateRef State,
SR.markLive(*i);
}
- auto ContMap = State->get<ContainerMap>();
- for (const auto &Cont : ContMap) {
- const auto CData = Cont.second;
- if (CData.getBegin()) {
- SR.markLive(CData.getBegin());
- if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
- SR.markLive(SIE->getLHS());
- }
- if (CData.getEnd()) {
- SR.markLive(CData.getEnd());
- if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
- SR.markLive(SIE->getLHS());
- }
- }
}
void IteratorModeling::checkDeadSymbols(SymbolReaper &SR,
@@ -484,18 +333,92 @@ void IteratorModeling::checkDeadSymbols(SymbolReaper &SR,
}
}
- auto ContMap = State->get<ContainerMap>();
- for (const auto &Cont : ContMap) {
- if (!SR.isLiveRegion(Cont.first)) {
- // We must keep the container data while it has live iterators to be able
- // to compare them to the begin and the end of the container.
- if (!hasLiveIterators(State, Cont.first)) {
- State = State->remove<ContainerMap>(Cont.first);
+ C.addTransition(State);
+}
+
+void
+IteratorModeling::handleOverloadedOperator(CheckerContext &C,
+ const CallEvent &Call,
+ OverloadedOperatorKind Op) const {
+ if (isSimpleComparisonOperator(Op)) {
+ const auto *OrigExpr = Call.getOriginExpr();
+ if (!OrigExpr)
+ return;
+
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ 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(Op)) {
+ const auto *OrigExpr = Call.getOriginExpr();
+ if (!OrigExpr)
+ return;
+
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ if (Call.getNumArgs() >= 1 &&
+ Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
+ handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
+ InstCall->getCXXThisVal(), Call.getArgSVal(0));
+ return;
+ }
+ } else {
+ if (Call.getNumArgs() >= 2 &&
+ Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
+ handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
+ Call.getArgSVal(0), Call.getArgSVal(1));
+ return;
+ }
+ }
+ } else if (isIncrementOperator(Op)) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
+ Call.getNumArgs());
+ return;
+ }
+
+ handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
+ Call.getNumArgs());
+ return;
+ } else if (isDecrementOperator(Op)) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
+ Call.getNumArgs());
+ return;
+ }
+
+ handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
+ Call.getNumArgs());
+ return;
}
+}
+
+void
+IteratorModeling::handleAdvanceLikeFunction(CheckerContext &C,
+ const CallEvent &Call,
+ const Expr *OrigExpr,
+ const AdvanceFn *Handler) const {
+ if (!C.wasInlined) {
+ (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
+ Call.getArgSVal(0), Call.getArgSVal(1));
+ return;
}
- C.addTransition(State);
+ // If std::advance() was inlined, but a non-standard function it calls inside
+ // was not, then we have to model it explicitly
+ const auto *IdInfo = cast<FunctionDecl>(Call.getDecl())->getIdentifier();
+ if (IdInfo) {
+ if (IdInfo->getName() == "advance") {
+ if (noChangeInAdvance(C, Call.getArgSVal(0), OrigExpr)) {
+ (this->**Handler)(C, OrigExpr, Call.getReturnValue(),
+ Call.getArgSVal(0), Call.getArgSVal(1));
+ }
+ }
+ }
}
void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
@@ -518,7 +441,7 @@ void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
if (!Cont)
return;
- // At least one of the iterators have recorded positions. If one of them has
+ // At least one of the iterators has recorded positions. If one of them does
// not then create a new symbol for the offset.
SymbolRef Sym;
if (!LPos || !RPos) {
@@ -538,7 +461,7 @@ void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
RPos = getIteratorPosition(State, RVal);
}
- // We cannot make assumpotions on `UnknownVal`. Let us conjure a symbol
+ // We cannot make assumptions on `UnknownVal`. Let us conjure a symbol
// instead.
if (RetVal.isUnknown()) {
auto &SymMgr = C.getSymbolManager();
@@ -574,7 +497,7 @@ void IteratorModeling::processComparison(CheckerContext &C,
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);
@@ -648,481 +571,139 @@ void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C,
return;
const auto *value = &RHS;
+ SVal val;
if (auto loc = RHS.getAs<Loc>()) {
- const auto val = State->getRawSVal(*loc);
+ val = State->getRawSVal(*loc);
value = &val;
}
auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
- auto NewState =
- advancePosition(State, LHS, Op, *value);
- if (NewState) {
- const auto *NewPos = getIteratorPosition(NewState, LHS);
+ // `AdvancedState` is a state where the position of `LHS` is advanced. We
+ // only need this state to retrieve the new position, but we do not want
+ // to change the position of `LHS` (in every case).
+ auto AdvancedState = advancePosition(State, LHS, Op, *value);
+ if (AdvancedState) {
+ const auto *NewPos = getIteratorPosition(AdvancedState, LHS);
assert(NewPos &&
"Iterator should have position after successful advancement");
- State = setIteratorPosition(NewState, TgtVal, *NewPos);
+ State = setIteratorPosition(State, TgtVal, *NewPos);
C.addTransition(State);
} else {
assignToContainer(C, CE, TgtVal, Pos->getContainer());
}
}
-void IteratorModeling::handleBegin(CheckerContext &C, const Expr *CE,
- const SVal &RetVal, const SVal &Cont) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
-
- ContReg = ContReg->getMostDerivedObjectRegion();
-
- // If the container already has a begin symbol then use it. Otherwise first
- // create a new one.
- auto State = C.getState();
- auto BeginSym = getContainerBegin(State, ContReg);
- if (!BeginSym) {
- State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
- C.getLocationContext(), C.blockCount());
- BeginSym = getContainerBegin(State, ContReg);
- }
- State = setIteratorPosition(State, RetVal,
- IteratorPosition::getPosition(ContReg, BeginSym));
- C.addTransition(State);
-}
-
-void IteratorModeling::handleEnd(CheckerContext &C, const Expr *CE,
- const SVal &RetVal, const SVal &Cont) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
-
- ContReg = ContReg->getMostDerivedObjectRegion();
-
- // If the container already has an end symbol then use it. Otherwise first
- // create a new one.
- auto State = C.getState();
- auto EndSym = getContainerEnd(State, ContReg);
- if (!EndSym) {
- State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
- C.getLocationContext(), C.blockCount());
- EndSym = getContainerEnd(State, ContReg);
- }
- State = setIteratorPosition(State, RetVal,
- IteratorPosition::getPosition(ContReg, EndSym));
- C.addTransition(State);
-}
-
-void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE,
- const SVal &RetVal,
- const MemRegion *Cont) const {
- Cont = Cont->getMostDerivedObjectRegion();
-
- auto State = C.getState();
- auto &SymMgr = C.getSymbolManager();
- auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
- C.getASTContext().LongTy, C.blockCount());
- State = assumeNoOverflow(State, Sym, 4);
- State = setIteratorPosition(State, RetVal,
- IteratorPosition::getPosition(Cont, Sym));
- C.addTransition(State);
-}
-
-void IteratorModeling::handleAssign(CheckerContext &C, const SVal &Cont,
- const Expr *CE, const SVal &OldCont) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
-
- ContReg = ContReg->getMostDerivedObjectRegion();
-
- // Assignment of a new value to a container always invalidates all its
- // iterators
- auto State = C.getState();
- const auto CData = getContainerData(State, ContReg);
- if (CData) {
- State = invalidateAllIteratorPositions(State, ContReg);
- }
-
- // In case of move, iterators of the old container (except the past-end
- // iterators) remain valid but refer to the new container
- if (!OldCont.isUndef()) {
- const auto *OldContReg = OldCont.getAsRegion();
- if (OldContReg) {
- OldContReg = OldContReg->getMostDerivedObjectRegion();
- const auto OldCData = getContainerData(State, OldContReg);
- if (OldCData) {
- if (const auto OldEndSym = OldCData->getEnd()) {
- // If we already assigned an "end" symbol to the old container, then
- // first reassign all iterator positions to the new container which
- // are not past the container (thus not greater or equal to the
- // current "end" symbol).
- State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
- OldEndSym, BO_GE);
- auto &SymMgr = C.getSymbolManager();
- auto &SVB = C.getSValBuilder();
- // Then generate and assign a new "end" symbol for the new container.
- auto NewEndSym =
- SymMgr.conjureSymbol(CE, C.getLocationContext(),
- C.getASTContext().LongTy, C.blockCount());
- State = assumeNoOverflow(State, NewEndSym, 4);
- if (CData) {
- State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
- } else {
- State = setContainerData(State, ContReg,
- ContainerData::fromEnd(NewEndSym));
- }
- // Finally, replace the old "end" symbol in the already reassigned
- // iterator positions with the new "end" symbol.
- State = rebaseSymbolInIteratorPositionsIf(
- State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
- } else {
- // There was no "end" symbol assigned yet to the old container,
- // so reassign all iterator positions to the new container.
- State = reassignAllIteratorPositions(State, OldContReg, ContReg);
- }
- if (const auto OldBeginSym = OldCData->getBegin()) {
- // If we already assigned a "begin" symbol to the old container, then
- // assign it to the new container and remove it from the old one.
- if (CData) {
- State =
- setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
- } else {
- State = setContainerData(State, ContReg,
- ContainerData::fromBegin(OldBeginSym));
- }
- State =
- setContainerData(State, OldContReg, OldCData->newEnd(nullptr));
- }
- } else {
- // There was neither "begin" nor "end" symbol assigned yet to the old
- // container, so reassign all iterator positions to the new container.
- State = reassignAllIteratorPositions(State, OldContReg, ContReg);
- }
- }
- }
- C.addTransition(State);
-}
-
-void IteratorModeling::handleClear(CheckerContext &C, const SVal &Cont) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
-
- ContReg = ContReg->getMostDerivedObjectRegion();
-
- // The clear() operation invalidates all the iterators, except the past-end
- // iterators of list-like containers
- auto State = C.getState();
- if (!hasSubscriptOperator(State, ContReg) ||
- !backModifiable(State, ContReg)) {
- const auto CData = getContainerData(State, ContReg);
- if (CData) {
- if (const auto EndSym = CData->getEnd()) {
- State =
- invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
- C.addTransition(State);
- return;
- }
- }
- }
- State = invalidateAllIteratorPositions(State, ContReg);
- C.addTransition(State);
-}
-
-void IteratorModeling::handlePushBack(CheckerContext &C,
- const SVal &Cont) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
+void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C,
+ const Expr *Iterator,
+ OverloadedOperatorKind OK,
+ SVal Offset) const {
+ QualType PtrType = Iterator->getType();
+ if (!PtrType->isPointerType())
return;
+ QualType ElementType = PtrType->getPointeeType();
- ContReg = ContReg->getMostDerivedObjectRegion();
+ ProgramStateRef State = C.getState();
+ SVal OldVal = State->getSVal(Iterator, C.getLocationContext());
- // For deque-like containers invalidate all iterator positions
- auto State = C.getState();
- if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
- State = invalidateAllIteratorPositions(State, ContReg);
- C.addTransition(State);
+ const IteratorPosition *OldPos = getIteratorPosition(State, OldVal);
+ if (!OldPos)
return;
- }
- const auto CData = getContainerData(State, ContReg);
- if (!CData)
- return;
+ SVal NewVal;
+ if (OK == OO_Plus || OK == OO_PlusEqual)
+ NewVal = State->getLValue(ElementType, Offset, OldVal);
+ else {
+ const llvm::APSInt &OffsetInt =
+ Offset.castAs<nonloc::ConcreteInt>().getValue();
+ auto &BVF = C.getSymbolManager().getBasicVals();
+ SVal NegatedOffset = nonloc::ConcreteInt(BVF.getValue(-OffsetInt));
+ NewVal = State->getLValue(ElementType, NegatedOffset, OldVal);
+ }
+
+ // `AdvancedState` is a state where the position of `Old` is advanced. We
+ // only need this state to retrieve the new position, but we do not want
+ // ever to change the position of `OldVal`.
+ auto AdvancedState = advancePosition(State, OldVal, OK, Offset);
+ if (AdvancedState) {
+ const IteratorPosition *NewPos = getIteratorPosition(AdvancedState, OldVal);
+ assert(NewPos &&
+ "Iterator should have position after successful advancement");
- // For vector-like containers invalidate the past-end iterator positions
- if (const auto EndSym = CData->getEnd()) {
- if (hasSubscriptOperator(State, ContReg)) {
- State = invalidateIteratorPositions(State, EndSym, BO_GE);
- }
- auto &SymMgr = C.getSymbolManager();
- auto &BVF = SymMgr.getBasicVals();
- auto &SVB = C.getSValBuilder();
- const auto newEndSym =
- SVB.evalBinOp(State, BO_Add,
- nonloc::SymbolVal(EndSym),
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
- SymMgr.getType(EndSym)).getAsSymbol();
- State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
+ ProgramStateRef NewState = setIteratorPosition(State, NewVal, *NewPos);
+ C.addTransition(NewState);
+ } else {
+ assignToContainer(C, Iterator, NewVal, OldPos->getContainer());
}
- C.addTransition(State);
}
-void IteratorModeling::handlePopBack(CheckerContext &C,
- const SVal &Cont) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
-
- ContReg = ContReg->getMostDerivedObjectRegion();
-
- auto State = C.getState();
- const auto CData = getContainerData(State, ContReg);
- if (!CData)
- return;
-
- if (const auto EndSym = CData->getEnd()) {
- auto &SymMgr = C.getSymbolManager();
- auto &BVF = SymMgr.getBasicVals();
- auto &SVB = C.getSValBuilder();
- const auto BackSym =
- SVB.evalBinOp(State, BO_Sub,
- nonloc::SymbolVal(EndSym),
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
- SymMgr.getType(EndSym)).getAsSymbol();
- // For vector-like and deque-like containers invalidate the last and the
- // past-end iterator positions. For list-like containers only invalidate
- // the last position
- if (hasSubscriptOperator(State, ContReg) &&
- backModifiable(State, ContReg)) {
- State = invalidateIteratorPositions(State, BackSym, BO_GE);
- State = setContainerData(State, ContReg, CData->newEnd(nullptr));
- } else {
- State = invalidateIteratorPositions(State, BackSym, BO_EQ);
- }
- auto newEndSym = BackSym;
- State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
- C.addTransition(State);
- }
+void IteratorModeling::handleAdvance(CheckerContext &C, const Expr *CE,
+ SVal RetVal, SVal Iter,
+ SVal Amount) const {
+ handleRandomIncrOrDecr(C, CE, OO_PlusEqual, RetVal, Iter, Amount);
}
-void IteratorModeling::handlePushFront(CheckerContext &C,
- const SVal &Cont) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
-
- ContReg = ContReg->getMostDerivedObjectRegion();
-
- // For deque-like containers invalidate all iterator positions
- auto State = C.getState();
- if (hasSubscriptOperator(State, ContReg)) {
- State = invalidateAllIteratorPositions(State, ContReg);
- C.addTransition(State);
- } else {
- const auto CData = getContainerData(State, ContReg);
- if (!CData)
- return;
-
- if (const auto BeginSym = CData->getBegin()) {
- auto &SymMgr = C.getSymbolManager();
- auto &BVF = SymMgr.getBasicVals();
- auto &SVB = C.getSValBuilder();
- const auto newBeginSym =
- SVB.evalBinOp(State, BO_Sub,
- nonloc::SymbolVal(BeginSym),
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
- SymMgr.getType(BeginSym)).getAsSymbol();
- State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
- C.addTransition(State);
- }
- }
+void IteratorModeling::handlePrev(CheckerContext &C, const Expr *CE,
+ SVal RetVal, SVal Iter, SVal Amount) const {
+ handleRandomIncrOrDecr(C, CE, OO_Minus, RetVal, Iter, Amount);
}
-void IteratorModeling::handlePopFront(CheckerContext &C,
- const SVal &Cont) const {
- const auto *ContReg = Cont.getAsRegion();
- if (!ContReg)
- return;
-
- ContReg = ContReg->getMostDerivedObjectRegion();
-
- auto State = C.getState();
- const auto CData = getContainerData(State, ContReg);
- if (!CData)
- return;
-
- // For deque-like containers invalidate all iterator positions. For list-like
- // iterators only invalidate the first position
- if (const auto BeginSym = CData->getBegin()) {
- if (hasSubscriptOperator(State, ContReg)) {
- State = invalidateIteratorPositions(State, BeginSym, BO_LE);
- } else {
- State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
- }
- auto &SymMgr = C.getSymbolManager();
- auto &BVF = SymMgr.getBasicVals();
- auto &SVB = C.getSValBuilder();
- const auto newBeginSym =
- SVB.evalBinOp(State, BO_Add,
- nonloc::SymbolVal(BeginSym),
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
- SymMgr.getType(BeginSym)).getAsSymbol();
- State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
- C.addTransition(State);
- }
+void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE,
+ SVal RetVal, SVal Iter, SVal Amount) const {
+ handleRandomIncrOrDecr(C, CE, OO_Plus, RetVal, Iter, Amount);
}
-void IteratorModeling::handleInsert(CheckerContext &C, const SVal &Iter) const {
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, Iter);
- if (!Pos)
- return;
-
- // For deque-like containers invalidate all iterator positions. For
- // vector-like containers invalidate iterator positions after the insertion.
- const auto *Cont = Pos->getContainer();
- if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
- if (frontModifiable(State, Cont)) {
- State = invalidateAllIteratorPositions(State, Cont);
- } else {
- State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
- }
- if (const auto *CData = getContainerData(State, Cont)) {
- if (const auto EndSym = CData->getEnd()) {
- State = invalidateIteratorPositions(State, EndSym, BO_GE);
- State = setContainerData(State, Cont, CData->newEnd(nullptr));
- }
- }
- C.addTransition(State);
- }
-}
+void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE,
+ const SVal &RetVal,
+ const MemRegion *Cont) const {
+ Cont = Cont->getMostDerivedObjectRegion();
-void IteratorModeling::handleErase(CheckerContext &C, const SVal &Iter) const {
auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, Iter);
- if (!Pos)
- return;
+ const auto *LCtx = C.getLocationContext();
+ State = createIteratorPosition(State, RetVal, Cont, CE, LCtx, C.blockCount());
- // For deque-like containers invalidate all iterator positions. For
- // vector-like containers invalidate iterator positions at and after the
- // deletion. For list-like containers only invalidate the deleted position.
- const auto *Cont = Pos->getContainer();
- if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
- if (frontModifiable(State, Cont)) {
- State = invalidateAllIteratorPositions(State, Cont);
- } else {
- State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
- }
- if (const auto *CData = getContainerData(State, Cont)) {
- if (const auto EndSym = CData->getEnd()) {
- State = invalidateIteratorPositions(State, EndSym, BO_GE);
- State = setContainerData(State, Cont, CData->newEnd(nullptr));
- }
- }
- } else {
- State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
- }
C.addTransition(State);
}
-void IteratorModeling::handleErase(CheckerContext &C, const SVal &Iter1,
- const SVal &Iter2) const {
- auto State = C.getState();
- const auto *Pos1 = getIteratorPosition(State, Iter1);
- const auto *Pos2 = getIteratorPosition(State, Iter2);
- if (!Pos1 || !Pos2)
- return;
+bool IteratorModeling::noChangeInAdvance(CheckerContext &C, SVal Iter,
+ const Expr *CE) const {
+ // Compare the iterator position before and after the call. (To be called
+ // from `checkPostCall()`.)
+ const auto StateAfter = C.getState();
- // For deque-like containers invalidate all iterator positions. For
- // vector-like containers invalidate iterator positions at and after the
- // deletion range. For list-like containers only invalidate the deleted
- // position range [first..last].
- const auto *Cont = Pos1->getContainer();
- if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
- if (frontModifiable(State, Cont)) {
- State = invalidateAllIteratorPositions(State, Cont);
- } else {
- State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
- }
- if (const auto *CData = getContainerData(State, Cont)) {
- if (const auto EndSym = CData->getEnd()) {
- State = invalidateIteratorPositions(State, EndSym, BO_GE);
- State = setContainerData(State, Cont, CData->newEnd(nullptr));
- }
- }
- } else {
- State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
- Pos2->getOffset(), BO_LT);
- }
- C.addTransition(State);
-}
+ const auto *PosAfter = getIteratorPosition(StateAfter, Iter);
+ // If we have no position after the call of `std::advance`, then we are not
+ // interested. (Modeling of an inlined `std::advance()` should not remove the
+ // position in any case.)
+ if (!PosAfter)
+ return false;
-void IteratorModeling::handleEraseAfter(CheckerContext &C,
- const SVal &Iter) const {
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, Iter);
- if (!Pos)
- return;
+ const ExplodedNode *N = findCallEnter(C.getPredecessor(), CE);
+ assert(N && "Any call should have a `CallEnter` node.");
- // Invalidate the deleted iterator position, which is the position of the
- // parameter plus one.
- auto &SymMgr = C.getSymbolManager();
- auto &BVF = SymMgr.getBasicVals();
- auto &SVB = C.getSValBuilder();
- const auto NextSym =
- SVB.evalBinOp(State, BO_Add,
- nonloc::SymbolVal(Pos->getOffset()),
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
- SymMgr.getType(Pos->getOffset())).getAsSymbol();
- State = invalidateIteratorPositions(State, NextSym, BO_EQ);
- C.addTransition(State);
-}
+ const auto StateBefore = N->getState();
+ const auto *PosBefore = getIteratorPosition(StateBefore, Iter);
-void IteratorModeling::handleEraseAfter(CheckerContext &C, const SVal &Iter1,
- const SVal &Iter2) const {
- auto State = C.getState();
- const auto *Pos1 = getIteratorPosition(State, Iter1);
- const auto *Pos2 = getIteratorPosition(State, Iter2);
- if (!Pos1 || !Pos2)
- return;
+ assert(PosBefore && "`std::advance() should not create new iterator "
+ "position but change existing ones");
- // Invalidate the deleted iterator position range (first..last)
- State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
- Pos2->getOffset(), BO_LT);
- C.addTransition(State);
+ return PosBefore->getOffset() == PosAfter->getOffset();
}
void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State,
const char *NL, const char *Sep) const {
-
- auto ContMap = State->get<ContainerMap>();
-
- if (!ContMap.isEmpty()) {
- Out << Sep << "Container Data :" << NL;
- for (const auto &Cont : ContMap) {
- Cont.first->dumpToStream(Out);
- Out << " : [ ";
- const auto CData = Cont.second;
- if (CData.getBegin())
- CData.getBegin()->dumpToStream(Out);
- else
- Out << "<Unknown>";
- Out << " .. ";
- if (CData.getEnd())
- CData.getEnd()->dumpToStream(Out);
- else
- Out << "<Unknown>";
- Out << " ]" << NL;
- }
- }
-
auto SymbolMap = State->get<IteratorSymbolMap>();
auto RegionMap = State->get<IteratorRegionMap>();
+ // Use a counter to add newlines before every line except the first one.
+ unsigned Count = 0;
if (!SymbolMap.isEmpty() || !RegionMap.isEmpty()) {
Out << Sep << "Iterator Positions :" << NL;
for (const auto &Sym : SymbolMap) {
+ if (Count++)
+ Out << NL;
+
Sym.first->dumpToStream(Out);
Out << " : ";
const auto Pos = Sym.second;
@@ -1133,6 +714,9 @@ void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State,
}
for (const auto &Reg : RegionMap) {
+ if (Count++)
+ Out << NL;
+
Reg.first->dumpToStream(Out);
Out << " : ";
const auto Pos = Reg.second;
@@ -1144,229 +728,14 @@ void IteratorModeling::printState(raw_ostream &Out, ProgramStateRef State,
}
}
-
namespace {
-const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
- const MemRegion *Reg);
-
-bool isBeginCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- return IdInfo->getName().endswith_lower("begin");
-}
-
-bool isEndCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- return IdInfo->getName().endswith_lower("end");
-}
-
-bool isAssignCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() > 2)
- return false;
- return IdInfo->getName() == "assign";
-}
-
-bool isClearCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() > 0)
- return false;
- return IdInfo->getName() == "clear";
-}
-
-bool isPushBackCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() != 1)
- return false;
- return IdInfo->getName() == "push_back";
-}
-
-bool isEmplaceBackCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() < 1)
- return false;
- return IdInfo->getName() == "emplace_back";
-}
-
-bool isPopBackCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() > 0)
- return false;
- return IdInfo->getName() == "pop_back";
-}
-
-bool isPushFrontCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() != 1)
- return false;
- return IdInfo->getName() == "push_front";
-}
-
-bool isEmplaceFrontCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() < 1)
- return false;
- return IdInfo->getName() == "emplace_front";
-}
-
-bool isPopFrontCall(const FunctionDecl *Func) {
- const auto *IdInfo = Func->getIdentifier();
- if (!IdInfo)
- return false;
- if (Func->getNumParams() > 0)
- return false;
- return IdInfo->getName() == "pop_front";
-}
-
-bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; }
-
bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
}
-bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
- const auto *CRD = getCXXRecordDecl(State, Reg);
- if (!CRD)
- return false;
-
- for (const auto *Method : CRD->methods()) {
- if (!Method->isOverloadedOperator())
- continue;
- const auto OPK = Method->getOverloadedOperator();
- if (OPK == OO_Subscript) {
- return true;
- }
- }
- return false;
-}
-
-bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
- const auto *CRD = getCXXRecordDecl(State, Reg);
- if (!CRD)
- return false;
-
- for (const auto *Method : CRD->methods()) {
- if (!Method->getDeclName().isIdentifier())
- continue;
- if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
- return true;
- }
- }
- return false;
-}
-
-bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
- const auto *CRD = getCXXRecordDecl(State, Reg);
- if (!CRD)
- return false;
-
- for (const auto *Method : CRD->methods()) {
- if (!Method->getDeclName().isIdentifier())
- continue;
- if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
- return true;
- }
- }
- return false;
-}
-
-const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
- const MemRegion *Reg) {
- auto TI = getDynamicTypeInfo(State, Reg);
- if (!TI.isValid())
- return nullptr;
-
- auto Type = TI.getType();
- if (const auto *RefT = Type->getAs<ReferenceType>()) {
- Type = RefT->getPointeeType();
- }
-
- return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
-}
-
-SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
- const auto *CDataPtr = getContainerData(State, Cont);
- if (!CDataPtr)
- return nullptr;
-
- return CDataPtr->getBegin();
-}
-
-SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
- const auto *CDataPtr = getContainerData(State, Cont);
- if (!CDataPtr)
- return nullptr;
-
- return CDataPtr->getEnd();
-}
-
-ProgramStateRef createContainerBegin(ProgramStateRef State,
- const MemRegion *Cont, const 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) {
- 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 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) {
- const auto CData = CDataPtr->newEnd(Sym);
- return setContainerData(State, Cont, CData);
- }
-
- const auto CData = ContainerData::fromEnd(Sym);
- return setContainerData(State, Cont, CData);
-}
-
-ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
- const ContainerData &CData) {
- return State->set<ContainerMap>(Cont, CData);
+bool isSimpleComparisonOperator(BinaryOperatorKind OK) {
+ return OK == BO_EQ || OK == BO_NE;
}
ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
@@ -1381,47 +750,6 @@ ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
return nullptr;
}
-// 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;
-}
-
ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
SymbolRef Sym2, bool Equal) {
auto &SVB = State->getStateManager().getSValBuilder();
@@ -1454,22 +782,6 @@ ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
return NewState;
}
-bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
- auto RegionMap = State->get<IteratorRegionMap>();
- for (const auto &Reg : RegionMap) {
- if (Reg.second.getContainer() == Cont)
- return true;
- }
-
- auto SymbolMap = State->get<IteratorSymbolMap>();
- for (const auto &Sym : SymbolMap) {
- if (Sym.second.getContainer() == Cont)
- return true;
- }
-
- return false;
-}
-
bool isBoundThroughLazyCompoundVal(const Environment &Env,
const MemRegion *Reg) {
for (const auto &Binding : Env) {
@@ -1482,150 +794,18 @@ bool isBoundThroughLazyCompoundVal(const Environment &Env,
return false;
}
-template <typename Condition, typename Process>
-ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
- Process Proc) {
- auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
- auto RegionMap = State->get<IteratorRegionMap>();
- bool Changed = false;
- for (const auto &Reg : RegionMap) {
- if (Cond(Reg.second)) {
- RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
- Changed = true;
+const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) {
+ while (Node) {
+ ProgramPoint PP = Node->getLocation();
+ if (auto Enter = PP.getAs<CallEnter>()) {
+ if (Enter->getCallExpr() == Call)
+ break;
}
- }
-
- if (Changed)
- State = State->set<IteratorRegionMap>(RegionMap);
- auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
- auto SymbolMap = State->get<IteratorSymbolMap>();
- Changed = false;
- for (const auto &Sym : SymbolMap) {
- if (Cond(Sym.second)) {
- SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
- Changed = true;
- }
+ Node = Node->getFirstPred();
}
- if (Changed)
- State = State->set<IteratorSymbolMap>(SymbolMap);
-
- return State;
-}
-
-ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
- const MemRegion *Cont) {
- auto MatchCont = [&](const IteratorPosition &Pos) {
- return Pos.getContainer() == Cont;
- };
- auto Invalidate = [&](const IteratorPosition &Pos) {
- return Pos.invalidate();
- };
- return processIteratorPositions(State, MatchCont, Invalidate);
-}
-
-ProgramStateRef
-invalidateAllIteratorPositionsExcept(ProgramStateRef State,
- const MemRegion *Cont, SymbolRef Offset,
- BinaryOperator::Opcode Opc) {
- auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
- return Pos.getContainer() == Cont &&
- !compare(State, Pos.getOffset(), Offset, Opc);
- };
- auto Invalidate = [&](const IteratorPosition &Pos) {
- return Pos.invalidate();
- };
- return processIteratorPositions(State, MatchContAndCompare, Invalidate);
-}
-
-ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
- SymbolRef Offset,
- BinaryOperator::Opcode Opc) {
- auto Compare = [&](const IteratorPosition &Pos) {
- return compare(State, Pos.getOffset(), Offset, Opc);
- };
- auto Invalidate = [&](const IteratorPosition &Pos) {
- return Pos.invalidate();
- };
- return processIteratorPositions(State, Compare, Invalidate);
-}
-
-ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
- SymbolRef Offset1,
- BinaryOperator::Opcode Opc1,
- SymbolRef Offset2,
- BinaryOperator::Opcode Opc2) {
- auto Compare = [&](const IteratorPosition &Pos) {
- return compare(State, Pos.getOffset(), Offset1, Opc1) &&
- compare(State, Pos.getOffset(), Offset2, Opc2);
- };
- auto Invalidate = [&](const IteratorPosition &Pos) {
- return Pos.invalidate();
- };
- return processIteratorPositions(State, Compare, Invalidate);
-}
-
-ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
- const MemRegion *Cont,
- const MemRegion *NewCont) {
- auto MatchCont = [&](const IteratorPosition &Pos) {
- return Pos.getContainer() == Cont;
- };
- auto ReAssign = [&](const IteratorPosition &Pos) {
- return Pos.reAssign(NewCont);
- };
- return processIteratorPositions(State, MatchCont, ReAssign);
-}
-
-ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
- const MemRegion *Cont,
- const MemRegion *NewCont,
- SymbolRef Offset,
- BinaryOperator::Opcode Opc) {
- auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
- return Pos.getContainer() == Cont &&
- !compare(State, Pos.getOffset(), Offset, Opc);
- };
- auto ReAssign = [&](const IteratorPosition &Pos) {
- return Pos.reAssign(NewCont);
- };
- return processIteratorPositions(State, MatchContAndCompare, ReAssign);
-}
-
-// This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
-// `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator
-// position offsets where `CondSym` is true.
-ProgramStateRef rebaseSymbolInIteratorPositionsIf(
- ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
- SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
- auto LessThanEnd = [&](const IteratorPosition &Pos) {
- return compare(State, Pos.getOffset(), CondSym, Opc);
- };
- auto RebaseSymbol = [&](const IteratorPosition &Pos) {
- return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
- NewSym));
- };
- return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
-}
-
-// This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
-// `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression
-// `OrigExpr`.
-SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
- SymbolRef OrigExpr, SymbolRef OldExpr,
- SymbolRef NewSym) {
- auto &SymMgr = SVB.getSymbolManager();
- auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
- nonloc::SymbolVal(OldExpr),
- SymMgr.getType(OrigExpr));
-
- const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
- if (!DiffInt)
- return OrigExpr;
-
- return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
- SymMgr.getType(OrigExpr)).getAsSymbol();
+ return Node;
}
} // namespace
@@ -1634,6 +814,6 @@ void ento::registerIteratorModeling(CheckerManager &mgr) {
mgr.registerChecker<IteratorModeling>();
}
-bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) {
+bool ento::shouldRegisterIteratorModeling(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
index bd8b84d464b6..df8e379d1f20 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
@@ -27,22 +27,41 @@ using namespace iterator;
namespace {
class IteratorRangeChecker
- : public Checker<check::PreCall> {
+ : public Checker<check::PreCall, check::PreStmt<UnaryOperator>,
+ check::PreStmt<BinaryOperator>,
+ check::PreStmt<ArraySubscriptExpr>,
+ check::PreStmt<MemberExpr>> {
std::unique_ptr<BugType> OutOfRangeBugType;
- void verifyDereference(CheckerContext &C, const SVal &Val) const;
- void verifyIncrement(CheckerContext &C, const SVal &Iter) const;
- void verifyDecrement(CheckerContext &C, const SVal &Iter) const;
+ void verifyDereference(CheckerContext &C, SVal Val) const;
+ void verifyIncrement(CheckerContext &C, SVal Iter) const;
+ void verifyDecrement(CheckerContext &C, SVal Iter) const;
void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
- const SVal &LHS, const SVal &RHS) const;
- void reportBug(const StringRef &Message, const SVal &Val,
- CheckerContext &C, ExplodedNode *ErrNode) const;
+ SVal LHS, SVal RHS) const;
+ void verifyAdvance(CheckerContext &C, SVal LHS, SVal RHS) const;
+ void verifyPrev(CheckerContext &C, SVal LHS, SVal RHS) const;
+ void verifyNext(CheckerContext &C, SVal LHS, SVal RHS) const;
+ void reportBug(const StringRef &Message, SVal Val, CheckerContext &C,
+ ExplodedNode *ErrNode) const;
+
public:
IteratorRangeChecker();
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
-
+ void checkPreStmt(const UnaryOperator *UO, CheckerContext &C) const;
+ void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const;
+ void checkPreStmt(const ArraySubscriptExpr *ASE, CheckerContext &C) const;
+ void checkPreStmt(const MemberExpr *ME, CheckerContext &C) const;
+
+ using AdvanceFn = void (IteratorRangeChecker::*)(CheckerContext &, SVal,
+ SVal) const;
+
+ CallDescriptionMap<AdvanceFn> AdvanceFunctions = {
+ {{{"std", "advance"}, 2}, &IteratorRangeChecker::verifyAdvance},
+ {{{"std", "prev"}, 2}, &IteratorRangeChecker::verifyPrev},
+ {{{"std", "next"}, 2}, &IteratorRangeChecker::verifyNext},
+ };
};
bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
@@ -107,11 +126,73 @@ void IteratorRangeChecker::checkPreCall(const CallEvent &Call,
verifyDereference(C, Call.getArgSVal(0));
}
}
+ } else {
+ const AdvanceFn *Verifier = AdvanceFunctions.lookup(Call);
+ if (Verifier) {
+ if (Call.getNumArgs() > 1) {
+ (this->**Verifier)(C, Call.getArgSVal(0), Call.getArgSVal(1));
+ } else {
+ auto &BVF = C.getSValBuilder().getBasicValueFactory();
+ (this->**Verifier)(
+ C, Call.getArgSVal(0),
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
+ }
+ }
+ }
+}
+
+void IteratorRangeChecker::checkPreStmt(const UnaryOperator *UO,
+ CheckerContext &C) const {
+ if (isa<CXXThisExpr>(UO->getSubExpr()))
+ return;
+
+ ProgramStateRef State = C.getState();
+ UnaryOperatorKind OK = UO->getOpcode();
+ SVal SubVal = State->getSVal(UO->getSubExpr(), C.getLocationContext());
+
+ if (isDereferenceOperator(OK)) {
+ verifyDereference(C, SubVal);
+ } else if (isIncrementOperator(OK)) {
+ verifyIncrement(C, SubVal);
+ } else if (isDecrementOperator(OK)) {
+ verifyDecrement(C, SubVal);
+ }
+}
+
+void IteratorRangeChecker::checkPreStmt(const BinaryOperator *BO,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ BinaryOperatorKind OK = BO->getOpcode();
+ SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext());
+
+ if (isDereferenceOperator(OK)) {
+ verifyDereference(C, LVal);
+ } else if (isRandomIncrOrDecrOperator(OK)) {
+ SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext());
+ verifyRandomIncrOrDecr(C, BinaryOperator::getOverloadedOperator(OK), LVal,
+ RVal);
}
}
+void IteratorRangeChecker::checkPreStmt(const ArraySubscriptExpr *ASE,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SVal LVal = State->getSVal(ASE->getLHS(), C.getLocationContext());
+ verifyDereference(C, LVal);
+}
+
+void IteratorRangeChecker::checkPreStmt(const MemberExpr *ME,
+ CheckerContext &C) const {
+ if (!ME->isArrow() || ME->isImplicitAccess())
+ return;
+
+ ProgramStateRef State = C.getState();
+ SVal BaseVal = State->getSVal(ME->getBase(), C.getLocationContext());
+ verifyDereference(C, BaseVal);
+}
+
void IteratorRangeChecker::verifyDereference(CheckerContext &C,
- const SVal &Val) const {
+ SVal Val) const {
auto State = C.getState();
const auto *Pos = getIteratorPosition(State, Val);
if (Pos && isPastTheEnd(State, *Pos)) {
@@ -123,24 +204,21 @@ void IteratorRangeChecker::verifyDereference(CheckerContext &C,
}
}
-void IteratorRangeChecker::verifyIncrement(CheckerContext &C,
- const SVal &Iter) const {
+void IteratorRangeChecker::verifyIncrement(CheckerContext &C, SVal Iter) const {
auto &BVF = C.getSValBuilder().getBasicValueFactory();
verifyRandomIncrOrDecr(C, OO_Plus, Iter,
nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
}
-void IteratorRangeChecker::verifyDecrement(CheckerContext &C,
- const SVal &Iter) const {
+void IteratorRangeChecker::verifyDecrement(CheckerContext &C, SVal Iter) const {
auto &BVF = C.getSValBuilder().getBasicValueFactory();
verifyRandomIncrOrDecr(C, OO_Minus, Iter,
nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
}
void IteratorRangeChecker::verifyRandomIncrOrDecr(CheckerContext &C,
- OverloadedOperatorKind Op,
- const SVal &LHS,
- const SVal &RHS) const {
+ OverloadedOperatorKind Op,
+ SVal LHS, SVal RHS) const {
auto State = C.getState();
auto Value = RHS;
@@ -180,12 +258,32 @@ void IteratorRangeChecker::verifyRandomIncrOrDecr(CheckerContext &C,
}
}
-void IteratorRangeChecker::reportBug(const StringRef &Message,
- const SVal &Val, CheckerContext &C,
- ExplodedNode *ErrNode) const {
+void IteratorRangeChecker::verifyAdvance(CheckerContext &C, SVal LHS,
+ SVal RHS) const {
+ verifyRandomIncrOrDecr(C, OO_PlusEqual, LHS, RHS);
+}
+
+void IteratorRangeChecker::verifyPrev(CheckerContext &C, SVal LHS,
+ SVal RHS) const {
+ verifyRandomIncrOrDecr(C, OO_Minus, LHS, RHS);
+}
+
+void IteratorRangeChecker::verifyNext(CheckerContext &C, SVal LHS,
+ SVal RHS) const {
+ verifyRandomIncrOrDecr(C, OO_Plus, LHS, RHS);
+}
+
+void IteratorRangeChecker::reportBug(const StringRef &Message, SVal Val,
+ CheckerContext &C,
+ ExplodedNode *ErrNode) const {
auto R = std::make_unique<PathSensitiveBugReport>(*OutOfRangeBugType, Message,
ErrNode);
+
+ const auto *Pos = getIteratorPosition(C.getState(), Val);
+ assert(Pos && "Iterator without known position cannot be out-of-range.");
+
R->markInteresting(Val);
+ R->markInteresting(Pos->getContainer());
C.emitReport(std::move(R));
}
@@ -268,6 +366,6 @@ void ento::registerIteratorRangeChecker(CheckerManager &mgr) {
mgr.registerChecker<IteratorRangeChecker>();
}
-bool ento::shouldRegisterIteratorRangeChecker(const LangOptions &LO) {
+bool ento::shouldRegisterIteratorRangeChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
index 0d64fbd6f62e..3e6756efe0e6 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
@@ -739,7 +739,7 @@ void ento::registerIvarInvalidationModeling(CheckerManager &mgr) {
mgr.registerChecker<IvarInvalidationChecker>();
}
-bool ento::shouldRegisterIvarInvalidationModeling(const LangOptions &LO) {
+bool ento::shouldRegisterIvarInvalidationModeling(const CheckerManager &mgr) {
return true;
}
@@ -751,7 +751,7 @@ bool ento::shouldRegisterIvarInvalidationModeling(const LangOptions &LO) {
checker->Filter.checkName_##name = mgr.getCurrentCheckerName(); \
} \
\
- bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
+ bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
REGISTER_CHECKER(InstanceVariableInvalidation)
REGISTER_CHECKER(MissingInvalidationMethod)
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
index 7522fdd0a99b..1f3d8844d330 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
@@ -314,6 +314,6 @@ void ento::registerLLVMConventionsChecker(CheckerManager &mgr) {
mgr.registerChecker<LLVMConventionsChecker>();
}
-bool ento::shouldRegisterLLVMConventionsChecker(const LangOptions &LO) {
+bool ento::shouldRegisterLLVMConventionsChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
index 79de1844e745..252377f24bd7 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
@@ -1403,7 +1403,7 @@ void ento::registerNonLocalizedStringChecker(CheckerManager &mgr) {
checker, "AggressiveReport");
}
-bool ento::shouldRegisterNonLocalizedStringChecker(const LangOptions &LO) {
+bool ento::shouldRegisterNonLocalizedStringChecker(const CheckerManager &mgr) {
return true;
}
@@ -1412,7 +1412,7 @@ void ento::registerEmptyLocalizationContextChecker(CheckerManager &mgr) {
}
bool ento::shouldRegisterEmptyLocalizationContextChecker(
- const LangOptions &LO) {
+ const CheckerManager &mgr) {
return true;
}
@@ -1420,6 +1420,6 @@ void ento::registerPluralMisuseChecker(CheckerManager &mgr) {
mgr.registerChecker<PluralMisuseChecker>();
}
-bool ento::shouldRegisterPluralMisuseChecker(const LangOptions &LO) {
+bool ento::shouldRegisterPluralMisuseChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
index d73e2eb92d42..837213875a60 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
@@ -210,15 +210,16 @@ void MIGChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const {
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();
- });
+ const NoteTag *T =
+ C.getNoteTag([this, PVD](PathSensitiveBugReport &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 std::string(OS.str());
+ });
C.addTransition(State->set<ReleasedParameter>(true), T);
}
@@ -292,6 +293,6 @@ void ento::registerMIGChecker(CheckerManager &Mgr) {
Mgr.registerChecker<MIGChecker>();
}
-bool ento::shouldRegisterMIGChecker(const LangOptions &LO) {
+bool ento::shouldRegisterMIGChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp
index 7f9ba0de1dc2..7ac7a38dacf3 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp
@@ -16,6 +16,7 @@
#include "MPIChecker.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
namespace clang {
namespace ento {
@@ -146,7 +147,7 @@ void MPIChecker::allRegionsUsedByWait(
llvm::SmallVector<const MemRegion *, 2> &ReqRegions,
const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const {
- MemRegionManager *const RegionManager = MR->getMemRegionManager();
+ MemRegionManager &RegionManager = MR->getMemRegionManager();
if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
const SubRegion *SuperRegion{nullptr};
@@ -160,15 +161,16 @@ void MPIChecker::allRegionsUsedByWait(
return;
}
- const auto &Size = Ctx.getStoreManager().getSizeInElements(
- Ctx.getState(), SuperRegion,
+ DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
+ Ctx.getState(), SuperRegion, Ctx.getSValBuilder(),
CE.getArgExpr(1)->getType()->getPointeeType());
- const llvm::APSInt &ArrSize = Size.getAs<nonloc::ConcreteInt>()->getValue();
+ const llvm::APSInt &ArrSize =
+ ElementCount.getAs<nonloc::ConcreteInt>()->getValue();
for (size_t i = 0; i < ArrSize; ++i) {
const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i);
- const ElementRegion *const ER = RegionManager->getElementRegion(
+ const ElementRegion *const ER = RegionManager.getElementRegion(
CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion,
Ctx.getASTContext());
@@ -188,6 +190,6 @@ void clang::ento::registerMPIChecker(CheckerManager &MGR) {
MGR.registerChecker<clang::ento::mpi::MPIChecker>();
}
-bool clang::ento::shouldRegisterMPIChecker(const LangOptions &LO) {
+bool clang::ento::shouldRegisterMPIChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
index e064ca6bd88f..87477e96d2d1 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
@@ -66,7 +66,7 @@ public:
ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
bool Assumption) const;
void printState(raw_ostream &Out, ProgramStateRef State,
- const char *NL, const char *Sep) const;
+ const char *NL, const char *Sep) const override;
private:
typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
@@ -667,6 +667,6 @@ void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
mgr.registerChecker<MacOSKeychainAPIChecker>();
}
-bool ento::shouldRegisterMacOSKeychainAPIChecker(const LangOptions &LO) {
+bool ento::shouldRegisterMacOSKeychainAPIChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
index 410721d8b6ff..04e7f8dec8d7 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
@@ -176,6 +176,6 @@ void ento::registerMacOSXAPIChecker(CheckerManager &mgr) {
mgr.registerChecker<MacOSXAPIChecker>();
}
-bool ento::shouldRegisterMacOSXAPIChecker(const LangOptions &LO) {
+bool ento::shouldRegisterMacOSXAPIChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 09306383d53f..d5b0a5b2220f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -44,34 +44,49 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "AllocationState.h"
#include "InterCheckerAPI.h"
#include "clang/AST/Attr.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/AST/ParentMap.h"
+#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/Lexer.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
-#include "AllocationState.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorHandling.h"
#include <climits>
+#include <functional>
#include <utility>
using namespace clang;
using namespace ento;
+using namespace std::placeholders;
//===----------------------------------------------------------------------===//
-// The types of allocation we're modeling.
+// The types of allocation we're modeling. This is used to check whether a
+// dynamically allocated object is deallocated with the correct function, like
+// not using operator delete on an object created by malloc(), or alloca regions
+// aren't ever deallocated manually.
//===----------------------------------------------------------------------===//
namespace {
@@ -87,26 +102,16 @@ enum AllocationFamily {
AF_InnerBuffer
};
-struct MemFunctionInfoTy;
-
} // end of anonymous namespace
-/// Determine family of a deallocation expression.
-static AllocationFamily
-getAllocationFamily(const MemFunctionInfoTy &MemFunctionInfo, CheckerContext &C,
- const Stmt *S);
-
/// Print names of allocators and deallocators.
///
/// \returns true on success.
-static bool printAllocDeallocName(raw_ostream &os, CheckerContext &C,
- const Expr *E);
+static bool printMemFnName(raw_ostream &os, CheckerContext &C, const Expr *E);
-/// Print expected name of an allocator based on the deallocator's
-/// family derived from the DeallocExpr.
-static void printExpectedAllocName(raw_ostream &os,
- const MemFunctionInfoTy &MemFunctionInfo,
- CheckerContext &C, const Expr *E);
+/// Print expected name of an allocator based on the deallocator's family
+/// derived from the DeallocExpr.
+static void printExpectedAllocName(raw_ostream &os, AllocationFamily Family);
/// Print expected name of a deallocator based on the allocator's
/// family.
@@ -207,7 +212,7 @@ static bool isReleased(SymbolRef Sym, CheckerContext &C);
/// value; if unspecified, the value of expression \p E is used.
static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E,
ProgramStateRef State,
- AllocationFamily Family = AF_Malloc,
+ AllocationFamily Family,
Optional<SVal> RetVal = None);
//===----------------------------------------------------------------------===//
@@ -265,60 +270,14 @@ struct ReallocPair {
REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair)
-//===----------------------------------------------------------------------===//
-// Kinds of memory operations, information about resource managing functions.
-//===----------------------------------------------------------------------===//
-
-namespace {
-
-enum class MemoryOperationKind { MOK_Allocate, MOK_Free, MOK_Any };
-
-struct MemFunctionInfoTy {
- /// The value of the MallocChecker:Optimistic is stored in this variable.
- ///
- /// In pessimistic mode, the checker assumes that it does not know which
- /// functions might free the memory.
- /// In optimistic mode, the checker assumes that all user-defined functions
- /// which might free a pointer are annotated.
- DefaultBool ShouldIncludeOwnershipAnnotatedFunctions;
-
- // TODO: Change these to CallDescription, and get rid of lazy initialization.
- mutable IdentifierInfo *II_alloca = nullptr, *II_win_alloca = nullptr,
- *II_malloc = nullptr, *II_free = nullptr,
- *II_realloc = nullptr, *II_calloc = nullptr,
- *II_valloc = nullptr, *II_reallocf = nullptr,
- *II_strndup = nullptr, *II_strdup = nullptr,
- *II_win_strdup = nullptr, *II_kmalloc = nullptr,
- *II_if_nameindex = nullptr,
- *II_if_freenameindex = nullptr, *II_wcsdup = nullptr,
- *II_win_wcsdup = nullptr, *II_g_malloc = nullptr,
- *II_g_malloc0 = nullptr, *II_g_realloc = nullptr,
- *II_g_try_malloc = nullptr,
- *II_g_try_malloc0 = nullptr,
- *II_g_try_realloc = nullptr, *II_g_free = nullptr,
- *II_g_memdup = nullptr, *II_g_malloc_n = nullptr,
- *II_g_malloc0_n = nullptr, *II_g_realloc_n = nullptr,
- *II_g_try_malloc_n = nullptr,
- *II_g_try_malloc0_n = nullptr, *II_kfree = nullptr,
- *II_g_try_realloc_n = nullptr;
-
- void initIdentifierInfo(ASTContext &C) const;
-
- ///@{
- /// Check if this is one of the functions which can allocate/reallocate
- /// memory pointed to by one of its arguments.
- bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const;
- bool isCMemFunction(const FunctionDecl *FD, ASTContext &C,
- AllocationFamily Family,
- MemoryOperationKind MemKind) const;
-
- /// Tells if the callee is one of the builtin new/delete operators, including
- /// placement operators and other standard overloads.
- bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;
- ///@}
-};
-
-} // end of anonymous namespace
+/// Tells if the callee is one of the builtin new/delete operators, including
+/// placement operators and other standard overloads.
+static bool isStandardNewDelete(const FunctionDecl *FD);
+static bool isStandardNewDelete(const CallEvent &Call) {
+ if (!Call.getDecl() || !isa<FunctionDecl>(Call.getDecl()))
+ return false;
+ return isStandardNewDelete(cast<FunctionDecl>(Call.getDecl()));
+}
//===----------------------------------------------------------------------===//
// Definition of the MallocChecker class.
@@ -329,13 +288,15 @@ namespace {
class MallocChecker
: public Checker<check::DeadSymbols, check::PointerEscape,
check::ConstPointerEscape, check::PreStmt<ReturnStmt>,
- check::EndFunction, check::PreCall,
- check::PostStmt<CallExpr>, check::PostStmt<CXXNewExpr>,
- check::NewAllocator, check::PreStmt<CXXDeleteExpr>,
- check::PostStmt<BlockExpr>, check::PostObjCMessage,
- check::Location, eval::Assume> {
+ check::EndFunction, check::PreCall, check::PostCall,
+ check::NewAllocator, check::PostStmt<BlockExpr>,
+ check::PostObjCMessage, check::Location, eval::Assume> {
public:
- MemFunctionInfoTy MemFunctionInfo;
+ /// In pessimistic mode, the checker assumes that it does not know which
+ /// functions might free the memory.
+ /// In optimistic mode, the checker assumes that all user-defined functions
+ /// which might free a pointer are annotated.
+ DefaultBool ShouldIncludeOwnershipAnnotatedFunctions;
/// Many checkers are essentially built into this one, so enabling them will
/// make MallocChecker perform additional modeling and reporting.
@@ -357,11 +318,8 @@ public:
CheckerNameRef CheckNames[CK_NumCheckKinds];
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
- void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
- void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
- void checkNewAllocator(const CXXNewExpr *NE, SVal Target,
- CheckerContext &C) const;
- void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkNewAllocator(const CXXAllocatorCall &Call, CheckerContext &C) const;
void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const;
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
@@ -395,23 +353,107 @@ private:
mutable std::unique_ptr<BugType> BT_OffsetFree[CK_NumCheckKinds];
mutable std::unique_ptr<BugType> BT_UseZerroAllocated[CK_NumCheckKinds];
+#define CHECK_FN(NAME) \
+ void NAME(const CallEvent &Call, CheckerContext &C) const;
+
+ CHECK_FN(checkFree)
+ CHECK_FN(checkIfNameIndex)
+ CHECK_FN(checkBasicAlloc)
+ CHECK_FN(checkKernelMalloc)
+ CHECK_FN(checkCalloc)
+ CHECK_FN(checkAlloca)
+ CHECK_FN(checkStrdup)
+ CHECK_FN(checkIfFreeNameIndex)
+ CHECK_FN(checkCXXNewOrCXXDelete)
+ CHECK_FN(checkGMalloc0)
+ CHECK_FN(checkGMemdup)
+ CHECK_FN(checkGMallocN)
+ CHECK_FN(checkGMallocN0)
+ CHECK_FN(checkReallocN)
+ CHECK_FN(checkOwnershipAttr)
+
+ void checkRealloc(const CallEvent &Call, CheckerContext &C,
+ bool ShouldFreeOnFail) const;
+
+ using CheckFn = std::function<void(const MallocChecker *,
+ const CallEvent &Call, CheckerContext &C)>;
+
+ const CallDescriptionMap<CheckFn> FreeingMemFnMap{
+ {{"free", 1}, &MallocChecker::checkFree},
+ {{"if_freenameindex", 1}, &MallocChecker::checkIfFreeNameIndex},
+ {{"kfree", 1}, &MallocChecker::checkFree},
+ {{"g_free", 1}, &MallocChecker::checkFree},
+ };
+
+ bool isFreeingCall(const CallEvent &Call) const;
+
+ CallDescriptionMap<CheckFn> AllocatingMemFnMap{
+ {{"alloca", 1}, &MallocChecker::checkAlloca},
+ {{"_alloca", 1}, &MallocChecker::checkAlloca},
+ {{"malloc", 1}, &MallocChecker::checkBasicAlloc},
+ {{"malloc", 3}, &MallocChecker::checkKernelMalloc},
+ {{"calloc", 2}, &MallocChecker::checkCalloc},
+ {{"valloc", 1}, &MallocChecker::checkBasicAlloc},
+ {{CDF_MaybeBuiltin, "strndup", 2}, &MallocChecker::checkStrdup},
+ {{CDF_MaybeBuiltin, "strdup", 1}, &MallocChecker::checkStrdup},
+ {{"_strdup", 1}, &MallocChecker::checkStrdup},
+ {{"kmalloc", 2}, &MallocChecker::checkKernelMalloc},
+ {{"if_nameindex", 1}, &MallocChecker::checkIfNameIndex},
+ {{CDF_MaybeBuiltin, "wcsdup", 1}, &MallocChecker::checkStrdup},
+ {{CDF_MaybeBuiltin, "_wcsdup", 1}, &MallocChecker::checkStrdup},
+ {{"g_malloc", 1}, &MallocChecker::checkBasicAlloc},
+ {{"g_malloc0", 1}, &MallocChecker::checkGMalloc0},
+ {{"g_try_malloc", 1}, &MallocChecker::checkBasicAlloc},
+ {{"g_try_malloc0", 1}, &MallocChecker::checkGMalloc0},
+ {{"g_memdup", 2}, &MallocChecker::checkGMemdup},
+ {{"g_malloc_n", 2}, &MallocChecker::checkGMallocN},
+ {{"g_malloc0_n", 2}, &MallocChecker::checkGMallocN0},
+ {{"g_try_malloc_n", 2}, &MallocChecker::checkGMallocN},
+ {{"g_try_malloc0_n", 2}, &MallocChecker::checkGMallocN0},
+ };
+
+ CallDescriptionMap<CheckFn> ReallocatingMemFnMap{
+ {{"realloc", 2},
+ std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)},
+ {{"reallocf", 2},
+ std::bind(&MallocChecker::checkRealloc, _1, _2, _3, true)},
+ {{"g_realloc", 2},
+ std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)},
+ {{"g_try_realloc", 2},
+ std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)},
+ {{"g_realloc_n", 3}, &MallocChecker::checkReallocN},
+ {{"g_try_realloc_n", 3}, &MallocChecker::checkReallocN},
+ };
+
+ bool isMemCall(const CallEvent &Call) const;
+
// TODO: Remove mutable by moving the initializtaion to the registry function.
mutable Optional<uint64_t> KernelZeroFlagVal;
+ using KernelZeroSizePtrValueTy = Optional<int>;
+ /// Store the value of macro called `ZERO_SIZE_PTR`.
+ /// The value is initialized at first use, before first use the outer
+ /// Optional is empty, afterwards it contains another Optional that indicates
+ /// if the macro value could be determined, and if yes the value itself.
+ mutable Optional<KernelZeroSizePtrValueTy> KernelZeroSizePtrValue;
+
/// Process C++ operator new()'s allocation, which is the part of C++
/// new-expression that goes before the constructor.
- void processNewAllocation(const CXXNewExpr *NE, CheckerContext &C,
- SVal Target) const;
+ LLVM_NODISCARD
+ ProgramStateRef processNewAllocation(const CXXAllocatorCall &Call,
+ CheckerContext &C,
+ AllocationFamily Family) const;
/// Perform a zero-allocation check.
///
- /// \param [in] E The expression that allocates memory.
+ /// \param [in] Call The expression that allocates memory.
/// \param [in] IndexOfSizeArg Index of the argument that specifies the size
/// of the memory that needs to be allocated. E.g. for malloc, this would be
/// 0.
/// \param [in] RetVal Specifies the newly allocated pointer value;
/// if unspecified, the value of expression \p E is used.
- static ProgramStateRef ProcessZeroAllocCheck(CheckerContext &C, const Expr *E,
+ LLVM_NODISCARD
+ static ProgramStateRef ProcessZeroAllocCheck(const CallEvent &Call,
const unsigned IndexOfSizeArg,
ProgramStateRef State,
Optional<SVal> RetVal = None);
@@ -428,50 +470,54 @@ private:
/// - first: name of the resource (e.g. 'malloc')
/// - (OPTIONAL) second: size of the allocated region
///
- /// \param [in] CE The expression that allocates memory.
+ /// \param [in] Call The expression that allocates memory.
/// \param [in] Att The ownership_returns attribute.
/// \param [in] State The \c ProgramState right before allocation.
/// \returns The ProgramState right after allocation.
- ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
- const CallExpr *CE,
- const OwnershipAttr* Att,
+ LLVM_NODISCARD
+ ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call,
+ const OwnershipAttr *Att,
ProgramStateRef State) const;
/// Models memory allocation.
///
- /// \param [in] CE The expression that allocates memory.
+ /// \param [in] Call The expression that allocates memory.
/// \param [in] SizeEx Size of the memory that needs to be allocated.
/// \param [in] Init The value the allocated memory needs to be initialized.
/// with. For example, \c calloc initializes the allocated memory to 0,
/// malloc leaves it undefined.
/// \param [in] State The \c ProgramState right before allocation.
/// \returns The ProgramState right after allocation.
- static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
+ LLVM_NODISCARD
+ static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call,
const Expr *SizeEx, SVal Init,
ProgramStateRef State,
- AllocationFamily Family = AF_Malloc);
+ AllocationFamily Family);
/// Models memory allocation.
///
- /// \param [in] CE The expression that allocates memory.
+ /// \param [in] Call The expression that allocates memory.
/// \param [in] Size Size of the memory that needs to be allocated.
/// \param [in] Init The value the allocated memory needs to be initialized.
/// with. For example, \c calloc initializes the allocated memory to 0,
/// malloc leaves it undefined.
/// \param [in] State The \c ProgramState right before allocation.
/// \returns The ProgramState right after allocation.
- static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
+ LLVM_NODISCARD
+ static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call,
SVal Size, SVal Init,
ProgramStateRef State,
- AllocationFamily Family = AF_Malloc);
+ AllocationFamily Family);
+ LLVM_NODISCARD
static ProgramStateRef addExtentSize(CheckerContext &C, const CXXNewExpr *NE,
ProgramStateRef State, SVal Target);
// Check if this malloc() for special flags. At present that means M_ZERO or
// __GFP_ZERO (in which case, treat it like calloc).
+ LLVM_NODISCARD
llvm::Optional<ProgramStateRef>
- performKernelMalloc(const CallExpr *CE, CheckerContext &C,
+ performKernelMalloc(const CallEvent &Call, CheckerContext &C,
const ProgramStateRef &State) const;
/// Model functions with the ownership_takes and ownership_holds attributes.
@@ -487,17 +533,18 @@ private:
/// - first: name of the resource (e.g. 'malloc')
/// - second: index of the parameter the attribute applies to
///
- /// \param [in] CE The expression that frees memory.
+ /// \param [in] Call The expression that frees memory.
/// \param [in] Att The ownership_takes or ownership_holds attribute.
/// \param [in] State The \c ProgramState right before allocation.
/// \returns The ProgramState right after deallocation.
- ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE,
- const OwnershipAttr* Att,
+ LLVM_NODISCARD
+ ProgramStateRef FreeMemAttr(CheckerContext &C, const CallEvent &Call,
+ const OwnershipAttr *Att,
ProgramStateRef State) const;
/// Models memory deallocation.
///
- /// \param [in] CE The expression that frees memory.
+ /// \param [in] Call The expression that frees memory.
/// \param [in] State The \c ProgramState right before allocation.
/// \param [in] Num Index of the argument that needs to be freed. This is
/// normally 0, but for custom free functions it may be different.
@@ -514,15 +561,17 @@ private:
/// \param [in] ReturnsNullOnFailure Whether the memory deallocation function
/// we're modeling returns with Null on failure.
/// \returns The ProgramState right after deallocation.
- ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE,
+ LLVM_NODISCARD
+ ProgramStateRef FreeMemAux(CheckerContext &C, const CallEvent &Call,
ProgramStateRef State, unsigned Num, bool Hold,
bool &IsKnownToBeAllocated,
+ AllocationFamily Family,
bool ReturnsNullOnFailure = false) const;
/// Models memory deallocation.
///
/// \param [in] ArgExpr The variable who's pointee needs to be freed.
- /// \param [in] ParentExpr The expression that frees the memory.
+ /// \param [in] Call The expression that frees the memory.
/// \param [in] State The \c ProgramState right before allocation.
/// normally 0, but for custom free functions it may be different.
/// \param [in] Hold Whether the parameter at \p Index has the ownership_holds
@@ -538,9 +587,11 @@ private:
/// \param [in] ReturnsNullOnFailure Whether the memory deallocation function
/// we're modeling returns with Null on failure.
/// \returns The ProgramState right after deallocation.
+ LLVM_NODISCARD
ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
- const Expr *ParentExpr, ProgramStateRef State,
+ const CallEvent &Call, ProgramStateRef State,
bool Hold, bool &IsKnownToBeAllocated,
+ AllocationFamily Family,
bool ReturnsNullOnFailure = false) const;
// TODO: Needs some refactoring, as all other deallocation modeling
@@ -549,15 +600,17 @@ private:
//
/// Models memory reallocation.
///
- /// \param [in] CE The expression that reallocated memory
+ /// \param [in] Call The expression that reallocated memory
/// \param [in] ShouldFreeOnFail Whether if reallocation fails, the supplied
/// memory should be freed.
/// \param [in] State The \c ProgramState right before reallocation.
/// \param [in] SuffixWithN Whether the reallocation function we're modeling
/// has an '_n' suffix, such as g_realloc_n.
/// \returns The ProgramState right after reallocation.
- ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE,
+ LLVM_NODISCARD
+ ProgramStateRef ReallocMemAux(CheckerContext &C, const CallEvent &Call,
bool ShouldFreeOnFail, ProgramStateRef State,
+ AllocationFamily Family,
bool SuffixWithN = false) const;
/// Evaluates the buffer size that needs to be allocated.
@@ -565,20 +618,22 @@ private:
/// \param [in] Blocks The amount of blocks that needs to be allocated.
/// \param [in] BlockBytes The size of a block.
/// \returns The symbolic value of \p Blocks * \p BlockBytes.
+ LLVM_NODISCARD
static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
const Expr *BlockBytes);
/// Models zero initialized array allocation.
///
- /// \param [in] CE The expression that reallocated memory
+ /// \param [in] Call The expression that reallocated memory
/// \param [in] State The \c ProgramState right before reallocation.
/// \returns The ProgramState right after allocation.
- static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE,
+ LLVM_NODISCARD
+ static ProgramStateRef CallocMem(CheckerContext &C, const CallEvent &Call,
ProgramStateRef State);
/// 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,
+ bool suppressDeallocationsInSuspiciousContexts(const CallEvent &Call,
CheckerContext &C) const;
/// If in \p S \p Sym is used, check whether \p Sym was already freed.
@@ -607,6 +662,7 @@ private:
SymbolRef &EscapingSymbol) const;
/// Implementation of the checkPointerEscape callbacks.
+ LLVM_NODISCARD
ProgramStateRef checkPointerEscapeAux(ProgramStateRef State,
const InvalidatedSymbols &Escaped,
const CallEvent *Call,
@@ -622,44 +678,53 @@ private:
/// family/call/symbol.
Optional<CheckKind> getCheckIfTracked(AllocationFamily Family,
bool IsALeakCheck = false) const;
- Optional<CheckKind> getCheckIfTracked(CheckerContext &C,
- const Stmt *AllocDeallocStmt,
- bool IsALeakCheck = false) const;
+
Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
bool IsALeakCheck = false) const;
///@}
static bool SummarizeValue(raw_ostream &os, SVal V);
static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR);
- void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
- const Expr *DeallocExpr) const;
- void ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
+ void HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal, SourceRange Range,
+ const Expr *DeallocExpr,
+ AllocationFamily Family) const;
+
+ void HandleFreeAlloca(CheckerContext &C, SVal ArgVal,
SourceRange Range) const;
- void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range,
+
+ void HandleMismatchedDealloc(CheckerContext &C, SourceRange Range,
const Expr *DeallocExpr, const RefState *RS,
SymbolRef Sym, bool OwnershipTransferred) const;
- void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
- const Expr *DeallocExpr,
+
+ void HandleOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
+ const Expr *DeallocExpr, AllocationFamily Family,
const Expr *AllocExpr = nullptr) const;
- void ReportUseAfterFree(CheckerContext &C, SourceRange Range,
+
+ void HandleUseAfterFree(CheckerContext &C, SourceRange Range,
SymbolRef Sym) const;
- void ReportDoubleFree(CheckerContext &C, SourceRange Range, bool Released,
+
+ void HandleDoubleFree(CheckerContext &C, SourceRange Range, bool Released,
SymbolRef Sym, SymbolRef PrevSym) const;
- void ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const;
+ void HandleDoubleDelete(CheckerContext &C, SymbolRef Sym) const;
- void ReportUseZeroAllocated(CheckerContext &C, SourceRange Range,
- SymbolRef Sym) const;
+ void HandleUseZeroAlloc(CheckerContext &C, SourceRange Range,
+ SymbolRef Sym) const;
- void ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal,
- SourceRange Range, const Expr *FreeExpr) const;
+ void HandleFunctionPtrFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
+ const Expr *FreeExpr,
+ AllocationFamily Family) const;
/// Find the location of the allocation for Sym on the path leading to the
/// exploded node N.
static LeakInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
CheckerContext &C);
- void reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const;
+ void HandleLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const;
+
+ /// Test if value in ArgVal equals to value in macro `ZERO_SIZE_PTR`.
+ bool isArgZERO_SIZE_PTR(ProgramStateRef State, CheckerContext &C,
+ SVal ArgVal) const;
};
//===----------------------------------------------------------------------===//
@@ -782,7 +847,7 @@ private:
os << "Reallocation of " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex)
<< " parameter failed";
- return os.str();
+ return std::string(os.str());
}
std::string getMessageForReturn(const CallExpr *CallExpr) override {
@@ -800,6 +865,7 @@ REGISTER_MAP_WITH_PROGRAMSTATE(FreeReturnValue, SymbolRef, SymbolRef)
namespace {
class StopTrackingCallback final : public SymbolVisitor {
ProgramStateRef state;
+
public:
StopTrackingCallback(ProgramStateRef st) : state(std::move(st)) {}
ProgramStateRef getState() const { return state; }
@@ -811,160 +877,57 @@ public:
};
} // end anonymous namespace
-//===----------------------------------------------------------------------===//
-// Methods of MemFunctionInfoTy.
-//===----------------------------------------------------------------------===//
-
-void MemFunctionInfoTy::initIdentifierInfo(ASTContext &Ctx) const {
- if (II_malloc)
- return;
- II_alloca = &Ctx.Idents.get("alloca");
- II_malloc = &Ctx.Idents.get("malloc");
- II_free = &Ctx.Idents.get("free");
- II_realloc = &Ctx.Idents.get("realloc");
- II_reallocf = &Ctx.Idents.get("reallocf");
- II_calloc = &Ctx.Idents.get("calloc");
- II_valloc = &Ctx.Idents.get("valloc");
- II_strdup = &Ctx.Idents.get("strdup");
- II_strndup = &Ctx.Idents.get("strndup");
- II_wcsdup = &Ctx.Idents.get("wcsdup");
- II_kmalloc = &Ctx.Idents.get("kmalloc");
- II_kfree = &Ctx.Idents.get("kfree");
- II_if_nameindex = &Ctx.Idents.get("if_nameindex");
- II_if_freenameindex = &Ctx.Idents.get("if_freenameindex");
-
- //MSVC uses `_`-prefixed instead, so we check for them too.
- II_win_strdup = &Ctx.Idents.get("_strdup");
- II_win_wcsdup = &Ctx.Idents.get("_wcsdup");
- II_win_alloca = &Ctx.Idents.get("_alloca");
-
- // Glib
- II_g_malloc = &Ctx.Idents.get("g_malloc");
- II_g_malloc0 = &Ctx.Idents.get("g_malloc0");
- II_g_realloc = &Ctx.Idents.get("g_realloc");
- II_g_try_malloc = &Ctx.Idents.get("g_try_malloc");
- II_g_try_malloc0 = &Ctx.Idents.get("g_try_malloc0");
- II_g_try_realloc = &Ctx.Idents.get("g_try_realloc");
- II_g_free = &Ctx.Idents.get("g_free");
- II_g_memdup = &Ctx.Idents.get("g_memdup");
- II_g_malloc_n = &Ctx.Idents.get("g_malloc_n");
- II_g_malloc0_n = &Ctx.Idents.get("g_malloc0_n");
- II_g_realloc_n = &Ctx.Idents.get("g_realloc_n");
- II_g_try_malloc_n = &Ctx.Idents.get("g_try_malloc_n");
- II_g_try_malloc0_n = &Ctx.Idents.get("g_try_malloc0_n");
- II_g_try_realloc_n = &Ctx.Idents.get("g_try_realloc_n");
-}
-
-bool MemFunctionInfoTy::isMemFunction(const FunctionDecl *FD,
- ASTContext &C) const {
- if (isCMemFunction(FD, C, AF_Malloc, MemoryOperationKind::MOK_Any))
- return true;
-
- if (isCMemFunction(FD, C, AF_IfNameIndex, MemoryOperationKind::MOK_Any))
- return true;
-
- if (isCMemFunction(FD, C, AF_Alloca, MemoryOperationKind::MOK_Any))
- return true;
-
- if (isStandardNewDelete(FD, C))
- return true;
-
- return false;
-}
-
-bool MemFunctionInfoTy::isCMemFunction(const FunctionDecl *FD, ASTContext &C,
- AllocationFamily Family,
- MemoryOperationKind MemKind) const {
+static bool isStandardNewDelete(const FunctionDecl *FD) {
if (!FD)
return false;
- bool CheckFree = (MemKind == MemoryOperationKind::MOK_Any ||
- MemKind == MemoryOperationKind::MOK_Free);
- bool CheckAlloc = (MemKind == MemoryOperationKind::MOK_Any ||
- MemKind == MemoryOperationKind::MOK_Allocate);
-
- if (FD->getKind() == Decl::Function) {
- const IdentifierInfo *FunI = FD->getIdentifier();
- initIdentifierInfo(C);
-
- if (Family == AF_Malloc && CheckFree) {
- if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf ||
- FunI == II_g_free || FunI == II_kfree)
- return true;
- }
-
- if (Family == AF_Malloc && CheckAlloc) {
- if (FunI == II_malloc || FunI == II_realloc || FunI == II_reallocf ||
- FunI == II_calloc || FunI == II_valloc || FunI == II_strdup ||
- FunI == II_win_strdup || FunI == II_strndup || FunI == II_wcsdup ||
- FunI == II_win_wcsdup || FunI == II_kmalloc ||
- FunI == II_g_malloc || FunI == II_g_malloc0 ||
- FunI == II_g_realloc || FunI == II_g_try_malloc ||
- FunI == II_g_try_malloc0 || FunI == II_g_try_realloc ||
- FunI == II_g_memdup || FunI == II_g_malloc_n ||
- FunI == II_g_malloc0_n || FunI == II_g_realloc_n ||
- FunI == II_g_try_malloc_n || FunI == II_g_try_malloc0_n ||
- FunI == II_g_try_realloc_n)
- return true;
- }
-
- if (Family == AF_IfNameIndex && CheckFree) {
- if (FunI == II_if_freenameindex)
- return true;
- }
+ OverloadedOperatorKind Kind = FD->getOverloadedOperator();
+ if (Kind != OO_New && Kind != OO_Array_New && Kind != OO_Delete &&
+ Kind != OO_Array_Delete)
+ return false;
- if (Family == AF_IfNameIndex && CheckAlloc) {
- if (FunI == II_if_nameindex)
- return true;
- }
+ // This is standard if and only if it's not defined in a user file.
+ SourceLocation L = FD->getLocation();
+ // If the header for operator delete is not included, it's still defined
+ // in an invalid source location. Check to make sure we don't crash.
+ return !L.isValid() ||
+ FD->getASTContext().getSourceManager().isInSystemHeader(L);
+}
- if (Family == AF_Alloca && CheckAlloc) {
- if (FunI == II_alloca || FunI == II_win_alloca)
- return true;
- }
- }
+//===----------------------------------------------------------------------===//
+// Methods of MallocChecker and MallocBugVisitor.
+//===----------------------------------------------------------------------===//
- if (Family != AF_Malloc)
- return false;
+bool MallocChecker::isFreeingCall(const CallEvent &Call) const {
+ if (FreeingMemFnMap.lookup(Call) || ReallocatingMemFnMap.lookup(Call))
+ return true;
- if (ShouldIncludeOwnershipAnnotatedFunctions && FD->hasAttrs()) {
- for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
+ const auto *Func = dyn_cast<FunctionDecl>(Call.getDecl());
+ if (Func && Func->hasAttrs()) {
+ for (const auto *I : Func->specific_attrs<OwnershipAttr>()) {
OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind();
- if(OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds) {
- if (CheckFree)
- return true;
- } else if (OwnKind == OwnershipAttr::Returns) {
- if (CheckAlloc)
- return true;
- }
+ if (OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds)
+ return true;
}
}
-
return false;
}
-bool MemFunctionInfoTy::isStandardNewDelete(const FunctionDecl *FD,
- ASTContext &C) const {
- if (!FD)
- return false;
- OverloadedOperatorKind Kind = FD->getOverloadedOperator();
- if (Kind != OO_New && Kind != OO_Array_New &&
- Kind != OO_Delete && Kind != OO_Array_Delete)
+bool MallocChecker::isMemCall(const CallEvent &Call) const {
+ if (FreeingMemFnMap.lookup(Call) || AllocatingMemFnMap.lookup(Call) ||
+ ReallocatingMemFnMap.lookup(Call))
+ return true;
+
+ if (!ShouldIncludeOwnershipAnnotatedFunctions)
return false;
- // This is standard if and only if it's not defined in a user file.
- SourceLocation L = FD->getLocation();
- // If the header for operator delete is not included, it's still defined
- // in an invalid source location. Check to make sure we don't crash.
- return !L.isValid() || C.getSourceManager().isInSystemHeader(L);
+ const auto *Func = dyn_cast<FunctionDecl>(Call.getDecl());
+ return Func && Func->hasAttr<OwnershipAttr>();
}
-//===----------------------------------------------------------------------===//
-// Methods of MallocChecker and MallocBugVisitor.
-//===----------------------------------------------------------------------===//
-
-llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc(
- const CallExpr *CE, CheckerContext &C, const ProgramStateRef &State) const {
+llvm::Optional<ProgramStateRef>
+MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C,
+ const ProgramStateRef &State) const {
// 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels:
//
// void *malloc(unsigned long size, struct malloc_type *mtp, int flags);
@@ -1006,10 +969,10 @@ llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc(
// We treat the last argument as the flags argument, and callers fall-back to
// normal malloc on a None return. This works for the FreeBSD kernel malloc
// as well as Linux kmalloc.
- if (CE->getNumArgs() < 2)
+ if (Call.getNumArgs() < 2)
return None;
- const Expr *FlagsEx = CE->getArg(CE->getNumArgs() - 1);
+ const Expr *FlagsEx = Call.getArgExpr(Call.getNumArgs() - 1);
const SVal V = C.getSVal(FlagsEx);
if (!V.getAs<NonLoc>()) {
// The case where 'V' can be a location can only be due to a bad header,
@@ -1035,7 +998,8 @@ llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc(
// If M_ZERO is set, treat this like calloc (initialized).
if (TrueState && !FalseState) {
SVal ZeroVal = C.getSValBuilder().makeZeroVal(Ctx.CharTy);
- return MallocMemAux(C, CE, CE->getArg(0), ZeroVal, TrueState);
+ return MallocMemAux(C, Call, Call.getArgExpr(0), ZeroVal, TrueState,
+ AF_Malloc);
}
return None;
@@ -1052,161 +1016,234 @@ SVal MallocChecker::evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
return TotalSize;
}
-void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
- if (C.wasInlined)
+void MallocChecker::checkBasicAlloc(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State,
+ AF_Malloc);
+ State = ProcessZeroAllocCheck(Call, 0, State);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkKernelMalloc(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ llvm::Optional<ProgramStateRef> MaybeState =
+ performKernelMalloc(Call, C, State);
+ if (MaybeState.hasValue())
+ State = MaybeState.getValue();
+ else
+ State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State,
+ AF_Malloc);
+ C.addTransition(State);
+}
+
+static bool isStandardRealloc(const CallEvent &Call) {
+ const FunctionDecl *FD = dyn_cast<FunctionDecl>(Call.getDecl());
+ assert(FD);
+ ASTContext &AC = FD->getASTContext();
+
+ if (isa<CXXMethodDecl>(FD))
+ return false;
+
+ return FD->getDeclaredReturnType().getDesugaredType(AC) == AC.VoidPtrTy &&
+ FD->getParamDecl(0)->getType().getDesugaredType(AC) == AC.VoidPtrTy &&
+ FD->getParamDecl(1)->getType().getDesugaredType(AC) ==
+ AC.getSizeType();
+}
+
+static bool isGRealloc(const CallEvent &Call) {
+ const FunctionDecl *FD = dyn_cast<FunctionDecl>(Call.getDecl());
+ assert(FD);
+ ASTContext &AC = FD->getASTContext();
+
+ if (isa<CXXMethodDecl>(FD))
+ return false;
+
+ return FD->getDeclaredReturnType().getDesugaredType(AC) == AC.VoidPtrTy &&
+ FD->getParamDecl(0)->getType().getDesugaredType(AC) == AC.VoidPtrTy &&
+ FD->getParamDecl(1)->getType().getDesugaredType(AC) ==
+ AC.UnsignedLongTy;
+}
+
+void MallocChecker::checkRealloc(const CallEvent &Call, CheckerContext &C,
+ bool ShouldFreeOnFail) const {
+ // HACK: CallDescription currently recognizes non-standard realloc functions
+ // as standard because it doesn't check the type, or wether its a non-method
+ // function. This should be solved by making CallDescription smarter.
+ // Mind that this came from a bug report, and all other functions suffer from
+ // this.
+ // https://bugs.llvm.org/show_bug.cgi?id=46253
+ if (!isStandardRealloc(Call) && !isGRealloc(Call))
return;
+ ProgramStateRef State = C.getState();
+ State = ReallocMemAux(C, Call, ShouldFreeOnFail, State, AF_Malloc);
+ State = ProcessZeroAllocCheck(Call, 1, State);
+ C.addTransition(State);
+}
- const FunctionDecl *FD = C.getCalleeDecl(CE);
- if (!FD)
+void MallocChecker::checkCalloc(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ State = CallocMem(C, Call, State);
+ State = ProcessZeroAllocCheck(Call, 0, State);
+ State = ProcessZeroAllocCheck(Call, 1, State);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkFree(const CallEvent &Call, CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ bool IsKnownToBeAllocatedMemory = false;
+ if (suppressDeallocationsInSuspiciousContexts(Call, C))
return;
+ State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,
+ AF_Malloc);
+ C.addTransition(State);
+}
+void MallocChecker::checkAlloca(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State,
+ AF_Alloca);
+ State = ProcessZeroAllocCheck(Call, 0, State);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkStrdup(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+ State = MallocUpdateRefState(C, CE, State, AF_Malloc);
+
+ C.addTransition(State);
+}
+
+void MallocChecker::checkIfNameIndex(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ // Should we model this differently? We can allocate a fixed number of
+ // elements with zeros in the last one.
+ State =
+ MallocMemAux(C, Call, UnknownVal(), UnknownVal(), State, AF_IfNameIndex);
+
+ C.addTransition(State);
+}
+
+void MallocChecker::checkIfFreeNameIndex(const CallEvent &Call,
+ CheckerContext &C) const {
ProgramStateRef State = C.getState();
bool IsKnownToBeAllocatedMemory = false;
+ State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,
+ AF_IfNameIndex);
+ C.addTransition(State);
+}
- if (FD->getKind() == Decl::Function) {
- MemFunctionInfo.initIdentifierInfo(C.getASTContext());
- IdentifierInfo *FunI = FD->getIdentifier();
+void MallocChecker::checkCXXNewOrCXXDelete(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ bool IsKnownToBeAllocatedMemory = false;
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
- if (FunI == MemFunctionInfo.II_malloc ||
- FunI == MemFunctionInfo.II_g_malloc ||
- FunI == MemFunctionInfo.II_g_try_malloc) {
- switch (CE->getNumArgs()) {
- default:
- return;
- case 1:
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- break;
- case 2:
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- break;
- case 3:
- llvm::Optional<ProgramStateRef> MaybeState =
- performKernelMalloc(CE, C, State);
- if (MaybeState.hasValue())
- State = MaybeState.getValue();
- else
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- break;
- }
- } else if (FunI == MemFunctionInfo.II_kmalloc) {
- if (CE->getNumArgs() < 1)
- return;
- llvm::Optional<ProgramStateRef> MaybeState =
- performKernelMalloc(CE, C, State);
- if (MaybeState.hasValue())
- State = MaybeState.getValue();
- else
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- } else if (FunI == MemFunctionInfo.II_valloc) {
- if (CE->getNumArgs() < 1)
- return;
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- } else if (FunI == MemFunctionInfo.II_realloc ||
- FunI == MemFunctionInfo.II_g_realloc ||
- FunI == MemFunctionInfo.II_g_try_realloc) {
- State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ false, State);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- } else if (FunI == MemFunctionInfo.II_reallocf) {
- State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ true, State);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- } else if (FunI == MemFunctionInfo.II_calloc) {
- State = CallocMem(C, CE, State);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- } else if (FunI == MemFunctionInfo.II_free ||
- FunI == MemFunctionInfo.II_g_free ||
- FunI == MemFunctionInfo.II_kfree) {
- if (suppressDeallocationsInSuspiciousContexts(CE, C))
- return;
+ assert(isStandardNewDelete(Call));
- State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory);
- } else if (FunI == MemFunctionInfo.II_strdup ||
- FunI == MemFunctionInfo.II_win_strdup ||
- FunI == MemFunctionInfo.II_wcsdup ||
- FunI == MemFunctionInfo.II_win_wcsdup) {
- State = MallocUpdateRefState(C, CE, State);
- } else if (FunI == MemFunctionInfo.II_strndup) {
- State = MallocUpdateRefState(C, CE, State);
- } else if (FunI == MemFunctionInfo.II_alloca ||
- FunI == MemFunctionInfo.II_win_alloca) {
- if (CE->getNumArgs() < 1)
- return;
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
- AF_Alloca);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- } else if (MemFunctionInfo.isStandardNewDelete(FD, C.getASTContext())) {
- // Process direct calls to operator new/new[]/delete/delete[] functions
- // as distinct from new/new[]/delete/delete[] expressions that are
- // processed by the checkPostStmt callbacks for CXXNewExpr and
- // CXXDeleteExpr.
- switch (FD->getOverloadedOperator()) {
- case OO_New:
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
- AF_CXXNew);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- break;
- case OO_Array_New:
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
- AF_CXXNewArray);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- break;
- case OO_Delete:
- case OO_Array_Delete:
- State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory);
- break;
- default:
- llvm_unreachable("not a new/delete operator");
- }
- } else if (FunI == MemFunctionInfo.II_if_nameindex) {
- // Should we model this differently? We can allocate a fixed number of
- // elements with zeros in the last one.
- State = MallocMemAux(C, CE, UnknownVal(), UnknownVal(), State,
- AF_IfNameIndex);
- } else if (FunI == MemFunctionInfo.II_if_freenameindex) {
- State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory);
- } else if (FunI == MemFunctionInfo.II_g_malloc0 ||
- FunI == MemFunctionInfo.II_g_try_malloc0) {
- if (CE->getNumArgs() < 1)
- return;
- SValBuilder &svalBuilder = C.getSValBuilder();
- SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
- State = MallocMemAux(C, CE, CE->getArg(0), zeroVal, State);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- } else if (FunI == MemFunctionInfo.II_g_memdup) {
- if (CE->getNumArgs() < 2)
- return;
- State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- } else if (FunI == MemFunctionInfo.II_g_malloc_n ||
- FunI == MemFunctionInfo.II_g_try_malloc_n ||
- FunI == MemFunctionInfo.II_g_malloc0_n ||
- FunI == MemFunctionInfo.II_g_try_malloc0_n) {
- if (CE->getNumArgs() < 2)
- return;
- SVal Init = UndefinedVal();
- if (FunI == MemFunctionInfo.II_g_malloc0_n ||
- FunI == MemFunctionInfo.II_g_try_malloc0_n) {
- SValBuilder &SB = C.getSValBuilder();
- Init = SB.makeZeroVal(SB.getContext().CharTy);
- }
- SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1));
- State = MallocMemAux(C, CE, TotalSize, Init, State);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- } else if (FunI == MemFunctionInfo.II_g_realloc_n ||
- FunI == MemFunctionInfo.II_g_try_realloc_n) {
- if (CE->getNumArgs() < 3)
- return;
- State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ false, State,
- /*SuffixWithN*/ true);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- State = ProcessZeroAllocCheck(C, CE, 2, State);
- }
+ // Process direct calls to operator new/new[]/delete/delete[] functions
+ // as distinct from new/new[]/delete/delete[] expressions that are
+ // processed by the checkPostStmt callbacks for CXXNewExpr and
+ // CXXDeleteExpr.
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ switch (FD->getOverloadedOperator()) {
+ case OO_New:
+ State =
+ MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State, AF_CXXNew);
+ State = ProcessZeroAllocCheck(Call, 0, State);
+ break;
+ case OO_Array_New:
+ State = MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State,
+ AF_CXXNewArray);
+ State = ProcessZeroAllocCheck(Call, 0, State);
+ break;
+ case OO_Delete:
+ State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,
+ AF_CXXNew);
+ break;
+ case OO_Array_Delete:
+ State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,
+ AF_CXXNewArray);
+ break;
+ default:
+ llvm_unreachable("not a new/delete operator");
}
- if (MemFunctionInfo.ShouldIncludeOwnershipAnnotatedFunctions ||
+ C.addTransition(State);
+}
+
+void MallocChecker::checkGMalloc0(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
+ State = MallocMemAux(C, Call, Call.getArgExpr(0), zeroVal, State, AF_Malloc);
+ State = ProcessZeroAllocCheck(Call, 0, State);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkGMemdup(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ State = MallocMemAux(C, Call, Call.getArgExpr(1), UndefinedVal(), State,
+ AF_Malloc);
+ State = ProcessZeroAllocCheck(Call, 1, State);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkGMallocN(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SVal Init = UndefinedVal();
+ SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
+ State = MallocMemAux(C, Call, TotalSize, Init, State, AF_Malloc);
+ State = ProcessZeroAllocCheck(Call, 0, State);
+ State = ProcessZeroAllocCheck(Call, 1, State);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkGMallocN0(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SValBuilder &SB = C.getSValBuilder();
+ SVal Init = SB.makeZeroVal(SB.getContext().CharTy);
+ SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
+ State = MallocMemAux(C, Call, TotalSize, Init, State, AF_Malloc);
+ State = ProcessZeroAllocCheck(Call, 0, State);
+ State = ProcessZeroAllocCheck(Call, 1, State);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkReallocN(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ State = ReallocMemAux(C, Call, /*ShouldFreeOnFail=*/false, State, AF_Malloc,
+ /*SuffixWithN=*/true);
+ State = ProcessZeroAllocCheck(Call, 1, State);
+ State = ProcessZeroAllocCheck(Call, 2, State);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkOwnershipAttr(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ if (!FD)
+ return;
+ if (ShouldIncludeOwnershipAnnotatedFunctions ||
ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
// Check all the attributes, if there are any.
// There can be multiple of these attributes.
@@ -1214,11 +1251,11 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
switch (I->getOwnKind()) {
case OwnershipAttr::Returns:
- State = MallocMemReturnsAttr(C, CE, I, State);
+ State = MallocMemReturnsAttr(C, Call, I, State);
break;
case OwnershipAttr::Takes:
case OwnershipAttr::Holds:
- State = FreeMemAttr(C, CE, I, State);
+ State = FreeMemAttr(C, Call, I, State);
break;
}
}
@@ -1226,40 +1263,73 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
C.addTransition(State);
}
+void MallocChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (C.wasInlined)
+ return;
+ if (!Call.getOriginExpr())
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ if (const CheckFn *Callback = FreeingMemFnMap.lookup(Call)) {
+ (*Callback)(this, Call, C);
+ return;
+ }
+
+ if (const CheckFn *Callback = AllocatingMemFnMap.lookup(Call)) {
+ (*Callback)(this, Call, C);
+ return;
+ }
+
+ if (const CheckFn *Callback = ReallocatingMemFnMap.lookup(Call)) {
+ (*Callback)(this, Call, C);
+ return;
+ }
+
+ if (isStandardNewDelete(Call)) {
+ checkCXXNewOrCXXDelete(Call, C);
+ return;
+ }
+
+ checkOwnershipAttr(Call, C);
+}
+
// Performs a 0-sized allocations check.
ProgramStateRef MallocChecker::ProcessZeroAllocCheck(
- CheckerContext &C, const Expr *E, const unsigned IndexOfSizeArg,
- ProgramStateRef State, Optional<SVal> RetVal) {
+ const CallEvent &Call, const unsigned IndexOfSizeArg, ProgramStateRef State,
+ Optional<SVal> RetVal) {
if (!State)
return nullptr;
if (!RetVal)
- RetVal = C.getSVal(E);
+ RetVal = Call.getReturnValue();
const Expr *Arg = nullptr;
- if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+ if (const CallExpr *CE = dyn_cast<CallExpr>(Call.getOriginExpr())) {
Arg = CE->getArg(IndexOfSizeArg);
- }
- else if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) {
- if (NE->isArray())
+ } else if (const CXXNewExpr *NE =
+ dyn_cast<CXXNewExpr>(Call.getOriginExpr())) {
+ if (NE->isArray()) {
Arg = *NE->getArraySize();
- else
+ } else {
return State;
- }
- else
+ }
+ } else
llvm_unreachable("not a CallExpr or CXXNewExpr");
assert(Arg);
- Optional<DefinedSVal> DefArgVal = C.getSVal(Arg).getAs<DefinedSVal>();
+ auto DefArgVal =
+ State->getSVal(Arg, Call.getLocationContext()).getAs<DefinedSVal>();
if (!DefArgVal)
return State;
// Check if the allocation size is 0.
ProgramStateRef TrueState, FalseState;
- SValBuilder &SvalBuilder = C.getSValBuilder();
+ SValBuilder &SvalBuilder = State->getStateManager().getSValBuilder();
DefinedSVal Zero =
SvalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>();
@@ -1330,44 +1400,43 @@ static bool hasNonTrivialConstructorCall(const CXXNewExpr *NE) {
return false;
}
-void MallocChecker::processNewAllocation(const CXXNewExpr *NE,
- CheckerContext &C,
- SVal Target) const {
- if (!MemFunctionInfo.isStandardNewDelete(NE->getOperatorNew(),
- C.getASTContext()))
- return;
+ProgramStateRef
+MallocChecker::processNewAllocation(const CXXAllocatorCall &Call,
+ CheckerContext &C,
+ AllocationFamily Family) const {
+ if (!isStandardNewDelete(Call))
+ return nullptr;
+ const CXXNewExpr *NE = Call.getOriginExpr();
const ParentMap &PM = C.getLocationContext()->getParentMap();
+ ProgramStateRef State = C.getState();
// Non-trivial constructors have a chance to escape 'this', but marking all
// invocations of trivial constructors as escaped would cause too great of
// reduction of true positives, so let's just do that for constructors that
// have an argument of a pointer-to-record type.
if (!PM.isConsumedExpr(NE) && hasNonTrivialConstructorCall(NE))
- return;
+ return State;
- ProgramStateRef State = C.getState();
// The return value from operator new is bound to a specified initialization
// value (if any) and we don't want to loose this value. So we call
// MallocUpdateRefState() instead of MallocMemAux() which breaks the
// existing binding.
- State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray
- : AF_CXXNew, Target);
+ SVal Target = Call.getObjectUnderConstruction();
+ State = MallocUpdateRefState(C, NE, State, Family, Target);
State = addExtentSize(C, NE, State, Target);
- State = ProcessZeroAllocCheck(C, NE, 0, State, Target);
- C.addTransition(State);
-}
-
-void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
- CheckerContext &C) const {
- if (!C.getAnalysisManager().getAnalyzerOptions().MayInlineCXXAllocator)
- processNewAllocation(NE, C, C.getSVal(NE));
+ State = ProcessZeroAllocCheck(Call, 0, State, Target);
+ return State;
}
-void MallocChecker::checkNewAllocator(const CXXNewExpr *NE, SVal Target,
+void MallocChecker::checkNewAllocator(const CXXAllocatorCall &Call,
CheckerContext &C) const {
- if (!C.wasInlined)
- processNewAllocation(NE, C, Target);
+ if (!C.wasInlined) {
+ ProgramStateRef State = processNewAllocation(
+ Call, C,
+ (Call.getOriginExpr()->isArray() ? AF_CXXNewArray : AF_CXXNew));
+ C.addTransition(State);
+ }
}
// Sets the extent value of the MemRegion allocated by
@@ -1402,38 +1471,20 @@ ProgramStateRef MallocChecker::addExtentSize(CheckerContext &C,
CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType);
if (ElementCount.getAs<NonLoc>()) {
- DefinedOrUnknownSVal Extent = Region->getExtent(svalBuilder);
+ DefinedOrUnknownSVal DynSize = getDynamicSize(State, Region, svalBuilder);
+
// size in Bytes = ElementCount*TypeSize
SVal SizeInBytes = svalBuilder.evalBinOpNN(
State, BO_Mul, ElementCount.castAs<NonLoc>(),
svalBuilder.makeArrayIndex(TypeSize.getQuantity()),
svalBuilder.getArrayIndexType());
- DefinedOrUnknownSVal extentMatchesSize = svalBuilder.evalEQ(
- State, Extent, SizeInBytes.castAs<DefinedOrUnknownSVal>());
- State = State->assume(extentMatchesSize, true);
+ DefinedOrUnknownSVal DynSizeMatchesSize = svalBuilder.evalEQ(
+ State, DynSize, SizeInBytes.castAs<DefinedOrUnknownSVal>());
+ State = State->assume(DynSizeMatchesSize, true);
}
return State;
}
-void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE,
- CheckerContext &C) const {
-
- if (!ChecksEnabled[CK_NewDeleteChecker])
- if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol())
- checkUseAfterFree(Sym, C, DE->getArgument());
-
- if (!MemFunctionInfo.isStandardNewDelete(DE->getOperatorDelete(),
- C.getASTContext()))
- return;
-
- ProgramStateRef State = C.getState();
- bool IsKnownToBeAllocated;
- State = FreeMemAux(C, DE->getArgument(), DE, State,
- /*Hold*/ false, IsKnownToBeAllocated);
-
- C.addTransition(State);
-}
-
static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) {
// If the first selector piece is one of the names below, assume that the
// object takes ownership of the memory, promising to eventually deallocate it
@@ -1474,50 +1525,52 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
bool IsKnownToBeAllocatedMemory;
ProgramStateRef State =
- FreeMemAux(C, Call.getArgExpr(0), Call.getOriginExpr(), C.getState(),
- /*Hold=*/true, IsKnownToBeAllocatedMemory,
+ FreeMemAux(C, Call.getArgExpr(0), Call, C.getState(),
+ /*Hold=*/true, IsKnownToBeAllocatedMemory, AF_Malloc,
/*RetNullOnFailure=*/true);
C.addTransition(State);
}
ProgramStateRef
-MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
+MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call,
const OwnershipAttr *Att,
ProgramStateRef State) const {
if (!State)
return nullptr;
- if (Att->getModule() != MemFunctionInfo.II_malloc)
+ if (Att->getModule()->getName() != "malloc")
return nullptr;
OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
if (I != E) {
- return MallocMemAux(C, CE, CE->getArg(I->getASTIndex()), UndefinedVal(),
- State);
+ return MallocMemAux(C, Call, Call.getArgExpr(I->getASTIndex()),
+ UndefinedVal(), State, AF_Malloc);
}
- return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), State);
+ return MallocMemAux(C, Call, UnknownVal(), UndefinedVal(), State, AF_Malloc);
}
ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
- const CallExpr *CE,
+ const CallEvent &Call,
const Expr *SizeEx, SVal Init,
ProgramStateRef State,
AllocationFamily Family) {
if (!State)
return nullptr;
- return MallocMemAux(C, CE, C.getSVal(SizeEx), Init, State, Family);
+ assert(SizeEx);
+ return MallocMemAux(C, Call, C.getSVal(SizeEx), Init, State, Family);
}
ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
- const CallExpr *CE,
- SVal Size, SVal Init,
- ProgramStateRef State,
- AllocationFamily Family) {
+ const CallEvent &Call, SVal Size,
+ SVal Init, ProgramStateRef State,
+ AllocationFamily Family) {
if (!State)
return nullptr;
+ const Expr *CE = Call.getOriginExpr();
+
// We expect the malloc functions to return a pointer.
if (!Loc::isLocType(CE->getType()))
return nullptr;
@@ -1542,12 +1595,12 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
return nullptr;
if (Optional<DefinedOrUnknownSVal> DefinedSize =
Size.getAs<DefinedOrUnknownSVal>()) {
- SValBuilder &svalBuilder = C.getSValBuilder();
- DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder);
- DefinedOrUnknownSVal extentMatchesSize =
- svalBuilder.evalEQ(State, Extent, *DefinedSize);
+ DefinedOrUnknownSVal DynSize = getDynamicSize(State, R, svalBuilder);
+
+ DefinedOrUnknownSVal DynSizeMatchesSize =
+ svalBuilder.evalEQ(State, DynSize, *DefinedSize);
- State = State->assume(extentMatchesSize, true);
+ State = State->assume(DynSizeMatchesSize, true);
assert(State);
}
@@ -1579,39 +1632,42 @@ static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E,
}
ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
- const CallExpr *CE,
+ const CallEvent &Call,
const OwnershipAttr *Att,
ProgramStateRef State) const {
if (!State)
return nullptr;
- if (Att->getModule() != MemFunctionInfo.II_malloc)
+ if (Att->getModule()->getName() != "malloc")
return nullptr;
bool IsKnownToBeAllocated = false;
for (const auto &Arg : Att->args()) {
- ProgramStateRef StateI = FreeMemAux(
- C, CE, State, Arg.getASTIndex(),
- Att->getOwnKind() == OwnershipAttr::Holds, IsKnownToBeAllocated);
+ ProgramStateRef StateI =
+ FreeMemAux(C, Call, State, Arg.getASTIndex(),
+ Att->getOwnKind() == OwnershipAttr::Holds,
+ IsKnownToBeAllocated, AF_Malloc);
if (StateI)
State = StateI;
}
return State;
}
-ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE,
+ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
+ const CallEvent &Call,
ProgramStateRef State, unsigned Num,
bool Hold, bool &IsKnownToBeAllocated,
+ AllocationFamily Family,
bool ReturnsNullOnFailure) const {
if (!State)
return nullptr;
- if (CE->getNumArgs() < (Num + 1))
+ if (Call.getNumArgs() < (Num + 1))
return nullptr;
- return FreeMemAux(C, CE->getArg(Num), CE, State, Hold, IsKnownToBeAllocated,
- ReturnsNullOnFailure);
+ return FreeMemAux(C, Call.getArgExpr(Num), Call, State, Hold,
+ IsKnownToBeAllocated, Family, ReturnsNullOnFailure);
}
/// Checks if the previous call to free on the given symbol failed - if free
@@ -1629,58 +1685,7 @@ static bool didPreviousFreeFail(ProgramStateRef State,
return false;
}
-static AllocationFamily
-getAllocationFamily(const MemFunctionInfoTy &MemFunctionInfo, CheckerContext &C,
- const Stmt *S) {
-
- if (!S)
- return AF_None;
-
- if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
- const FunctionDecl *FD = C.getCalleeDecl(CE);
-
- if (!FD)
- FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
-
- ASTContext &Ctx = C.getASTContext();
-
- if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Malloc,
- MemoryOperationKind::MOK_Any))
- return AF_Malloc;
-
- if (MemFunctionInfo.isStandardNewDelete(FD, Ctx)) {
- OverloadedOperatorKind Kind = FD->getOverloadedOperator();
- if (Kind == OO_New || Kind == OO_Delete)
- return AF_CXXNew;
- else if (Kind == OO_Array_New || Kind == OO_Array_Delete)
- return AF_CXXNewArray;
- }
-
- if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_IfNameIndex,
- MemoryOperationKind::MOK_Any))
- return AF_IfNameIndex;
-
- if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Alloca,
- MemoryOperationKind::MOK_Any))
- return AF_Alloca;
-
- return AF_None;
- }
-
- if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(S))
- return NE->isArray() ? AF_CXXNewArray : AF_CXXNew;
-
- if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(S))
- return DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew;
-
- if (isa<ObjCMessageExpr>(S))
- return AF_Malloc;
-
- return AF_None;
-}
-
-static bool printAllocDeallocName(raw_ostream &os, CheckerContext &C,
- const Expr *E) {
+static bool printMemFnName(raw_ostream &os, CheckerContext &C, const Expr *E) {
if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
// FIXME: This doesn't handle indirect calls.
const FunctionDecl *FD = CE->getDirectCallee();
@@ -1719,10 +1724,7 @@ static bool printAllocDeallocName(raw_ostream &os, CheckerContext &C,
return false;
}
-static void printExpectedAllocName(raw_ostream &os,
- const MemFunctionInfoTy &MemFunctionInfo,
- CheckerContext &C, const Expr *E) {
- AllocationFamily Family = getAllocationFamily(MemFunctionInfo, C, E);
+static void printExpectedAllocName(raw_ostream &os, AllocationFamily Family) {
switch(Family) {
case AF_Malloc: os << "malloc()"; return;
@@ -1747,12 +1749,10 @@ static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) {
}
}
-ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
- const Expr *ArgExpr,
- const Expr *ParentExpr,
- ProgramStateRef State, bool Hold,
- bool &IsKnownToBeAllocated,
- bool ReturnsNullOnFailure) const {
+ProgramStateRef MallocChecker::FreeMemAux(
+ CheckerContext &C, const Expr *ArgExpr, const CallEvent &Call,
+ ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated,
+ AllocationFamily Family, bool ReturnsNullOnFailure) const {
if (!State)
return nullptr;
@@ -1778,11 +1778,28 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
return nullptr;
const MemRegion *R = ArgVal.getAsRegion();
+ const Expr *ParentExpr = Call.getOriginExpr();
+
+ // NOTE: We detected a bug, but the checker under whose name we would emit the
+ // error could be disabled. Generally speaking, the MallocChecker family is an
+ // integral part of the Static Analyzer, and disabling any part of it should
+ // only be done under exceptional circumstances, such as frequent false
+ // positives. If this is the case, we can reasonably believe that there are
+ // serious faults in our understanding of the source code, and even if we
+ // don't emit an warning, we should terminate further analysis with a sink
+ // node.
// Nonlocs can't be freed, of course.
// Non-region locations (labels and fixed addresses) also shouldn't be freed.
if (!R) {
- ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
+ // Exception:
+ // If the macro ZERO_SIZE_PTR is defined, this could be a kernel source
+ // code. In that case, the ZERO_SIZE_PTR defines a special value used for a
+ // zero-sized memory block which is allowed to be freed, despite not being a
+ // null pointer.
+ if (Family != AF_Malloc || !isArgZERO_SIZE_PTR(State, C, ArgVal))
+ HandleNonHeapDealloc(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
+ Family);
return nullptr;
}
@@ -1790,7 +1807,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
// Blocks might show up as heap data, but should not be free()d
if (isa<BlockDataRegion>(R)) {
- ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
+ HandleNonHeapDealloc(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
+ Family);
return nullptr;
}
@@ -1808,9 +1826,10 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
// False negatives are better than false positives.
if (isa<AllocaRegion>(R))
- ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange());
+ HandleFreeAlloca(C, ArgVal, ArgExpr->getSourceRange());
else
- ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
+ HandleNonHeapDealloc(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
+ Family);
return nullptr;
}
@@ -1832,14 +1851,14 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
// Memory returned by alloca() shouldn't be freed.
if (RsBase->getAllocationFamily() == AF_Alloca) {
- ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange());
+ HandleFreeAlloca(C, ArgVal, ArgExpr->getSourceRange());
return nullptr;
}
// Check for double free first.
if ((RsBase->isReleased() || RsBase->isRelinquished()) &&
!didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol)) {
- ReportDoubleFree(C, ParentExpr->getSourceRange(), RsBase->isReleased(),
+ HandleDoubleFree(C, ParentExpr->getSourceRange(), RsBase->isReleased(),
SymBase, PreviousRetStatusSymbol);
return nullptr;
@@ -1849,12 +1868,10 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
RsBase->isEscaped()) {
// Check if an expected deallocation function matches the real one.
- bool DeallocMatchesAlloc =
- RsBase->getAllocationFamily() ==
- getAllocationFamily(MemFunctionInfo, C, ParentExpr);
+ bool DeallocMatchesAlloc = RsBase->getAllocationFamily() == Family;
if (!DeallocMatchesAlloc) {
- ReportMismatchedDealloc(C, ArgExpr->getSourceRange(),
- ParentExpr, RsBase, SymBase, Hold);
+ HandleMismatchedDealloc(C, ArgExpr->getSourceRange(), ParentExpr,
+ RsBase, SymBase, Hold);
return nullptr;
}
@@ -1865,15 +1882,16 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
!Offset.hasSymbolicOffset() &&
Offset.getOffset() != 0) {
const Expr *AllocExpr = cast<Expr>(RsBase->getStmt());
- ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
- AllocExpr);
+ HandleOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
+ Family, AllocExpr);
return nullptr;
}
}
}
if (SymBase->getType()->isFunctionPointerType()) {
- ReportFunctionPointerFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
+ HandleFunctionPtrFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
+ Family);
return nullptr;
}
@@ -1891,9 +1909,12 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
}
}
- AllocationFamily Family =
- RsBase ? RsBase->getAllocationFamily()
- : getAllocationFamily(MemFunctionInfo, C, ParentExpr);
+ // If we don't know anything about this symbol, a free on it may be totally
+ // valid. If this is the case, lets assume that the allocation family of the
+ // freeing function is the same as the symbols allocation family, and go with
+ // that.
+ assert(!RsBase || (RsBase && RsBase->getAllocationFamily() == Family));
+
// Normal free.
if (Hold)
return State->set<RegionState>(SymBase,
@@ -1940,14 +1961,6 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family,
}
Optional<MallocChecker::CheckKind>
-MallocChecker::getCheckIfTracked(CheckerContext &C,
- const Stmt *AllocDeallocStmt,
- bool IsALeakCheck) const {
- return getCheckIfTracked(
- getAllocationFamily(MemFunctionInfo, C, AllocDeallocStmt), IsALeakCheck);
-}
-
-Optional<MallocChecker::CheckKind>
MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
bool IsALeakCheck) const {
if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym))
@@ -2045,16 +2058,17 @@ bool MallocChecker::SummarizeRegion(raw_ostream &os,
}
}
-void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
- SourceRange Range,
- const Expr *DeallocExpr) const {
+void MallocChecker::HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal,
+ SourceRange Range,
+ const Expr *DeallocExpr,
+ AllocationFamily Family) const {
- if (!ChecksEnabled[CK_MallocChecker] &&
- !ChecksEnabled[CK_NewDeleteChecker])
+ if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteChecker]) {
+ C.addSink();
return;
+ }
- Optional<MallocChecker::CheckKind> CheckKind =
- getCheckIfTracked(C, DeallocExpr);
+ Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
if (!CheckKind.hasValue())
return;
@@ -2071,7 +2085,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
MR = ER->getSuperRegion();
os << "Argument to ";
- if (!printAllocDeallocName(os, C, DeallocExpr))
+ if (!printMemFnName(os, C, DeallocExpr))
os << "deallocator";
os << " is ";
@@ -2082,7 +2096,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
else
os << "not memory allocated by ";
- printExpectedAllocName(os, MemFunctionInfo, C, DeallocExpr);
+ printExpectedAllocName(os, Family);
auto R = std::make_unique<PathSensitiveBugReport>(*BT_BadFree[*CheckKind],
os.str(), N);
@@ -2092,7 +2106,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
}
}
-void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
+void MallocChecker::HandleFreeAlloca(CheckerContext &C, SVal ArgVal,
SourceRange Range) const {
Optional<MallocChecker::CheckKind> CheckKind;
@@ -2101,8 +2115,10 @@ void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
CheckKind = CK_MallocChecker;
else if (ChecksEnabled[CK_MismatchedDeallocatorChecker])
CheckKind = CK_MismatchedDeallocatorChecker;
- else
+ else {
+ C.addSink();
return;
+ }
if (ExplodedNode *N = C.generateErrorNode()) {
if (!BT_FreeAlloca[*CheckKind])
@@ -2118,15 +2134,16 @@ void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
}
}
-void MallocChecker::ReportMismatchedDealloc(CheckerContext &C,
+void MallocChecker::HandleMismatchedDealloc(CheckerContext &C,
SourceRange Range,
const Expr *DeallocExpr,
- const RefState *RS,
- SymbolRef Sym,
+ const RefState *RS, SymbolRef Sym,
bool OwnershipTransferred) const {
- if (!ChecksEnabled[CK_MismatchedDeallocatorChecker])
+ if (!ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
+ C.addSink();
return;
+ }
if (ExplodedNode *N = C.generateErrorNode()) {
if (!BT_MismatchedDealloc)
@@ -2144,25 +2161,25 @@ void MallocChecker::ReportMismatchedDealloc(CheckerContext &C,
llvm::raw_svector_ostream DeallocOs(DeallocBuf);
if (OwnershipTransferred) {
- if (printAllocDeallocName(DeallocOs, C, DeallocExpr))
+ if (printMemFnName(DeallocOs, C, DeallocExpr))
os << DeallocOs.str() << " cannot";
else
os << "Cannot";
os << " take ownership of memory";
- if (printAllocDeallocName(AllocOs, C, AllocExpr))
+ if (printMemFnName(AllocOs, C, AllocExpr))
os << " allocated by " << AllocOs.str();
} else {
os << "Memory";
- if (printAllocDeallocName(AllocOs, C, AllocExpr))
+ if (printMemFnName(AllocOs, C, AllocExpr))
os << " allocated by " << AllocOs.str();
os << " should be deallocated by ";
printExpectedDeallocName(os, RS->getAllocationFamily());
- if (printAllocDeallocName(DeallocOs, C, DeallocExpr))
- os << ", not " << DeallocOs.str();
+ if (printMemFnName(DeallocOs, C, DeallocExpr))
+ os << ", not " << DeallocOs.str();
}
auto R = std::make_unique<PathSensitiveBugReport>(*BT_MismatchedDealloc,
@@ -2174,17 +2191,17 @@ void MallocChecker::ReportMismatchedDealloc(CheckerContext &C,
}
}
-void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
+void MallocChecker::HandleOffsetFree(CheckerContext &C, SVal ArgVal,
SourceRange Range, const Expr *DeallocExpr,
+ AllocationFamily Family,
const Expr *AllocExpr) const {
-
- if (!ChecksEnabled[CK_MallocChecker] &&
- !ChecksEnabled[CK_NewDeleteChecker])
+ if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteChecker]) {
+ C.addSink();
return;
+ }
- Optional<MallocChecker::CheckKind> CheckKind =
- getCheckIfTracked(C, AllocExpr);
+ Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
if (!CheckKind.hasValue())
return;
@@ -2213,14 +2230,14 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
int offsetBytes = Offset.getOffset() / C.getASTContext().getCharWidth();
os << "Argument to ";
- if (!printAllocDeallocName(os, C, DeallocExpr))
+ if (!printMemFnName(os, C, DeallocExpr))
os << "deallocator";
os << " is offset by "
<< offsetBytes
<< " "
<< ((abs(offsetBytes) > 1) ? "bytes" : "byte")
<< " from the start of ";
- if (AllocExpr && printAllocDeallocName(AllocNameOs, C, AllocExpr))
+ if (AllocExpr && printMemFnName(AllocNameOs, C, AllocExpr))
os << "memory allocated by " << AllocNameOs.str();
else
os << "allocated memory";
@@ -2232,13 +2249,14 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
C.emitReport(std::move(R));
}
-void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range,
+void MallocChecker::HandleUseAfterFree(CheckerContext &C, SourceRange Range,
SymbolRef Sym) const {
- if (!ChecksEnabled[CK_MallocChecker] &&
- !ChecksEnabled[CK_NewDeleteChecker] &&
- !ChecksEnabled[CK_InnerPointerChecker])
+ if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteChecker] &&
+ !ChecksEnabled[CK_InnerPointerChecker]) {
+ C.addSink();
return;
+ }
Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
if (!CheckKind.hasValue())
@@ -2270,13 +2288,14 @@ void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range,
}
}
-void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range,
+void MallocChecker::HandleDoubleFree(CheckerContext &C, SourceRange Range,
bool Released, SymbolRef Sym,
SymbolRef PrevSym) const {
- if (!ChecksEnabled[CK_MallocChecker] &&
- !ChecksEnabled[CK_NewDeleteChecker])
+ if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteChecker]) {
+ C.addSink();
return;
+ }
Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
if (!CheckKind.hasValue())
@@ -2301,10 +2320,12 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range,
}
}
-void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const {
+void MallocChecker::HandleDoubleDelete(CheckerContext &C, SymbolRef Sym) const {
- if (!ChecksEnabled[CK_NewDeleteChecker])
+ if (!ChecksEnabled[CK_NewDeleteChecker]) {
+ C.addSink();
return;
+ }
Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
if (!CheckKind.hasValue())
@@ -2325,13 +2346,13 @@ void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const {
}
}
-void MallocChecker::ReportUseZeroAllocated(CheckerContext &C,
- SourceRange Range,
- SymbolRef Sym) const {
+void MallocChecker::HandleUseZeroAlloc(CheckerContext &C, SourceRange Range,
+ SymbolRef Sym) const {
- if (!ChecksEnabled[CK_MallocChecker] &&
- !ChecksEnabled[CK_NewDeleteChecker])
+ if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteChecker]) {
+ C.addSink();
return;
+ }
Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
@@ -2356,13 +2377,16 @@ void MallocChecker::ReportUseZeroAllocated(CheckerContext &C,
}
}
-void MallocChecker::ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal,
- SourceRange Range,
- const Expr *FreeExpr) const {
- if (!ChecksEnabled[CK_MallocChecker])
+void MallocChecker::HandleFunctionPtrFree(CheckerContext &C, SVal ArgVal,
+ SourceRange Range,
+ const Expr *FreeExpr,
+ AllocationFamily Family) const {
+ if (!ChecksEnabled[CK_MallocChecker]) {
+ C.addSink();
return;
+ }
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, FreeExpr);
+ Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
if (!CheckKind.hasValue())
return;
@@ -2379,7 +2403,7 @@ void MallocChecker::ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal,
MR = ER->getSuperRegion();
Os << "Argument to ";
- if (!printAllocDeallocName(Os, C, FreeExpr))
+ if (!printMemFnName(Os, C, FreeExpr))
Os << "deallocator";
Os << " is a function pointer";
@@ -2392,14 +2416,15 @@ void MallocChecker::ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal,
}
}
-ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C,
- const CallExpr *CE,
- bool ShouldFreeOnFail,
- ProgramStateRef State,
- bool SuffixWithN) const {
+ProgramStateRef
+MallocChecker::ReallocMemAux(CheckerContext &C, const CallEvent &Call,
+ bool ShouldFreeOnFail, ProgramStateRef State,
+ AllocationFamily Family, bool SuffixWithN) const {
if (!State)
return nullptr;
+ const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr());
+
if (SuffixWithN && CE->getNumArgs() < 3)
return nullptr;
else if (CE->getNumArgs() < 2)
@@ -2443,21 +2468,15 @@ ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C,
// If the ptr is NULL and the size is not 0, the call is equivalent to
// malloc(size).
if (PrtIsNull && !SizeIsZero) {
- ProgramStateRef stateMalloc = MallocMemAux(C, CE, TotalSize,
- UndefinedVal(), StatePtrIsNull);
+ ProgramStateRef stateMalloc = MallocMemAux(
+ C, Call, TotalSize, UndefinedVal(), StatePtrIsNull, Family);
return stateMalloc;
}
if (PrtIsNull && SizeIsZero)
return State;
- // Get the from and to pointer symbols as in toPtr = realloc(fromPtr, size).
assert(!PrtIsNull);
- SymbolRef FromPtr = arg0Val.getAsSymbol();
- SVal RetVal = C.getSVal(CE);
- SymbolRef ToPtr = RetVal.getAsSymbol();
- if (!FromPtr || !ToPtr)
- return nullptr;
bool IsKnownToBeAllocated = false;
@@ -2467,16 +2486,16 @@ ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C,
// If size was equal to 0, either NULL or a pointer suitable to be passed
// to free() is returned. We just free the input pointer and do not add
// any constrains on the output pointer.
- if (ProgramStateRef stateFree =
- FreeMemAux(C, CE, StateSizeIsZero, 0, false, IsKnownToBeAllocated))
+ if (ProgramStateRef stateFree = FreeMemAux(
+ C, Call, StateSizeIsZero, 0, false, IsKnownToBeAllocated, Family))
return stateFree;
// Default behavior.
if (ProgramStateRef stateFree =
- FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocated)) {
+ FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocated, Family)) {
- ProgramStateRef stateRealloc = MallocMemAux(C, CE, TotalSize,
- UnknownVal(), stateFree);
+ ProgramStateRef stateRealloc =
+ MallocMemAux(C, Call, TotalSize, UnknownVal(), stateFree, Family);
if (!stateRealloc)
return nullptr;
@@ -2486,6 +2505,14 @@ ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C,
else if (!IsKnownToBeAllocated)
Kind = OAR_DoNotTrackAfterFailure;
+ // Get the from and to pointer symbols as in toPtr = realloc(fromPtr, size).
+ SymbolRef FromPtr = arg0Val.getLocSymbolInBase();
+ SVal RetVal = C.getSVal(CE);
+ SymbolRef ToPtr = RetVal.getAsSymbol();
+ assert(FromPtr && ToPtr &&
+ "By this point, FreeMemAux and MallocMemAux should have checked "
+ "whether the argument or the return value is symbolic!");
+
// Record the info about the reallocated symbol so that we could properly
// process failed reallocation.
stateRealloc = stateRealloc->set<ReallocPairs>(ToPtr,
@@ -2497,19 +2524,21 @@ ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C,
return nullptr;
}
-ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE,
+ProgramStateRef MallocChecker::CallocMem(CheckerContext &C,
+ const CallEvent &Call,
ProgramStateRef State) {
if (!State)
return nullptr;
- if (CE->getNumArgs() < 2)
+ if (Call.getNumArgs() < 2)
return nullptr;
SValBuilder &svalBuilder = C.getSValBuilder();
SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
- SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1));
+ SVal TotalSize =
+ evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
- return MallocMemAux(C, CE, TotalSize, zeroVal, State);
+ return MallocMemAux(C, Call, TotalSize, zeroVal, State, AF_Malloc);
}
MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N,
@@ -2553,7 +2582,7 @@ MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N,
return LeakInfo(AllocNode, ReferenceRegion);
}
-void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
+void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N,
CheckerContext &C) const {
if (!ChecksEnabled[CK_MallocChecker] &&
@@ -2669,7 +2698,7 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
if (N) {
for (SmallVectorImpl<SymbolRef>::iterator
I = Errors.begin(), E = Errors.end(); I != E; ++I) {
- reportLeak(*I, N, C);
+ HandleLeak(*I, N, C);
}
}
}
@@ -2680,7 +2709,27 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
void MallocChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
- if (const CXXDestructorCall *DC = dyn_cast<CXXDestructorCall>(&Call)) {
+ if (const auto *DC = dyn_cast<CXXDeallocatorCall>(&Call)) {
+ const CXXDeleteExpr *DE = DC->getOriginExpr();
+
+ if (!ChecksEnabled[CK_NewDeleteChecker])
+ if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol())
+ checkUseAfterFree(Sym, C, DE->getArgument());
+
+ if (!isStandardNewDelete(DC->getDecl()))
+ return;
+
+ ProgramStateRef State = C.getState();
+ bool IsKnownToBeAllocated;
+ State = FreeMemAux(C, DE->getArgument(), Call, State,
+ /*Hold*/ false, IsKnownToBeAllocated,
+ (DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew));
+
+ C.addTransition(State);
+ return;
+ }
+
+ if (const auto *DC = dyn_cast<CXXDestructorCall>(&Call)) {
SymbolRef Sym = DC->getCXXThisVal().getAsSymbol();
if (!Sym || checkDoubleDelete(Sym, C))
return;
@@ -2692,12 +2741,7 @@ void MallocChecker::checkPreCall(const CallEvent &Call,
if (!FD)
return;
- ASTContext &Ctx = C.getASTContext();
- if (ChecksEnabled[CK_MallocChecker] &&
- (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Malloc,
- MemoryOperationKind::MOK_Free) ||
- MemFunctionInfo.isCMemFunction(FD, Ctx, AF_IfNameIndex,
- MemoryOperationKind::MOK_Free)))
+ if (ChecksEnabled[CK_MallocChecker] && isFreeingCall(Call))
return;
}
@@ -2807,8 +2851,8 @@ static bool isReleased(SymbolRef Sym, CheckerContext &C) {
}
bool MallocChecker::suppressDeallocationsInSuspiciousContexts(
- const CallExpr *CE, CheckerContext &C) const {
- if (CE->getNumArgs() == 0)
+ const CallEvent &Call, CheckerContext &C) const {
+ if (Call.getNumArgs() == 0)
return false;
StringRef FunctionStr = "";
@@ -2826,7 +2870,7 @@ bool MallocChecker::suppressDeallocationsInSuspiciousContexts(
ProgramStateRef State = C.getState();
- for (const Expr *Arg : CE->arguments())
+ for (const Expr *Arg : cast<CallExpr>(Call.getOriginExpr())->arguments())
if (SymbolRef Sym = C.getSVal(Arg).getAsSymbol())
if (const RefState *RS = State->get<RegionState>(Sym))
State = State->set<RegionState>(Sym, RefState::getEscaped(RS));
@@ -2839,7 +2883,7 @@ bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
const Stmt *S) const {
if (isReleased(Sym, C)) {
- ReportUseAfterFree(C, S->getSourceRange(), Sym);
+ HandleUseAfterFree(C, S->getSourceRange(), Sym);
return true;
}
@@ -2852,17 +2896,17 @@ void MallocChecker::checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,
if (const RefState *RS = C.getState()->get<RegionState>(Sym)) {
if (RS->isAllocatedOfSizeZero())
- ReportUseZeroAllocated(C, RS->getStmt()->getSourceRange(), Sym);
+ HandleUseZeroAlloc(C, RS->getStmt()->getSourceRange(), Sym);
}
else if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym)) {
- ReportUseZeroAllocated(C, S->getSourceRange(), Sym);
+ HandleUseZeroAlloc(C, S->getSourceRange(), Sym);
}
}
bool MallocChecker::checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const {
if (isReleased(Sym, C)) {
- ReportDoubleDelete(C, Sym);
+ HandleDoubleDelete(C, Sym);
return true;
}
return false;
@@ -2994,11 +3038,9 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
if (!FD)
return true;
- ASTContext &ASTC = State->getStateManager().getContext();
-
// If it's one of the allocation functions we can reason about, we model
// its behavior explicitly.
- if (MemFunctionInfo.isMemFunction(FD, ASTC))
+ if (isMemCall(*Call))
return false;
// If it's not a system call, assume it frees memory.
@@ -3142,6 +3184,18 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(
return State;
}
+bool MallocChecker::isArgZERO_SIZE_PTR(ProgramStateRef State, CheckerContext &C,
+ SVal ArgVal) const {
+ if (!KernelZeroSizePtrValue)
+ KernelZeroSizePtrValue =
+ tryExpandAsInteger("ZERO_SIZE_PTR", C.getPreprocessor());
+
+ const llvm::APSInt *ArgValKnown =
+ C.getSValBuilder().getKnownValue(State, ArgVal);
+ return ArgValKnown && *KernelZeroSizePtrValue &&
+ ArgValKnown->getSExtValue() == **KernelZeroSizePtrValue;
+}
+
static SymbolRef findFailedReallocSymbol(ProgramStateRef currState,
ProgramStateRef prevState) {
ReallocPairsTy currMap = currState->get<ReallocPairs>();
@@ -3404,11 +3458,11 @@ void ento::registerInnerPointerCheckerAux(CheckerManager &mgr) {
void ento::registerDynamicMemoryModeling(CheckerManager &mgr) {
auto *checker = mgr.registerChecker<MallocChecker>();
- checker->MemFunctionInfo.ShouldIncludeOwnershipAnnotatedFunctions =
+ checker->ShouldIncludeOwnershipAnnotatedFunctions =
mgr.getAnalyzerOptions().getCheckerBooleanOption(checker, "Optimistic");
}
-bool ento::shouldRegisterDynamicMemoryModeling(const LangOptions &LO) {
+bool ento::shouldRegisterDynamicMemoryModeling(const CheckerManager &mgr) {
return true;
}
@@ -3420,7 +3474,7 @@ bool ento::shouldRegisterDynamicMemoryModeling(const LangOptions &LO) {
mgr.getCurrentCheckerName(); \
} \
\
- bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
+ bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
REGISTER_CHECKER(MallocChecker)
REGISTER_CHECKER(NewDeleteChecker)
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
index 4fd06f24c5bc..e31630f63b5a 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
@@ -337,6 +337,6 @@ void ento::registerMallocOverflowSecurityChecker(CheckerManager &mgr) {
mgr.registerChecker<MallocOverflowSecurityChecker>();
}
-bool ento::shouldRegisterMallocOverflowSecurityChecker(const LangOptions &LO) {
+bool ento::shouldRegisterMallocOverflowSecurityChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
index b5881a9e6533..71f593cb2b56 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
@@ -250,6 +250,6 @@ void ento::registerMallocSizeofChecker(CheckerManager &mgr) {
mgr.registerChecker<MallocSizeofChecker>();
}
-bool ento::shouldRegisterMallocSizeofChecker(const LangOptions &LO) {
+bool ento::shouldRegisterMallocSizeofChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp
index 143910588959..1960873599f7 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp
@@ -28,7 +28,7 @@ using namespace iterator;
namespace {
class MismatchedIteratorChecker
- : public Checker<check::PreCall> {
+ : public Checker<check::PreCall, check::PreStmt<BinaryOperator>> {
std::unique_ptr<BugType> MismatchedBugType;
@@ -47,6 +47,7 @@ public:
MismatchedIteratorChecker();
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const;
};
@@ -141,7 +142,7 @@ void MismatchedIteratorChecker::checkPreCall(const CallEvent &Call,
// Example:
// template<typename I1, typename I2>
// void f(I1 first1, I1 last1, I2 first2, I2 last2);
- //
+ //
// In this case the first two arguments to f() must be iterators must belong
// to the same container and the last to also to the same container but
// not necessarily to the same as the first two.
@@ -188,6 +189,17 @@ void MismatchedIteratorChecker::checkPreCall(const CallEvent &Call,
}
}
+void MismatchedIteratorChecker::checkPreStmt(const BinaryOperator *BO,
+ CheckerContext &C) const {
+ if (!BO->isComparisonOp())
+ return;
+
+ ProgramStateRef State = C.getState();
+ SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext());
+ SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext());
+ verifyMatch(C, LVal, RVal);
+}
+
void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
const MemRegion *Cont) const {
// Verify match between a container and the container of an iterator
@@ -290,6 +302,6 @@ void ento::registerMismatchedIteratorChecker(CheckerManager &mgr) {
mgr.registerChecker<MismatchedIteratorChecker>();
}
-bool ento::shouldRegisterMismatchedIteratorChecker(const LangOptions &LO) {
+bool ento::shouldRegisterMismatchedIteratorChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
index ceea62160545..5d63d6efd234 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
@@ -88,6 +88,6 @@ void ento::registerMmapWriteExecChecker(CheckerManager &mgr) {
.getCheckerIntegerOption(Mwec, "MmapProtRead");
}
-bool ento::shouldRegisterMmapWriteExecChecker(const LangOptions &LO) {
+bool ento::shouldRegisterMmapWriteExecChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
index 40eb113e3f8e..7f0519c695b0 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
@@ -757,6 +757,6 @@ void ento::registerMoveChecker(CheckerManager &mgr) {
mgr.getAnalyzerOptions().getCheckerStringOption(chk, "WarnOn"), mgr);
}
-bool ento::shouldRegisterMoveChecker(const LangOptions &LO) {
+bool ento::shouldRegisterMoveChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
index 41b7fe5e43b6..be17e401fb53 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
@@ -80,6 +80,7 @@ void ento::registerNSAutoreleasePoolChecker(CheckerManager &mgr) {
mgr.registerChecker<NSAutoreleasePoolChecker>();
}
-bool ento::shouldRegisterNSAutoreleasePoolChecker(const LangOptions &LO) {
+bool ento::shouldRegisterNSAutoreleasePoolChecker(const CheckerManager &mgr) {
+ const LangOptions &LO = mgr.getLangOpts();
return LO.getGC() != LangOptions::NonGC;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
index 85370bf133cd..90c5583d8969 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
@@ -95,6 +95,15 @@ public:
};
}
+static bool hasReservedReturnType(const FunctionDecl *D) {
+ if (isa<CXXConstructorDecl>(D))
+ return true;
+
+ // operators delete and delete[] are required to have 'void' return type
+ auto OperatorKind = D->getOverloadedOperator();
+ return OperatorKind == OO_Delete || OperatorKind == OO_Array_Delete;
+}
+
void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
AnalysisManager &mgr,
BugReporter &BR) const {
@@ -102,6 +111,8 @@ void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
return;
if (!D->getReturnType()->isVoidType())
return;
+ if (hasReservedReturnType(D))
+ return;
if (!II)
II = &D->getASTContext().Idents.get("CFErrorRef");
@@ -133,14 +144,14 @@ namespace {
class NSErrorDerefBug : public BugType {
public:
- NSErrorDerefBug(const CheckerBase *Checker)
+ NSErrorDerefBug(const CheckerNameRef Checker)
: BugType(Checker, "NSError** null dereference",
"Coding conventions (Apple)") {}
};
class CFErrorDerefBug : public BugType {
public:
- CFErrorDerefBug(const CheckerBase *Checker)
+ CFErrorDerefBug(const CheckerNameRef Checker)
: BugType(Checker, "CFErrorRef* null dereference",
"Coding conventions (Apple)") {}
};
@@ -155,9 +166,9 @@ class NSOrCFErrorDerefChecker
mutable std::unique_ptr<NSErrorDerefBug> NSBT;
mutable std::unique_ptr<CFErrorDerefBug> CFBT;
public:
- bool ShouldCheckNSError, ShouldCheckCFError;
- NSOrCFErrorDerefChecker() : NSErrorII(nullptr), CFErrorII(nullptr),
- ShouldCheckNSError(0), ShouldCheckCFError(0) { }
+ DefaultBool ShouldCheckNSError, ShouldCheckCFError;
+ CheckerNameRef NSErrorName, CFErrorName;
+ NSOrCFErrorDerefChecker() : NSErrorII(nullptr), CFErrorII(nullptr) {}
void checkLocation(SVal loc, bool isLoad, const Stmt *S,
CheckerContext &C) const;
@@ -265,12 +276,12 @@ void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const {
BugType *bug = nullptr;
if (isNSError) {
if (!NSBT)
- NSBT.reset(new NSErrorDerefBug(this));
+ NSBT.reset(new NSErrorDerefBug(NSErrorName));
bug = NSBT.get();
}
else {
if (!CFBT)
- CFBT.reset(new CFErrorDerefBug(this));
+ CFBT.reset(new CFErrorDerefBug(CFErrorName));
bug = CFBT.get();
}
BR.emitReport(
@@ -312,7 +323,7 @@ void ento::registerNSOrCFErrorDerefChecker(CheckerManager &mgr) {
mgr.registerChecker<NSOrCFErrorDerefChecker>();
}
-bool ento::shouldRegisterNSOrCFErrorDerefChecker(const LangOptions &LO) {
+bool ento::shouldRegisterNSOrCFErrorDerefChecker(const CheckerManager &mgr) {
return true;
}
@@ -320,9 +331,10 @@ void ento::registerNSErrorChecker(CheckerManager &mgr) {
mgr.registerChecker<NSErrorMethodChecker>();
NSOrCFErrorDerefChecker *checker = mgr.getChecker<NSOrCFErrorDerefChecker>();
checker->ShouldCheckNSError = true;
+ checker->NSErrorName = mgr.getCurrentCheckerName();
}
-bool ento::shouldRegisterNSErrorChecker(const LangOptions &LO) {
+bool ento::shouldRegisterNSErrorChecker(const CheckerManager &mgr) {
return true;
}
@@ -330,8 +342,9 @@ void ento::registerCFErrorChecker(CheckerManager &mgr) {
mgr.registerChecker<CFErrorFunctionChecker>();
NSOrCFErrorDerefChecker *checker = mgr.getChecker<NSOrCFErrorDerefChecker>();
checker->ShouldCheckCFError = true;
+ checker->CFErrorName = mgr.getCurrentCheckerName();
}
-bool ento::shouldRegisterCFErrorChecker(const LangOptions &LO) {
+bool ento::shouldRegisterCFErrorChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
index fc34255bf6c9..af208e867318 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
@@ -143,6 +143,6 @@ void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) {
mgr.registerChecker<NoReturnFunctionChecker>();
}
-bool ento::shouldRegisterNoReturnFunctionChecker(const LangOptions &LO) {
+bool ento::shouldRegisterNoReturnFunctionChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
index 6ffc89745365..534b5d68434f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
@@ -14,57 +14,97 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/Attr.h"
+#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"
+#include "llvm/ADT/StringExtras.h"
using namespace clang;
using namespace ento;
namespace {
class NonNullParamChecker
- : public Checker< check::PreCall, EventDispatcher<ImplicitNullDerefEvent> > {
+ : public Checker<check::PreCall, check::BeginFunction,
+ EventDispatcher<ImplicitNullDerefEvent>> {
mutable std::unique_ptr<BugType> BTAttrNonNull;
mutable std::unique_ptr<BugType> BTNullRefArg;
public:
-
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkBeginFunction(CheckerContext &C) const;
std::unique_ptr<PathSensitiveBugReport>
- genReportNullAttrNonNull(const ExplodedNode *ErrorN,
- const Expr *ArgE,
+ genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE,
unsigned IdxOfArg) const;
std::unique_ptr<PathSensitiveBugReport>
genReportReferenceToNullPointer(const ExplodedNode *ErrorN,
const Expr *ArgE) const;
};
-} // end anonymous namespace
-/// \return Bitvector marking non-null attributes.
-static llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) {
+template <class CallType>
+void setBitsAccordingToFunctionAttributes(const CallType &Call,
+ llvm::SmallBitVector &AttrNonNull) {
const Decl *FD = Call.getDecl();
- unsigned NumArgs = Call.getNumArgs();
- llvm::SmallBitVector AttrNonNull(NumArgs);
+
for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {
if (!NonNull->args_size()) {
- AttrNonNull.set(0, NumArgs);
+ // Lack of attribute parameters means that all of the parameters are
+ // implicitly marked as non-null.
+ AttrNonNull.set();
break;
}
+
for (const ParamIdx &Idx : NonNull->args()) {
+ // 'nonnull' attribute's parameters are 1-based and should be adjusted to
+ // match actual AST parameter/argument indices.
unsigned IdxAST = Idx.getASTIndex();
- if (IdxAST >= NumArgs)
+ if (IdxAST >= AttrNonNull.size())
continue;
AttrNonNull.set(IdxAST);
}
}
+}
+
+template <class CallType>
+void setBitsAccordingToParameterAttributes(const CallType &Call,
+ llvm::SmallBitVector &AttrNonNull) {
+ for (const ParmVarDecl *Parameter : Call.parameters()) {
+ unsigned ParameterIndex = Parameter->getFunctionScopeIndex();
+ if (ParameterIndex == AttrNonNull.size())
+ break;
+
+ if (Parameter->hasAttr<NonNullAttr>())
+ AttrNonNull.set(ParameterIndex);
+ }
+}
+
+template <class CallType>
+llvm::SmallBitVector getNonNullAttrsImpl(const CallType &Call,
+ unsigned ExpectedSize) {
+ llvm::SmallBitVector AttrNonNull(ExpectedSize);
+
+ setBitsAccordingToFunctionAttributes(Call, AttrNonNull);
+ setBitsAccordingToParameterAttributes(Call, AttrNonNull);
+
return AttrNonNull;
}
+/// \return Bitvector marking non-null attributes.
+llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) {
+ return getNonNullAttrsImpl(Call, Call.getNumArgs());
+}
+
+/// \return Bitvector marking non-null attributes.
+llvm::SmallBitVector getNonNullAttrs(const AnyCall &Call) {
+ return getNonNullAttrsImpl(Call, Call.param_size());
+}
+} // end anonymous namespace
+
void NonNullParamChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
if (!Call.getDecl())
@@ -74,7 +114,7 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
unsigned NumArgs = Call.getNumArgs();
ProgramStateRef state = C.getState();
- ArrayRef<ParmVarDecl*> parms = Call.parameters();
+ ArrayRef<ParmVarDecl *> parms = Call.parameters();
for (unsigned idx = 0; idx < NumArgs; ++idx) {
// For vararg functions, a corresponding parameter decl may not exist.
@@ -82,15 +122,11 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
// Check if the parameter is a reference. We want to report when reference
// to a null pointer is passed as a parameter.
- bool haveRefTypeParam =
+ bool HasRefTypeParam =
HasParam ? parms[idx]->getType()->isReferenceType() : false;
- bool haveAttrNonNull = AttrNonNull[idx];
+ bool ExpectedToBeNonNull = AttrNonNull.test(idx);
- // Check if the parameter is also marked 'nonnull'.
- if (!haveAttrNonNull && HasParam)
- haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>();
-
- if (!haveAttrNonNull && !haveRefTypeParam)
+ if (!ExpectedToBeNonNull && !HasRefTypeParam)
continue;
// If the value is unknown or undefined, we can't perform this check.
@@ -100,10 +136,10 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
if (!DV)
continue;
- assert(!haveRefTypeParam || DV->getAs<Loc>());
+ assert(!HasRefTypeParam || DV->getAs<Loc>());
// Process the case when the argument is not a location.
- if (haveAttrNonNull && !DV->getAs<Loc>()) {
+ if (ExpectedToBeNonNull && !DV->getAs<Loc>()) {
// If the argument is a union type, we want to handle a potential
// transparent_union GCC extension.
if (!ArgE)
@@ -144,9 +180,9 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) {
std::unique_ptr<BugReport> R;
- if (haveAttrNonNull)
+ if (ExpectedToBeNonNull)
R = genReportNullAttrNonNull(errorNode, ArgE, idx + 1);
- else if (haveRefTypeParam)
+ else if (HasRefTypeParam)
R = genReportReferenceToNullPointer(errorNode, ArgE);
// Highlight the range of the argument that was null.
@@ -163,8 +199,8 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
if (stateNull) {
if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) {
ImplicitNullDerefEvent event = {
- V, false, N, &C.getBugReporter(),
- /*IsDirectDereference=*/haveRefTypeParam};
+ V, false, N, &C.getBugReporter(),
+ /*IsDirectDereference=*/HasRefTypeParam};
dispatchEvent(event);
}
}
@@ -179,6 +215,65 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
C.addTransition(state);
}
+/// We want to trust developer annotations and consider all 'nonnull' parameters
+/// as non-null indeed. Each marked parameter will get a corresponding
+/// constraint.
+///
+/// This approach will not only help us to get rid of some false positives, but
+/// remove duplicates and shorten warning traces as well.
+///
+/// \code
+/// void foo(int *x) [[gnu::nonnull]] {
+/// // . . .
+/// *x = 42; // we don't want to consider this as an error...
+/// // . . .
+/// }
+///
+/// foo(nullptr); // ...and report here instead
+/// \endcode
+void NonNullParamChecker::checkBeginFunction(CheckerContext &Context) const {
+ // Planned assumption makes sense only for top-level functions.
+ // Inlined functions will get similar constraints as part of 'checkPreCall'.
+ if (!Context.inTopFrame())
+ return;
+
+ const LocationContext *LocContext = Context.getLocationContext();
+
+ const Decl *FD = LocContext->getDecl();
+ // AnyCall helps us here to avoid checking for FunctionDecl and ObjCMethodDecl
+ // separately and aggregates interfaces of these classes.
+ auto AbstractCall = AnyCall::forDecl(FD);
+ if (!AbstractCall)
+ return;
+
+ ProgramStateRef State = Context.getState();
+ llvm::SmallBitVector ParameterNonNullMarks = getNonNullAttrs(*AbstractCall);
+
+ for (const ParmVarDecl *Parameter : AbstractCall->parameters()) {
+ // 1. Check parameter if it is annotated as non-null
+ if (!ParameterNonNullMarks.test(Parameter->getFunctionScopeIndex()))
+ continue;
+
+ // 2. Check that parameter is a pointer.
+ // Nonnull attribute can be applied to non-pointers (by default
+ // __attribute__(nonnull) implies "all parameters").
+ if (!Parameter->getType()->isPointerType())
+ continue;
+
+ Loc ParameterLoc = State->getLValue(Parameter, LocContext);
+ // We never consider top-level function parameters undefined.
+ auto StoredVal =
+ State->getSVal(ParameterLoc).castAs<DefinedOrUnknownSVal>();
+
+ // 3. Assume that it is indeed non-null
+ if (ProgramStateRef NewState = State->assume(StoredVal, true)) {
+ State = NewState;
+ }
+ }
+
+ Context.addTransition(State);
+}
+
std::unique_ptr<PathSensitiveBugReport>
NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,
const Expr *ArgE,
@@ -226,6 +321,6 @@ void ento::registerNonNullParamChecker(CheckerManager &mgr) {
mgr.registerChecker<NonNullParamChecker>();
}
-bool ento::shouldRegisterNonNullParamChecker(const LangOptions &LO) {
+bool ento::shouldRegisterNonNullParamChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
index 6efba433eed2..80b705fb7392 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
@@ -147,6 +147,6 @@ void ento::registerNonnullGlobalConstantsChecker(CheckerManager &Mgr) {
Mgr.registerChecker<NonnullGlobalConstantsChecker>();
}
-bool ento::shouldRegisterNonnullGlobalConstantsChecker(const LangOptions &LO) {
+bool ento::shouldRegisterNonnullGlobalConstantsChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
index 922048733c7c..bc7a8a3b12a1 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
@@ -81,8 +81,7 @@ class NullabilityChecker
: public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>,
check::PostCall, check::PostStmt<ExplicitCastExpr>,
check::PostObjCMessage, check::DeadSymbols,
- check::Event<ImplicitNullDerefEvent>> {
- mutable std::unique_ptr<BugType> BT;
+ check::Location, check::Event<ImplicitNullDerefEvent>> {
public:
// If true, the checker will not diagnose nullabilility issues for calls
@@ -101,25 +100,32 @@ public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
void checkEvent(ImplicitNullDerefEvent Event) const;
+ void checkLocation(SVal Location, bool IsLoad, const Stmt *S,
+ CheckerContext &C) const;
void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
const char *Sep) const override;
- struct NullabilityChecksFilter {
- DefaultBool CheckNullPassedToNonnull;
- DefaultBool CheckNullReturnedFromNonnull;
- DefaultBool CheckNullableDereferenced;
- DefaultBool CheckNullablePassedToNonnull;
- DefaultBool CheckNullableReturnedFromNonnull;
-
- CheckerNameRef CheckNameNullPassedToNonnull;
- CheckerNameRef CheckNameNullReturnedFromNonnull;
- CheckerNameRef CheckNameNullableDereferenced;
- CheckerNameRef CheckNameNullablePassedToNonnull;
- CheckerNameRef CheckNameNullableReturnedFromNonnull;
+ enum CheckKind {
+ CK_NullPassedToNonnull,
+ CK_NullReturnedFromNonnull,
+ CK_NullableDereferenced,
+ CK_NullablePassedToNonnull,
+ CK_NullableReturnedFromNonnull,
+ CK_NumCheckKinds
};
- NullabilityChecksFilter Filter;
+ DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ CheckerNameRef CheckNames[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BTs[CK_NumCheckKinds];
+
+ const std::unique_ptr<BugType> &getBugType(CheckKind Kind) const {
+ if (!BTs[Kind])
+ BTs[Kind].reset(new BugType(CheckNames[Kind], "Nullability",
+ categories::MemoryError));
+ return BTs[Kind];
+ }
+
// When set to false no nullability information will be tracked in
// NullabilityMap. It is possible to catch errors like passing a null pointer
// to a callee that expects nonnull argument without the information that is
@@ -151,18 +157,16 @@ private:
///
/// When \p SuppressPath is set to true, no more bugs will be reported on this
/// path by this checker.
- void reportBugIfInvariantHolds(StringRef Msg, ErrorKind Error,
+ void reportBugIfInvariantHolds(StringRef Msg, ErrorKind Error, CheckKind CK,
ExplodedNode *N, const MemRegion *Region,
CheckerContext &C,
const Stmt *ValueExpr = nullptr,
- bool SuppressPath = false) const;
+ bool SuppressPath = false) const;
- void reportBug(StringRef Msg, ErrorKind Error, ExplodedNode *N,
+ void reportBug(StringRef Msg, ErrorKind Error, CheckKind CK, ExplodedNode *N,
const MemRegion *Region, BugReporter &BR,
const Stmt *ValueExpr = nullptr) const {
- if (!BT)
- BT.reset(new BugType(this, "Nullability", categories::MemoryError));
-
+ const std::unique_ptr<BugType> &BT = getBugType(CK);
auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
if (Region) {
R->markInteresting(Region);
@@ -430,9 +434,10 @@ static bool checkInvariantViolation(ProgramStateRef State, ExplodedNode *N,
return false;
}
-void NullabilityChecker::reportBugIfInvariantHolds(StringRef Msg,
- ErrorKind Error, ExplodedNode *N, const MemRegion *Region,
- CheckerContext &C, const Stmt *ValueExpr, bool SuppressPath) const {
+void NullabilityChecker::reportBugIfInvariantHolds(
+ StringRef Msg, ErrorKind Error, CheckKind CK, ExplodedNode *N,
+ const MemRegion *Region, CheckerContext &C, const Stmt *ValueExpr,
+ bool SuppressPath) const {
ProgramStateRef OriginalState = N->getState();
if (checkInvariantViolation(OriginalState, N, C))
@@ -442,7 +447,7 @@ void NullabilityChecker::reportBugIfInvariantHolds(StringRef Msg,
N = C.addTransition(OriginalState, N);
}
- reportBug(Msg, Error, N, Region, C.getBugReporter(), ValueExpr);
+ reportBug(Msg, Error, CK, N, Region, C.getBugReporter(), ValueExpr);
}
/// Cleaning up the program state.
@@ -487,34 +492,76 @@ void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const {
if (!TrackedNullability)
return;
- if (Filter.CheckNullableDereferenced &&
+ if (ChecksEnabled[CK_NullableDereferenced] &&
TrackedNullability->getValue() == Nullability::Nullable) {
BugReporter &BR = *Event.BR;
// Do not suppress errors on defensive code paths, because dereferencing
// a nullable pointer is always an error.
if (Event.IsDirectDereference)
reportBug("Nullable pointer is dereferenced",
- ErrorKind::NullableDereferenced, Event.SinkNode, Region, BR);
+ ErrorKind::NullableDereferenced, CK_NullableDereferenced,
+ Event.SinkNode, Region, BR);
else {
reportBug("Nullable pointer is passed to a callee that requires a "
- "non-null", ErrorKind::NullablePassedToNonnull,
+ "non-null",
+ ErrorKind::NullablePassedToNonnull, CK_NullableDereferenced,
Event.SinkNode, Region, BR);
}
}
}
+// Whenever we see a load from a typed memory region that's been annotated as
+// 'nonnull', we want to trust the user on that and assume that it is is indeed
+// non-null.
+//
+// We do so even if the value is known to have been assigned to null.
+// The user should be warned on assigning the null value to a non-null pointer
+// as opposed to warning on the later dereference of this pointer.
+//
+// \code
+// int * _Nonnull var = 0; // we want to warn the user here...
+// // . . .
+// *var = 42; // ...and not here
+// \endcode
+void NullabilityChecker::checkLocation(SVal Location, bool IsLoad,
+ const Stmt *S,
+ CheckerContext &Context) const {
+ // We should care only about loads.
+ // The main idea is to add a constraint whenever we're loading a value from
+ // an annotated pointer type.
+ if (!IsLoad)
+ return;
+
+ // Annotations that we want to consider make sense only for types.
+ const auto *Region =
+ dyn_cast_or_null<TypedValueRegion>(Location.getAsRegion());
+ if (!Region)
+ return;
+
+ ProgramStateRef State = Context.getState();
+
+ auto StoredVal = State->getSVal(Region).getAs<loc::MemRegionVal>();
+ if (!StoredVal)
+ return;
+
+ Nullability NullabilityOfTheLoadedValue =
+ getNullabilityAnnotation(Region->getValueType());
+
+ if (NullabilityOfTheLoadedValue == Nullability::Nonnull) {
+ // It doesn't matter what we think about this particular pointer, it should
+ // be considered non-null as annotated by the developer.
+ if (ProgramStateRef NewState = State->assume(*StoredVal, true)) {
+ Context.addTransition(NewState);
+ }
+ }
+}
+
/// Find the outermost subexpression of E that is not an implicit cast.
/// This looks through the implicit casts to _Nonnull that ARC adds to
/// return expressions of ObjC types when the return type of the function or
/// method is non-null but the express is not.
static const Expr *lookThroughImplicitCasts(const Expr *E) {
- assert(E);
-
- while (auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
- E = ICE->getSubExpr();
- }
-
- return E;
+ return E->IgnoreImpCasts();
}
/// This method check when nullable pointer or null value is returned from a
@@ -572,11 +619,9 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
bool NullReturnedFromNonNull = (RequiredNullability == Nullability::Nonnull &&
Nullness == NullConstraint::IsNull);
- if (Filter.CheckNullReturnedFromNonnull &&
- NullReturnedFromNonNull &&
+ if (ChecksEnabled[CK_NullReturnedFromNonnull] && NullReturnedFromNonNull &&
RetExprTypeLevelNullability != Nullability::Nonnull &&
- !InSuppressedMethodFamily &&
- C.getLocationContext()->inTopFrame()) {
+ !InSuppressedMethodFamily && C.getLocationContext()->inTopFrame()) {
static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull");
ExplodedNode *N = C.generateErrorNode(State, &Tag);
if (!N)
@@ -587,8 +632,8 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
OS << (RetExpr->getType()->isObjCObjectPointerType() ? "nil" : "Null");
OS << " returned from a " << C.getDeclDescription(D) <<
" that is expected to return a non-null value";
- reportBugIfInvariantHolds(OS.str(),
- ErrorKind::NilReturnedToNonnull, N, nullptr, C,
+ reportBugIfInvariantHolds(OS.str(), ErrorKind::NilReturnedToNonnull,
+ CK_NullReturnedFromNonnull, N, nullptr, C,
RetExpr);
return;
}
@@ -609,7 +654,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
State->get<NullabilityMap>(Region);
if (TrackedNullability) {
Nullability TrackedNullabValue = TrackedNullability->getValue();
- if (Filter.CheckNullableReturnedFromNonnull &&
+ if (ChecksEnabled[CK_NullableReturnedFromNonnull] &&
Nullness != NullConstraint::IsNotNull &&
TrackedNullabValue == Nullability::Nullable &&
RequiredNullability == Nullability::Nonnull) {
@@ -621,9 +666,8 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
OS << "Nullable pointer is returned from a " << C.getDeclDescription(D) <<
" that is expected to return a non-null value";
- reportBugIfInvariantHolds(OS.str(),
- ErrorKind::NullableReturnedToNonnull, N,
- Region, C);
+ reportBugIfInvariantHolds(OS.str(), ErrorKind::NullableReturnedToNonnull,
+ CK_NullableReturnedFromNonnull, N, Region, C);
}
return;
}
@@ -674,7 +718,8 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
unsigned ParamIdx = Param->getFunctionScopeIndex() + 1;
- if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull &&
+ if (ChecksEnabled[CK_NullPassedToNonnull] &&
+ Nullness == NullConstraint::IsNull &&
ArgExprTypeLevelNullability != Nullability::Nonnull &&
RequiredNullability == Nullability::Nonnull &&
isDiagnosableCall(Call)) {
@@ -687,9 +732,9 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
OS << (Param->getType()->isObjCObjectPointerType() ? "nil" : "Null");
OS << " passed to a callee that requires a non-null " << ParamIdx
<< llvm::getOrdinalSuffix(ParamIdx) << " parameter";
- reportBugIfInvariantHolds(OS.str(), ErrorKind::NilPassedToNonnull, N,
- nullptr, C,
- ArgExpr, /*SuppressPath=*/false);
+ reportBugIfInvariantHolds(OS.str(), ErrorKind::NilPassedToNonnull,
+ CK_NullPassedToNonnull, N, nullptr, C, ArgExpr,
+ /*SuppressPath=*/false);
return;
}
@@ -705,7 +750,7 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
TrackedNullability->getValue() != Nullability::Nullable)
continue;
- if (Filter.CheckNullablePassedToNonnull &&
+ if (ChecksEnabled[CK_NullablePassedToNonnull] &&
RequiredNullability == Nullability::Nonnull &&
isDiagnosableCall(Call)) {
ExplodedNode *N = C.addTransition(State);
@@ -713,17 +758,18 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
llvm::raw_svector_ostream OS(SBuf);
OS << "Nullable pointer is passed to a callee that requires a non-null "
<< ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter";
- reportBugIfInvariantHolds(OS.str(),
- ErrorKind::NullablePassedToNonnull, N,
- Region, C, ArgExpr, /*SuppressPath=*/true);
+ reportBugIfInvariantHolds(OS.str(), ErrorKind::NullablePassedToNonnull,
+ CK_NullablePassedToNonnull, N, Region, C,
+ ArgExpr, /*SuppressPath=*/true);
return;
}
- if (Filter.CheckNullableDereferenced &&
+ if (ChecksEnabled[CK_NullableDereferenced] &&
Param->getType()->isReferenceType()) {
ExplodedNode *N = C.addTransition(State);
reportBugIfInvariantHolds("Nullable pointer is dereferenced",
- ErrorKind::NullableDereferenced, N, Region,
- C, ArgExpr, /*SuppressPath=*/true);
+ ErrorKind::NullableDereferenced,
+ CK_NullableDereferenced, N, Region, C,
+ ArgExpr, /*SuppressPath=*/true);
return;
}
continue;
@@ -1083,8 +1129,7 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
bool NullAssignedToNonNull = (LocNullability == Nullability::Nonnull &&
RhsNullness == NullConstraint::IsNull);
- if (Filter.CheckNullPassedToNonnull &&
- NullAssignedToNonNull &&
+ if (ChecksEnabled[CK_NullPassedToNonnull] && NullAssignedToNonNull &&
ValNullability != Nullability::Nonnull &&
ValueExprTypeLevelNullability != Nullability::Nonnull &&
!isARCNilInitializedLocal(C, S)) {
@@ -1102,9 +1147,8 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
llvm::raw_svector_ostream OS(SBuf);
OS << (LocType->isObjCObjectPointerType() ? "nil" : "Null");
OS << " assigned to a pointer which is expected to have non-null value";
- reportBugIfInvariantHolds(OS.str(),
- ErrorKind::NilAssignedToNonnull, N, nullptr, C,
- ValueStmt);
+ reportBugIfInvariantHolds(OS.str(), ErrorKind::NilAssignedToNonnull,
+ CK_NullPassedToNonnull, N, nullptr, C, ValueStmt);
return;
}
@@ -1130,14 +1174,14 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
if (RhsNullness == NullConstraint::IsNotNull ||
TrackedNullability->getValue() != Nullability::Nullable)
return;
- if (Filter.CheckNullablePassedToNonnull &&
+ if (ChecksEnabled[CK_NullablePassedToNonnull] &&
LocNullability == Nullability::Nonnull) {
static CheckerProgramPointTag Tag(this, "NullablePassedToNonnull");
ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag);
reportBugIfInvariantHolds("Nullable pointer is assigned to a pointer "
"which is expected to have non-null value",
- ErrorKind::NullableAssignedToNonnull, N,
- ValueRegion, C);
+ ErrorKind::NullableAssignedToNonnull,
+ CK_NullablePassedToNonnull, N, ValueRegion, C);
}
return;
}
@@ -1188,15 +1232,16 @@ void ento::registerNullabilityBase(CheckerManager &mgr) {
mgr.registerChecker<NullabilityChecker>();
}
-bool ento::shouldRegisterNullabilityBase(const LangOptions &LO) {
+bool ento::shouldRegisterNullabilityBase(const CheckerManager &mgr) {
return true;
}
#define REGISTER_CHECKER(name, trackingRequired) \
void ento::register##name##Checker(CheckerManager &mgr) { \
NullabilityChecker *checker = mgr.getChecker<NullabilityChecker>(); \
- checker->Filter.Check##name = true; \
- checker->Filter.CheckName##name = mgr.getCurrentCheckerName(); \
+ checker->ChecksEnabled[NullabilityChecker::CK_##name] = true; \
+ checker->CheckNames[NullabilityChecker::CK_##name] = \
+ mgr.getCurrentCheckerName(); \
checker->NeedTracking = checker->NeedTracking || trackingRequired; \
checker->NoDiagnoseCallsToSystemHeaders = \
checker->NoDiagnoseCallsToSystemHeaders || \
@@ -1204,7 +1249,7 @@ bool ento::shouldRegisterNullabilityBase(const LangOptions &LO) {
checker, "NoDiagnoseCallsToSystemHeaders", true); \
} \
\
- bool ento::shouldRegister##name##Checker(const LangOptions &LO) { \
+ bool ento::shouldRegister##name##Checker(const CheckerManager &mgr) { \
return true; \
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
index 1053424ae6fa..df01cc760e7e 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
@@ -57,7 +57,7 @@ public:
Callback(const NumberObjectConversionChecker *C,
BugReporter &BR, AnalysisDeclContext *ADC)
: C(C), BR(BR), ADC(ADC) {}
- virtual void run(const MatchFinder::MatchResult &Result);
+ void run(const MatchFinder::MatchResult &Result) override;
};
} // end of anonymous namespace
@@ -338,7 +338,7 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D,
MatchFinder F;
Callback CB(this, BR, AM.getAnalysisDeclContext(D));
- F.addMatcher(stmt(forEachDescendant(FinalM)), &CB);
+ F.addMatcher(traverse(TK_AsIs, stmt(forEachDescendant(FinalM))), &CB);
F.match(*D->getBody(), AM.getASTContext());
}
@@ -349,6 +349,6 @@ void ento::registerNumberObjectConversionChecker(CheckerManager &Mgr) {
Mgr.getAnalyzerOptions().getCheckerBooleanOption(Chk, "Pedantic");
}
-bool ento::shouldRegisterNumberObjectConversionChecker(const LangOptions &LO) {
+bool ento::shouldRegisterNumberObjectConversionChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp
index 5b9895c338d8..53ed0e187a4c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp
@@ -55,8 +55,7 @@ static void emitDiagnostics(const BoundNodes &Nodes,
CE->getSourceRange());
}
-static auto hasTypePointingTo(DeclarationMatcher DeclM)
- -> decltype(hasType(pointerType())) {
+static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM) {
return hasType(pointerType(pointee(hasDeclaration(DeclM))));
}
@@ -85,6 +84,6 @@ void ento::registerOSObjectCStyleCast(CheckerManager &Mgr) {
Mgr.registerChecker<OSObjectCStyleCastChecker>();
}
-bool ento::shouldRegisterOSObjectCStyleCast(const LangOptions &LO) {
+bool ento::shouldRegisterOSObjectCStyleCast(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
index 0e25817c8793..43af4bb14286 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
@@ -91,6 +91,7 @@ void ento::registerObjCAtSyncChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCAtSyncChecker>();
}
-bool ento::shouldRegisterObjCAtSyncChecker(const LangOptions &LO) {
+bool ento::shouldRegisterObjCAtSyncChecker(const CheckerManager &mgr) {
+ const LangOptions &LO = mgr.getLangOpts();
return LO.ObjC;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp
index d2371fe60d21..7fd6e2abef4c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp
@@ -30,6 +30,7 @@
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "llvm/ADT/Twine.h"
@@ -44,6 +45,7 @@ const char *ProblematicWriteBind = "problematicwrite";
const char *CapturedBind = "capturedbind";
const char *ParamBind = "parambind";
const char *IsMethodBind = "ismethodbind";
+const char *IsARPBind = "isautoreleasepoolbind";
class ObjCAutoreleaseWriteChecker : public Checker<check::ASTCodeBody> {
public:
@@ -100,8 +102,7 @@ static inline std::vector<llvm::StringRef> toRefs(std::vector<std::string> V) {
return std::vector<llvm::StringRef>(V.begin(), V.end());
}
-static auto callsNames(std::vector<std::string> FunctionNames)
- -> decltype(callee(functionDecl())) {
+static decltype(auto) callsNames(std::vector<std::string> FunctionNames) {
return callee(functionDecl(hasAnyName(toRefs(FunctionNames))));
}
@@ -129,21 +130,39 @@ static void emitDiagnostics(BoundNodes &Match, const Decl *D, BugReporter &BR,
SourceRange Range = MarkedStmt->getSourceRange();
PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin(
MarkedStmt, BR.getSourceManager(), ADC);
+
bool IsMethod = Match.getNodeAs<ObjCMethodDecl>(IsMethodBind) != nullptr;
- const char *Name = IsMethod ? "method" : "function";
-
- BR.EmitBasicReport(
- ADC->getDecl(), Checker,
- /*Name=*/(llvm::Twine(ActionMsg)
- + " autoreleasing out parameter inside autorelease pool").str(),
- /*BugCategory=*/"Memory",
- (llvm::Twine(ActionMsg) + " autoreleasing out parameter " +
- (IsCapture ? "'" + PVD->getName() + "'" + " " : "") + "inside " +
- "autorelease pool that may exit before " + Name + " returns; consider "
- "writing first to a strong local variable declared outside of the block")
- .str(),
- Location,
- Range);
+ const char *FunctionDescription = IsMethod ? "method" : "function";
+ bool IsARP = Match.getNodeAs<ObjCAutoreleasePoolStmt>(IsARPBind) != nullptr;
+
+ llvm::SmallString<128> BugNameBuf;
+ llvm::raw_svector_ostream BugName(BugNameBuf);
+ BugName << ActionMsg
+ << " autoreleasing out parameter inside autorelease pool";
+
+ llvm::SmallString<128> BugMessageBuf;
+ llvm::raw_svector_ostream BugMessage(BugMessageBuf);
+ BugMessage << ActionMsg << " autoreleasing out parameter ";
+ if (IsCapture)
+ BugMessage << "'" + PVD->getName() + "' ";
+
+ BugMessage << "inside ";
+ if (IsARP)
+ BugMessage << "locally-scoped autorelease pool;";
+ else
+ BugMessage << "autorelease pool that may exit before "
+ << FunctionDescription << " returns;";
+
+ BugMessage << " consider writing first to a strong local variable"
+ " declared outside ";
+ if (IsARP)
+ BugMessage << "of the autorelease pool";
+ else
+ BugMessage << "of the block";
+
+ BR.EmitBasicReport(ADC->getDecl(), Checker, BugName.str(),
+ categories::MemoryRefCount, BugMessage.str(), Location,
+ Range);
}
void ObjCAutoreleaseWriteChecker::checkASTCodeBody(const Decl *D,
@@ -189,9 +208,16 @@ void ObjCAutoreleaseWriteChecker::checkASTCodeBody(const Decl *D,
WritesOrCapturesInBlockM))
));
- auto HasParamAndWritesInMarkedFuncM = allOf(
- hasAnyParameter(DoublePointerParamM),
- forEachDescendant(BlockPassedToMarkedFuncM));
+ // WritesIntoM happens inside an explicit @autoreleasepool.
+ auto WritesOrCapturesInPoolM =
+ autoreleasePoolStmt(
+ forEachDescendant(stmt(anyOf(WritesIntoM, CapturedInParamM))))
+ .bind(IsARPBind);
+
+ auto HasParamAndWritesInMarkedFuncM =
+ allOf(hasAnyParameter(DoublePointerParamM),
+ anyOf(forEachDescendant(BlockPassedToMarkedFuncM),
+ forEachDescendant(WritesOrCapturesInPoolM)));
auto MatcherM = decl(anyOf(
objcMethodDecl(HasParamAndWritesInMarkedFuncM).bind(IsMethodBind),
@@ -207,6 +233,6 @@ void ento::registerAutoreleaseWriteChecker(CheckerManager &Mgr) {
Mgr.registerChecker<ObjCAutoreleaseWriteChecker>();
}
-bool ento::shouldRegisterAutoreleaseWriteChecker(const LangOptions &LO) {
+bool ento::shouldRegisterAutoreleaseWriteChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
index 4450c464f89d..8428b2294ba6 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
@@ -172,6 +172,6 @@ void ento::registerObjCContainersASTChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCContainersASTChecker>();
}
-bool ento::shouldRegisterObjCContainersASTChecker(const LangOptions &LO) {
+bool ento::shouldRegisterObjCContainersASTChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
index 8abb926d4862..8c2008a7ceb4 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
@@ -58,7 +58,7 @@ public:
PointerEscapeKind Kind) const;
void printState(raw_ostream &OS, ProgramStateRef State,
- const char *NL, const char *Sep) const;
+ const char *NL, const char *Sep) const override;
};
} // end anonymous namespace
@@ -188,6 +188,6 @@ void ento::registerObjCContainersChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCContainersChecker>();
}
-bool ento::shouldRegisterObjCContainersChecker(const LangOptions &LO) {
+bool ento::shouldRegisterObjCContainersChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
index 1870c08432de..24e2a4dea922 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
@@ -221,7 +221,7 @@ void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
Mgr.registerChecker<ObjCSuperCallChecker>();
}
-bool ento::shouldRegisterObjCSuperCallChecker(const LangOptions &LO) {
+bool ento::shouldRegisterObjCSuperCallChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp
index 9a49200545e3..4636fd160511 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp
@@ -79,6 +79,6 @@ void ento::registerObjCPropertyChecker(CheckerManager &Mgr) {
Mgr.registerChecker<ObjCPropertyChecker>();
}
-bool ento::shouldRegisterObjCPropertyChecker(const LangOptions &LO) {
+bool ento::shouldRegisterObjCPropertyChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
index 344285750f0e..17d3c042ac40 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
@@ -437,6 +437,6 @@ void ento::registerObjCSelfInitChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCSelfInitChecker>();
}
-bool ento::shouldRegisterObjCSelfInitChecker(const LangOptions &LO) {
+bool ento::shouldRegisterObjCSelfInitChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
index 0575be845374..3547b7bb61a2 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
@@ -116,13 +116,14 @@ void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M,
return;
ProgramStateRef State = C.getState();
- SymbolRef ReceiverSymbol = M.getSelfSVal().getAsSymbol();
- assert(ReceiverSymbol && "No receiver symbol at call to [super dealloc]?");
+ const LocationContext *LC = C.getLocationContext();
+ SymbolRef SelfSymbol = State->getSelfSVal(LC).getAsSymbol();
+ assert(SelfSymbol && "No receiver symbol at call to [super dealloc]?");
// We add this transition in checkPostObjCMessage to avoid warning when
// we inline a call to [super dealloc] where the inlined call itself
// calls [super dealloc].
- State = State->add<CalledSuperDealloc>(ReceiverSymbol);
+ State = State->add<CalledSuperDealloc>(SelfSymbol);
C.addTransition(State);
}
@@ -284,6 +285,6 @@ void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) {
Mgr.registerChecker<ObjCSuperDeallocChecker>();
}
-bool ento::shouldRegisterObjCSuperDeallocChecker(const LangOptions &LO) {
+bool ento::shouldRegisterObjCSuperDeallocChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
index cb4770451572..c9828c36a06a 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
@@ -186,6 +186,6 @@ void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCUnusedIvarsChecker>();
}
-bool ento::shouldRegisterObjCUnusedIvarsChecker(const LangOptions &LO) {
+bool ento::shouldRegisterObjCUnusedIvarsChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
index 4a3c2b8cd40e..0b00664c7c10 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
@@ -353,6 +353,6 @@ void ento::registerPaddingChecker(CheckerManager &Mgr) {
Checker, "AllowedPad", "a non-negative value");
}
-bool ento::shouldRegisterPaddingChecker(const LangOptions &LO) {
+bool ento::shouldRegisterPaddingChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
index 259f23abdc95..d3e2849a0ce6 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
@@ -343,6 +343,6 @@ void ento::registerPointerArithChecker(CheckerManager &mgr) {
mgr.registerChecker<PointerArithChecker>();
}
-bool ento::shouldRegisterPointerArithChecker(const LangOptions &LO) {
+bool ento::shouldRegisterPointerArithChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp
index 307e59b8eebc..8aca6d009cdb 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp
@@ -95,6 +95,7 @@ void ento::registerPointerIterationChecker(CheckerManager &Mgr) {
Mgr.registerChecker<PointerIterationChecker>();
}
-bool ento::shouldRegisterPointerIterationChecker(const LangOptions &LO) {
+bool ento::shouldRegisterPointerIterationChecker(const CheckerManager &mgr) {
+ const LangOptions &LO = mgr.getLangOpts();
return LO.CPlusPlus;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp
index 586d9d3af2a6..25d87f4acfc9 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp
@@ -54,7 +54,7 @@ static void emitDiagnostics(const BoundNodes &Match, const Decl *D,
OS.str(), Location, Range);
}
-auto callsName(const char *FunctionName) -> decltype(callee(functionDecl())) {
+decltype(auto) callsName(const char *FunctionName) {
return callee(functionDecl(hasName(FunctionName)));
}
@@ -86,8 +86,9 @@ auto matchSortWithPointers() -> decltype(decl()) {
)))
))));
- auto PointerSortM = stmt(callExpr(allOf(SortFuncM, IteratesPointerEltsM))
- ).bind(WarnAtNode);
+ auto PointerSortM = traverse(
+ TK_AsIs,
+ stmt(callExpr(allOf(SortFuncM, IteratesPointerEltsM))).bind(WarnAtNode));
return decl(forEachDescendant(PointerSortM));
}
@@ -108,6 +109,7 @@ void ento::registerPointerSortingChecker(CheckerManager &Mgr) {
Mgr.registerChecker<PointerSortingChecker>();
}
-bool ento::shouldRegisterPointerSortingChecker(const LangOptions &LO) {
+bool ento::shouldRegisterPointerSortingChecker(const CheckerManager &mgr) {
+ const LangOptions &LO = mgr.getLangOpts();
return LO.CPlusPlus;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
index 88d0eb2ae748..81c19d9a0940 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
@@ -74,6 +74,6 @@ void ento::registerPointerSubChecker(CheckerManager &mgr) {
mgr.registerChecker<PointerSubChecker>();
}
-bool ento::shouldRegisterPointerSubChecker(const LangOptions &LO) {
+bool ento::shouldRegisterPointerSubChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index 8649b8b96dd0..285d2da104f1 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -6,8 +6,14 @@
//
//===----------------------------------------------------------------------===//
//
-// This defines PthreadLockChecker, a simple lock -> unlock checker.
-// Also handles XNU locks, which behave similarly enough to share code.
+// This file defines:
+// * PthreadLockChecker, a simple lock -> unlock checker.
+// Which also checks for XNU locks, which behave similarly enough to share
+// code.
+// * FuchsiaLocksChecker, which is also rather similar.
+// * C11LockChecker which also closely follows Pthread semantics.
+//
+// TODO: Path notes.
//
//===----------------------------------------------------------------------===//
@@ -15,8 +21,8 @@
#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"
using namespace clang;
using namespace ento;
@@ -46,9 +52,7 @@ public:
return LockState(UnlockedAndPossiblyDestroyed);
}
- bool operator==(const LockState &X) const {
- return K == X.K;
- }
+ bool operator==(const LockState &X) const { return K == X.K; }
bool isLocked() const { return K == Locked; }
bool isUnlocked() const { return K == Unlocked; }
@@ -60,40 +64,182 @@ public:
return K == UnlockedAndPossiblyDestroyed;
}
- void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddInteger(K);
- }
+ void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
};
-class PthreadLockChecker
- : public Checker<check::PostStmt<CallExpr>, check::DeadSymbols> {
- mutable std::unique_ptr<BugType> BT_doublelock;
- mutable std::unique_ptr<BugType> BT_doubleunlock;
- mutable std::unique_ptr<BugType> BT_destroylock;
- mutable std::unique_ptr<BugType> BT_initlock;
- mutable std::unique_ptr<BugType> BT_lor;
- enum LockingSemantics {
- NotApplicable = 0,
- PthreadSemantics,
- XNUSemantics
- };
+class PthreadLockChecker : public Checker<check::PostCall, check::DeadSymbols,
+ check::RegionChanges> {
public:
- void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
- void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
- void printState(raw_ostream &Out, ProgramStateRef State,
- const char *NL, const char *Sep) const override;
+ enum LockingSemantics { NotApplicable = 0, PthreadSemantics, XNUSemantics };
+ enum CheckerKind {
+ CK_PthreadLockChecker,
+ CK_FuchsiaLockChecker,
+ CK_C11LockChecker,
+ CK_NumCheckKinds
+ };
+ DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ CheckerNameRef CheckNames[CK_NumCheckKinds];
+
+private:
+ typedef void (PthreadLockChecker::*FnCheck)(const CallEvent &Call,
+ CheckerContext &C,
+ CheckerKind checkkind) const;
+ CallDescriptionMap<FnCheck> PThreadCallbacks = {
+ // Init.
+ {{"pthread_mutex_init", 2}, &PthreadLockChecker::InitAnyLock},
+ // TODO: pthread_rwlock_init(2 arguments).
+ // TODO: lck_mtx_init(3 arguments).
+ // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex.
+ // TODO: lck_rw_init(3 arguments).
+ // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex.
+
+ // Acquire.
+ {{"pthread_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{"pthread_rwlock_rdlock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{"pthread_rwlock_wrlock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{"lck_mtx_lock", 1}, &PthreadLockChecker::AcquireXNULock},
+ {{"lck_rw_lock_exclusive", 1}, &PthreadLockChecker::AcquireXNULock},
+ {{"lck_rw_lock_shared", 1}, &PthreadLockChecker::AcquireXNULock},
+
+ // Try.
+ {{"pthread_mutex_trylock", 1}, &PthreadLockChecker::TryPthreadLock},
+ {{"pthread_rwlock_tryrdlock", 1}, &PthreadLockChecker::TryPthreadLock},
+ {{"pthread_rwlock_trywrlock", 1}, &PthreadLockChecker::TryPthreadLock},
+ {{"lck_mtx_try_lock", 1}, &PthreadLockChecker::TryXNULock},
+ {{"lck_rw_try_lock_exclusive", 1}, &PthreadLockChecker::TryXNULock},
+ {{"lck_rw_try_lock_shared", 1}, &PthreadLockChecker::TryXNULock},
+
+ // Release.
+ {{"pthread_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{"pthread_rwlock_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{"lck_mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{"lck_rw_unlock_exclusive", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{"lck_rw_unlock_shared", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{"lck_rw_done", 1}, &PthreadLockChecker::ReleaseAnyLock},
+
+ // Destroy.
+ {{"pthread_mutex_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock},
+ {{"lck_mtx_destroy", 2}, &PthreadLockChecker::DestroyXNULock},
+ // TODO: pthread_rwlock_destroy(1 argument).
+ // TODO: lck_rw_destroy(2 arguments).
+ };
- void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock,
- bool isTryLock, enum LockingSemantics semantics) const;
+ CallDescriptionMap<FnCheck> FuchsiaCallbacks = {
+ // Init.
+ {{"spin_lock_init", 1}, &PthreadLockChecker::InitAnyLock},
+
+ // Acquire.
+ {{"spin_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{"spin_lock_save", 3}, &PthreadLockChecker::AcquirePthreadLock},
+ {{"sync_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{"sync_mutex_lock_with_waiter", 1},
+ &PthreadLockChecker::AcquirePthreadLock},
+
+ // Try.
+ {{"spin_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock},
+ {{"sync_mutex_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock},
+ {{"sync_mutex_timedlock", 2}, &PthreadLockChecker::TryFuchsiaLock},
+
+ // Release.
+ {{"spin_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{"spin_unlock_restore", 3}, &PthreadLockChecker::ReleaseAnyLock},
+ {{"sync_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ };
+
+ CallDescriptionMap<FnCheck> C11Callbacks = {
+ // Init.
+ {{"mtx_init", 2}, &PthreadLockChecker::InitAnyLock},
+
+ // Acquire.
+ {{"mtx_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+
+ // Try.
+ {{"mtx_trylock", 1}, &PthreadLockChecker::TryC11Lock},
+ {{"mtx_timedlock", 2}, &PthreadLockChecker::TryC11Lock},
+
+ // Release.
+ {{"mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+
+ // Destroy
+ {{"mtx_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock},
+ };
- void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const;
- void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock,
- enum LockingSemantics semantics) const;
- void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
- void reportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const;
ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
const MemRegion *lockR,
const SymbolRef *sym) const;
+ void reportUseDestroyedBug(const CallEvent &Call, CheckerContext &C,
+ unsigned ArgNo, CheckerKind checkKind) const;
+
+ // Init.
+ void InitAnyLock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
+ void InitLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
+ SVal Lock, CheckerKind checkkind) const;
+
+ // Lock, Try-lock.
+ void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
+ void AcquireXNULock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
+ void TryPthreadLock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
+ void TryXNULock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
+ void TryFuchsiaLock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
+ void TryC11Lock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
+ void AcquireLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
+ SVal lock, bool isTryLock, LockingSemantics semantics,
+ CheckerKind checkkind) const;
+
+ // Release.
+ void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
+ void ReleaseLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
+ SVal lock, CheckerKind checkkind) const;
+
+ // Destroy.
+ void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
+ void DestroyXNULock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkkind) const;
+ void DestroyLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
+ SVal Lock, LockingSemantics semantics,
+ CheckerKind checkkind) const;
+
+public:
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+ ProgramStateRef
+ checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols *Symbols,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx, const CallEvent *Call) const;
+ void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+ const char *Sep) const override;
+
+private:
+ mutable std::unique_ptr<BugType> BT_doublelock[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_doubleunlock[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_destroylock[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_initlock[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_lor[CK_NumCheckKinds];
+
+ void initBugType(CheckerKind checkKind) const {
+ if (BT_doublelock[checkKind])
+ return;
+ BT_doublelock[checkKind].reset(
+ new BugType{CheckNames[checkKind], "Double locking", "Lock checker"});
+ BT_doubleunlock[checkKind].reset(
+ new BugType{CheckNames[checkKind], "Double unlocking", "Lock checker"});
+ BT_destroylock[checkKind].reset(new BugType{
+ CheckNames[checkKind], "Use destroyed lock", "Lock checker"});
+ BT_initlock[checkKind].reset(new BugType{
+ CheckNames[checkKind], "Init invalid lock", "Lock checker"});
+ BT_lor[checkKind].reset(new BugType{CheckNames[checkKind],
+ "Lock order reversal", "Lock checker"});
+ }
};
} // end anonymous namespace
@@ -106,43 +252,23 @@ REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
// Return values for unresolved calls to pthread_mutex_destroy().
REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef)
-void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
+void PthreadLockChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
- StringRef FName = C.getCalleeName(CE);
- if (FName.empty())
+ // An additional umbrella check that all functions modeled by this checker
+ // are global C functions.
+ // TODO: Maybe make this the default behavior of CallDescription
+ // with exactly one identifier?
+ // FIXME: Try to handle cases when the implementation was inlined rather
+ // than just giving up.
+ if (!Call.isGlobalCFunction() || C.wasInlined)
return;
- if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
- return;
-
- if (FName == "pthread_mutex_lock" ||
- FName == "pthread_rwlock_rdlock" ||
- FName == "pthread_rwlock_wrlock")
- AcquireLock(C, CE, C.getSVal(CE->getArg(0)), false, PthreadSemantics);
- else if (FName == "lck_mtx_lock" ||
- FName == "lck_rw_lock_exclusive" ||
- FName == "lck_rw_lock_shared")
- AcquireLock(C, CE, C.getSVal(CE->getArg(0)), false, XNUSemantics);
- else if (FName == "pthread_mutex_trylock" ||
- FName == "pthread_rwlock_tryrdlock" ||
- FName == "pthread_rwlock_trywrlock")
- AcquireLock(C, CE, C.getSVal(CE->getArg(0)),
- true, PthreadSemantics);
- else if (FName == "lck_mtx_try_lock" ||
- FName == "lck_rw_try_lock_exclusive" ||
- FName == "lck_rw_try_lock_shared")
- AcquireLock(C, CE, C.getSVal(CE->getArg(0)), true, XNUSemantics);
- else if (FName == "pthread_mutex_unlock" ||
- FName == "pthread_rwlock_unlock" ||
- FName == "lck_mtx_unlock" ||
- FName == "lck_rw_done")
- ReleaseLock(C, CE, C.getSVal(CE->getArg(0)));
- else if (FName == "pthread_mutex_destroy")
- DestroyLock(C, CE, C.getSVal(CE->getArg(0)), PthreadSemantics);
- else if (FName == "lck_mtx_destroy")
- DestroyLock(C, CE, C.getSVal(CE->getArg(0)), XNUSemantics);
- else if (FName == "pthread_mutex_init")
- InitLock(C, CE, C.getSVal(CE->getArg(0)));
+ if (const FnCheck *Callback = PThreadCallbacks.lookup(Call))
+ (this->**Callback)(Call, C, CK_PthreadLockChecker);
+ else if (const FnCheck *Callback = FuchsiaCallbacks.lookup(Call))
+ (this->**Callback)(Call, C, CK_FuchsiaLockChecker);
+ else if (const FnCheck *Callback = C11Callbacks.lookup(Call))
+ (this->**Callback)(Call, C, CK_C11LockChecker);
}
// When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
@@ -204,7 +330,7 @@ void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State,
LockSetTy LS = State->get<LockSet>();
if (!LS.isEmpty()) {
Out << Sep << "Mutex lock order:" << NL;
- for (auto I: LS) {
+ for (auto I : LS) {
I->dumpToStream(Out);
Out << NL;
}
@@ -213,9 +339,53 @@ void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State,
// TODO: Dump destroyed mutex symbols?
}
-void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
- SVal lock, bool isTryLock,
- enum LockingSemantics semantics) const {
+void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call,
+ CheckerContext &C,
+ CheckerKind checkKind) const {
+ AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, PthreadSemantics,
+ checkKind);
+}
+
+void PthreadLockChecker::AcquireXNULock(const CallEvent &Call,
+ CheckerContext &C,
+ CheckerKind checkKind) const {
+ AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, XNUSemantics,
+ checkKind);
+}
+
+void PthreadLockChecker::TryPthreadLock(const CallEvent &Call,
+ CheckerContext &C,
+ CheckerKind checkKind) const {
+ AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
+ checkKind);
+}
+
+void PthreadLockChecker::TryXNULock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkKind) const {
+ AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
+ checkKind);
+}
+
+void PthreadLockChecker::TryFuchsiaLock(const CallEvent &Call,
+ CheckerContext &C,
+ CheckerKind checkKind) const {
+ AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
+ checkKind);
+}
+
+void PthreadLockChecker::TryC11Lock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkKind) const {
+ AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
+ checkKind);
+}
+
+void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
+ CheckerContext &C, unsigned ArgNo,
+ SVal lock, bool isTryLock,
+ enum LockingSemantics semantics,
+ CheckerKind checkKind) const {
+ if (!ChecksEnabled[checkKind])
+ return;
const MemRegion *lockR = lock.getAsRegion();
if (!lockR)
@@ -226,27 +396,19 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
if (sym)
state = resolvePossiblyDestroyedMutex(state, lockR, sym);
- SVal X = C.getSVal(CE);
- if (X.isUnknownOrUndef())
- return;
-
- DefinedSVal retVal = X.castAs<DefinedSVal>();
-
if (const LockState *LState = state->get<LockMap>(lockR)) {
if (LState->isLocked()) {
- if (!BT_doublelock)
- BT_doublelock.reset(new BugType(this, "Double locking",
- "Lock checker"));
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
+ initBugType(checkKind);
auto report = std::make_unique<PathSensitiveBugReport>(
- *BT_doublelock, "This lock has already been acquired", N);
- report->addRange(CE->getArg(0)->getSourceRange());
+ *BT_doublelock[checkKind], "This lock has already been acquired", N);
+ report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
C.emitReport(std::move(report));
return;
} else if (LState->isDestroyed()) {
- reportUseDestroyedBug(C, CE);
+ reportUseDestroyedBug(Call, C, ArgNo, checkKind);
return;
}
}
@@ -254,25 +416,35 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
ProgramStateRef lockSucc = state;
if (isTryLock) {
// Bifurcate the state, and allow a mode where the lock acquisition fails.
- ProgramStateRef lockFail;
- switch (semantics) {
- case PthreadSemantics:
- std::tie(lockFail, lockSucc) = state->assume(retVal);
- break;
- case XNUSemantics:
- std::tie(lockSucc, lockFail) = state->assume(retVal);
- break;
- default:
- llvm_unreachable("Unknown tryLock locking semantics");
+ SVal RetVal = Call.getReturnValue();
+ if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
+ ProgramStateRef lockFail;
+ switch (semantics) {
+ case PthreadSemantics:
+ std::tie(lockFail, lockSucc) = state->assume(*DefinedRetVal);
+ break;
+ case XNUSemantics:
+ std::tie(lockSucc, lockFail) = state->assume(*DefinedRetVal);
+ break;
+ default:
+ llvm_unreachable("Unknown tryLock locking semantics");
+ }
+ assert(lockFail && lockSucc);
+ C.addTransition(lockFail);
}
- assert(lockFail && lockSucc);
- C.addTransition(lockFail);
-
+ // We might want to handle the case when the mutex lock function was inlined
+ // and returned an Unknown or Undefined value.
} else if (semantics == PthreadSemantics) {
// Assume that the return value was 0.
- lockSucc = state->assume(retVal, false);
- assert(lockSucc);
-
+ SVal RetVal = Call.getReturnValue();
+ if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
+ // FIXME: If the lock function was inlined and returned true,
+ // we need to behave sanely - at least generate sink.
+ lockSucc = state->assume(*DefinedRetVal, false);
+ assert(lockSucc);
+ }
+ // We might want to handle the case when the mutex lock function was inlined
+ // and returned an Unknown or Undefined value.
} else {
// XNU locking semantics return void on non-try locks
assert((semantics == XNUSemantics) && "Unknown locking semantics");
@@ -285,8 +457,18 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
C.addTransition(lockSucc);
}
-void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
- SVal lock) const {
+void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call,
+ CheckerContext &C,
+ CheckerKind checkKind) const {
+ ReleaseLockAux(Call, C, 0, Call.getArgSVal(0), checkKind);
+}
+
+void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
+ CheckerContext &C, unsigned ArgNo,
+ SVal lock,
+ CheckerKind checkKind) const {
+ if (!ChecksEnabled[checkKind])
+ return;
const MemRegion *lockR = lock.getAsRegion();
if (!lockR)
@@ -299,39 +481,37 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
if (const LockState *LState = state->get<LockMap>(lockR)) {
if (LState->isUnlocked()) {
- if (!BT_doubleunlock)
- BT_doubleunlock.reset(new BugType(this, "Double unlocking",
- "Lock checker"));
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
+ initBugType(checkKind);
auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT_doubleunlock, "This lock has already been unlocked", N);
- Report->addRange(CE->getArg(0)->getSourceRange());
+ *BT_doubleunlock[checkKind], "This lock has already been unlocked",
+ N);
+ Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
C.emitReport(std::move(Report));
return;
} else if (LState->isDestroyed()) {
- reportUseDestroyedBug(C, CE);
+ reportUseDestroyedBug(Call, C, ArgNo, checkKind);
return;
}
}
LockSetTy LS = state->get<LockSet>();
- // FIXME: Better analysis requires IPA for wrappers.
-
if (!LS.isEmpty()) {
const MemRegion *firstLockR = LS.getHead();
if (firstLockR != lockR) {
- if (!BT_lor)
- BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker"));
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
+ initBugType(checkKind);
auto report = std::make_unique<PathSensitiveBugReport>(
- *BT_lor, "This was not the most recently acquired lock. Possible "
- "lock order reversal", N);
- report->addRange(CE->getArg(0)->getSourceRange());
+ *BT_lor[checkKind],
+ "This was not the most recently acquired lock. Possible "
+ "lock order reversal",
+ N);
+ report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
C.emitReport(std::move(report));
return;
}
@@ -343,9 +523,25 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
C.addTransition(state);
}
-void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
- SVal Lock,
- enum LockingSemantics semantics) const {
+void PthreadLockChecker::DestroyPthreadLock(const CallEvent &Call,
+ CheckerContext &C,
+ CheckerKind checkKind) const {
+ DestroyLockAux(Call, C, 0, Call.getArgSVal(0), PthreadSemantics, checkKind);
+}
+
+void PthreadLockChecker::DestroyXNULock(const CallEvent &Call,
+ CheckerContext &C,
+ CheckerKind checkKind) const {
+ DestroyLockAux(Call, C, 0, Call.getArgSVal(0), XNUSemantics, checkKind);
+}
+
+void PthreadLockChecker::DestroyLockAux(const CallEvent &Call,
+ CheckerContext &C, unsigned ArgNo,
+ SVal Lock,
+ enum LockingSemantics semantics,
+ CheckerKind checkKind) const {
+ if (!ChecksEnabled[checkKind])
+ return;
const MemRegion *LockR = Lock.getAsRegion();
if (!LockR)
@@ -362,7 +558,7 @@ void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
// PthreadSemantics
if (semantics == PthreadSemantics) {
if (!LState || LState->isUnlocked()) {
- SymbolRef sym = C.getSVal(CE).getAsSymbol();
+ SymbolRef sym = Call.getReturnValue().getAsSymbol();
if (!sym) {
State = State->remove<LockMap>(LockR);
C.addTransition(State);
@@ -393,20 +589,26 @@ void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
Message = "This lock has already been destroyed";
}
- if (!BT_destroylock)
- BT_destroylock.reset(new BugType(this, "Destroy invalid lock",
- "Lock checker"));
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
- auto Report =
- std::make_unique<PathSensitiveBugReport>(*BT_destroylock, Message, N);
- Report->addRange(CE->getArg(0)->getSourceRange());
+ initBugType(checkKind);
+ auto Report = std::make_unique<PathSensitiveBugReport>(
+ *BT_destroylock[checkKind], Message, N);
+ Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
C.emitReport(std::move(Report));
}
-void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE,
- SVal Lock) const {
+void PthreadLockChecker::InitAnyLock(const CallEvent &Call, CheckerContext &C,
+ CheckerKind checkKind) const {
+ InitLockAux(Call, C, 0, Call.getArgSVal(0), checkKind);
+}
+
+void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C,
+ unsigned ArgNo, SVal Lock,
+ CheckerKind checkKind) const {
+ if (!ChecksEnabled[checkKind])
+ return;
const MemRegion *LockR = Lock.getAsRegion();
if (!LockR)
@@ -433,29 +635,27 @@ void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE,
Message = "This lock has already been initialized";
}
- if (!BT_initlock)
- BT_initlock.reset(new BugType(this, "Init invalid lock",
- "Lock checker"));
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
- auto Report =
- std::make_unique<PathSensitiveBugReport>(*BT_initlock, Message, N);
- Report->addRange(CE->getArg(0)->getSourceRange());
+ initBugType(checkKind);
+ auto Report = std::make_unique<PathSensitiveBugReport>(
+ *BT_initlock[checkKind], Message, N);
+ Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
C.emitReport(std::move(Report));
}
-void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C,
- const CallExpr *CE) const {
- if (!BT_destroylock)
- BT_destroylock.reset(new BugType(this, "Use destroyed lock",
- "Lock checker"));
+void PthreadLockChecker::reportUseDestroyedBug(const CallEvent &Call,
+ CheckerContext &C,
+ unsigned ArgNo,
+ CheckerKind checkKind) const {
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
+ initBugType(checkKind);
auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT_destroylock, "This lock has already been destroyed", N);
- Report->addRange(CE->getArg(0)->getSourceRange());
+ *BT_destroylock[checkKind], "This lock has already been destroyed", N);
+ Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
C.emitReport(std::move(Report));
}
@@ -463,26 +663,80 @@ void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
- // TODO: Clean LockMap when a mutex region dies.
-
- DestroyRetValTy TrackedSymbols = State->get<DestroyRetVal>();
- for (DestroyRetValTy::iterator I = TrackedSymbols.begin(),
- E = TrackedSymbols.end();
- I != E; ++I) {
- const SymbolRef Sym = I->second;
- const MemRegion *lockR = I->first;
- bool IsSymDead = SymReaper.isDead(Sym);
- // Remove the dead symbol from the return value symbols map.
- if (IsSymDead)
- State = resolvePossiblyDestroyedMutex(State, lockR, &Sym);
+ for (auto I : State->get<DestroyRetVal>()) {
+ // Once the return value symbol dies, no more checks can be performed
+ // against it. See if the return value was checked before this point.
+ // This would remove the symbol from the map as well.
+ if (SymReaper.isDead(I.second))
+ State = resolvePossiblyDestroyedMutex(State, I.first, &I.second);
+ }
+
+ for (auto I : State->get<LockMap>()) {
+ // Stop tracking dead mutex regions as well.
+ if (!SymReaper.isLiveRegion(I.first))
+ State = State->remove<LockMap>(I.first);
}
+
+ // TODO: We probably need to clean up the lock stack as well.
+ // It is tricky though: even if the mutex cannot be unlocked anymore,
+ // it can still participate in lock order reversal resolution.
+
C.addTransition(State);
}
-void ento::registerPthreadLockChecker(CheckerManager &mgr) {
- mgr.registerChecker<PthreadLockChecker>();
+ProgramStateRef PthreadLockChecker::checkRegionChanges(
+ ProgramStateRef State, const InvalidatedSymbols *Symbols,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
+ const CallEvent *Call) const {
+
+ bool IsLibraryFunction = false;
+ if (Call && Call->isGlobalCFunction()) {
+ // Avoid invalidating mutex state when a known supported function is called.
+ if (PThreadCallbacks.lookup(*Call) || FuchsiaCallbacks.lookup(*Call) ||
+ C11Callbacks.lookup(*Call))
+ return State;
+
+ if (Call->isInSystemHeader())
+ IsLibraryFunction = true;
+ }
+
+ for (auto R : Regions) {
+ // We assume that system library function wouldn't touch the mutex unless
+ // it takes the mutex explicitly as an argument.
+ // FIXME: This is a bit quadratic.
+ if (IsLibraryFunction &&
+ std::find(ExplicitRegions.begin(), ExplicitRegions.end(), R) ==
+ ExplicitRegions.end())
+ continue;
+
+ State = State->remove<LockMap>(R);
+ State = State->remove<DestroyRetVal>(R);
+
+ // TODO: We need to invalidate the lock stack as well. This is tricky
+ // to implement correctly and efficiently though, because the effects
+ // of mutex escapes on lock order may be fairly varied.
+ }
+
+ return State;
}
-bool ento::shouldRegisterPthreadLockChecker(const LangOptions &LO) {
- return true;
+void ento::registerPthreadLockBase(CheckerManager &mgr) {
+ mgr.registerChecker<PthreadLockChecker>();
}
+
+bool ento::shouldRegisterPthreadLockBase(const CheckerManager &mgr) { return true; }
+
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &mgr) { \
+ PthreadLockChecker *checker = mgr.getChecker<PthreadLockChecker>(); \
+ checker->ChecksEnabled[PthreadLockChecker::CK_##name] = true; \
+ checker->CheckNames[PthreadLockChecker::CK_##name] = \
+ mgr.getCurrentCheckerName(); \
+ } \
+ \
+ bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
+
+REGISTER_CHECKER(PthreadLockChecker)
+REGISTER_CHECKER(FuchsiaLockChecker)
+REGISTER_CHECKER(C11LockChecker)
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
index 6f8cb1432bb1..3f3267ff9391 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
@@ -12,12 +12,12 @@
//===----------------------------------------------------------------------===//
#include "RetainCountChecker.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
using namespace clang;
using namespace ento;
using namespace retaincountchecker;
-using llvm::StrInStrNoCase;
REGISTER_MAP_WITH_PROGRAMSTATE(RefBindings, SymbolRef, RefVal)
@@ -701,7 +701,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ,
for (ProgramStateRef St : Out) {
if (DeallocSent) {
- C.addTransition(St, C.getPredecessor(), &DeallocSentTag);
+ C.addTransition(St, C.getPredecessor(), &getDeallocSentTag());
} else {
C.addTransition(St);
}
@@ -844,13 +844,13 @@ RetainCountChecker::errorKindToBugKind(RefVal::Kind ErrorKind,
SymbolRef Sym) const {
switch (ErrorKind) {
case RefVal::ErrorUseAfterRelease:
- return useAfterRelease;
+ return *UseAfterRelease;
case RefVal::ErrorReleaseNotOwned:
- return releaseNotOwned;
+ return *ReleaseNotOwned;
case RefVal::ErrorDeallocNotOwned:
if (Sym->getType()->getPointeeCXXRecordDecl())
- return freeNotOwned;
- return deallocNotOwned;
+ return *FreeNotOwned;
+ return *DeallocNotOwned;
default:
llvm_unreachable("Unhandled error.");
}
@@ -946,7 +946,7 @@ bool RetainCountChecker::evalCall(const CallEvent &Call,
// Assume that output is zero on the other branch.
NullOutputState = NullOutputState->BindExpr(
CE, LCtx, C.getSValBuilder().makeNull(), /*Invalidate=*/false);
- C.addTransition(NullOutputState, &CastFailTag);
+ C.addTransition(NullOutputState, &getCastFailTag());
// And on the original branch assume that both input and
// output are non-zero.
@@ -1095,7 +1095,7 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
if (N) {
const LangOptions &LOpts = C.getASTContext().getLangOpts();
auto R =
- std::make_unique<RefLeakReport>(leakAtReturn, LOpts, N, Sym, C);
+ std::make_unique<RefLeakReport>(*LeakAtReturn, LOpts, N, Sym, C);
C.emitReport(std::move(R));
}
return N;
@@ -1120,7 +1120,7 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag);
if (N) {
auto R = std::make_unique<RefCountReport>(
- returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym);
+ *ReturnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym);
C.emitReport(std::move(R));
}
return N;
@@ -1273,8 +1273,8 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
os << "has a +" << V.getCount() << " retain count";
const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
- auto R = std::make_unique<RefCountReport>(overAutorelease, LOpts, N, Sym,
- os.str());
+ auto R = std::make_unique<RefCountReport>(*OverAutorelease, LOpts, N, Sym,
+ os.str());
Ctx.emitReport(std::move(R));
}
@@ -1320,7 +1320,7 @@ RetainCountChecker::processLeaks(ProgramStateRef state,
if (N) {
for (SymbolRef L : Leaked) {
- const RefCountBug &BT = Pred ? leakWithinFunction : leakAtReturn;
+ const RefCountBug &BT = Pred ? *LeakWithinFunction : *LeakAtReturn;
Ctx.emitReport(std::make_unique<RefLeakReport>(BT, LOpts, N, L, Ctx));
}
}
@@ -1473,48 +1473,73 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
// Checker registration.
//===----------------------------------------------------------------------===//
+std::unique_ptr<CheckerProgramPointTag> RetainCountChecker::DeallocSentTag;
+std::unique_ptr<CheckerProgramPointTag> RetainCountChecker::CastFailTag;
+
void ento::registerRetainCountBase(CheckerManager &Mgr) {
- Mgr.registerChecker<RetainCountChecker>();
+ auto *Chk = Mgr.registerChecker<RetainCountChecker>();
+ Chk->DeallocSentTag =
+ std::make_unique<CheckerProgramPointTag>(Chk, "DeallocSent");
+ Chk->CastFailTag =
+ std::make_unique<CheckerProgramPointTag>(Chk, "DynamicCastFail");
}
-bool ento::shouldRegisterRetainCountBase(const LangOptions &LO) {
+bool ento::shouldRegisterRetainCountBase(const CheckerManager &mgr) {
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 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() == Value;
- return false;
-}
-
void ento::registerRetainCountChecker(CheckerManager &Mgr) {
auto *Chk = Mgr.getChecker<RetainCountChecker>();
Chk->TrackObjCAndCFObjects = true;
- Chk->TrackNSCFStartParam = getOption(Mgr.getAnalyzerOptions(),
- "TrackNSCFStartParam",
- "true");
+ Chk->TrackNSCFStartParam = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ Mgr.getCurrentCheckerName(), "TrackNSCFStartParam");
+
+#define INIT_BUGTYPE(KIND) \
+ Chk->KIND = std::make_unique<RefCountBug>(Mgr.getCurrentCheckerName(), \
+ RefCountBug::KIND);
+ // TODO: Ideally, we should have a checker for each of these bug types.
+ INIT_BUGTYPE(UseAfterRelease)
+ INIT_BUGTYPE(ReleaseNotOwned)
+ INIT_BUGTYPE(DeallocNotOwned)
+ INIT_BUGTYPE(FreeNotOwned)
+ INIT_BUGTYPE(OverAutorelease)
+ INIT_BUGTYPE(ReturnNotOwnedForOwned)
+ INIT_BUGTYPE(LeakWithinFunction)
+ INIT_BUGTYPE(LeakAtReturn)
+#undef INIT_BUGTYPE
}
-bool ento::shouldRegisterRetainCountChecker(const LangOptions &LO) {
+bool ento::shouldRegisterRetainCountChecker(const CheckerManager &mgr) {
return true;
}
void ento::registerOSObjectRetainCountChecker(CheckerManager &Mgr) {
auto *Chk = Mgr.getChecker<RetainCountChecker>();
- if (!getOption(Mgr.getAnalyzerOptions(),
- "CheckOSObject",
- "false"))
- Chk->TrackOSObjects = true;
+ Chk->TrackOSObjects = true;
+
+ // FIXME: We want bug reports to always have the same checker name associated
+ // with them, yet here, if RetainCountChecker is disabled but
+ // OSObjectRetainCountChecker is enabled, the checker names will be different.
+ // This hack will make it so that the checker name depends on which checker is
+ // enabled rather than on the registration order.
+ // For the most part, we want **non-hidden checkers** to be associated with
+ // diagnostics, and **hidden checker options** with the fine-tuning of
+ // modeling. Following this logic, OSObjectRetainCountChecker should be the
+ // latter, but we can't just remove it for backward compatibility reasons.
+#define LAZY_INIT_BUGTYPE(KIND) \
+ if (!Chk->KIND) \
+ Chk->KIND = std::make_unique<RefCountBug>(Mgr.getCurrentCheckerName(), \
+ RefCountBug::KIND);
+ LAZY_INIT_BUGTYPE(UseAfterRelease)
+ LAZY_INIT_BUGTYPE(ReleaseNotOwned)
+ LAZY_INIT_BUGTYPE(DeallocNotOwned)
+ LAZY_INIT_BUGTYPE(FreeNotOwned)
+ LAZY_INIT_BUGTYPE(OverAutorelease)
+ LAZY_INIT_BUGTYPE(ReturnNotOwnedForOwned)
+ LAZY_INIT_BUGTYPE(LeakWithinFunction)
+ LAZY_INIT_BUGTYPE(LeakAtReturn)
+#undef LAZY_INIT_BUGTYPE
}
-bool ento::shouldRegisterOSObjectRetainCountChecker(const LangOptions &LO) {
+bool ento::shouldRegisterOSObjectRetainCountChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
index dd79bbef321c..223e28c2c5b8 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
@@ -251,20 +251,20 @@ class RetainCountChecker
eval::Assume,
eval::Call > {
- 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"};
+public:
+ std::unique_ptr<RefCountBug> UseAfterRelease;
+ std::unique_ptr<RefCountBug> ReleaseNotOwned;
+ std::unique_ptr<RefCountBug> DeallocNotOwned;
+ std::unique_ptr<RefCountBug> FreeNotOwned;
+ std::unique_ptr<RefCountBug> OverAutorelease;
+ std::unique_ptr<RefCountBug> ReturnNotOwnedForOwned;
+ std::unique_ptr<RefCountBug> LeakWithinFunction;
+ std::unique_ptr<RefCountBug> LeakAtReturn;
mutable std::unique_ptr<RetainSummaryManager> Summaries;
-public:
+
+ static std::unique_ptr<CheckerProgramPointTag> DeallocSentTag;
+ static std::unique_ptr<CheckerProgramPointTag> CastFailTag;
/// Track Objective-C and CoreFoundation objects.
bool TrackObjCAndCFObjects = false;
@@ -360,13 +360,11 @@ public:
CheckerContext &Ctx,
ExplodedNode *Pred = nullptr) const;
- const CheckerProgramPointTag &getDeallocSentTag() const {
- return DeallocSentTag;
+ static const CheckerProgramPointTag &getDeallocSentTag() {
+ return *DeallocSentTag;
}
- const CheckerProgramPointTag &getCastFailTag() const {
- return CastFailTag;
- }
+ static const CheckerProgramPointTag &getCastFailTag() { return *CastFailTag; }
private:
/// Perform the necessary checks and state adjustments at the end of the
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
index 9853758f7f2c..1d8ed90f7590 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
@@ -18,7 +18,7 @@ using namespace clang;
using namespace ento;
using namespace retaincountchecker;
-StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugType BT) {
+StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugKind BT) {
switch (BT) {
case UseAfterRelease:
return "Use-after-release";
@@ -37,7 +37,7 @@ StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugType BT) {
case LeakAtReturn:
return "Leak of returned object";
}
- llvm_unreachable("Unknown RefCountBugType");
+ llvm_unreachable("Unknown RefCountBugKind");
}
StringRef RefCountBug::getDescription() const {
@@ -60,13 +60,14 @@ StringRef RefCountBug::getDescription() const {
case LeakAtReturn:
return "";
}
- llvm_unreachable("Unknown RefCountBugType");
+ llvm_unreachable("Unknown RefCountBugKind");
}
-RefCountBug::RefCountBug(const CheckerBase *Checker, RefCountBugType BT)
+RefCountBug::RefCountBug(CheckerNameRef Checker, RefCountBugKind BT)
: BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount,
- /*SuppressOnSink=*/BT == LeakWithinFunction || BT == LeakAtReturn),
- BT(BT), Checker(Checker) {}
+ /*SuppressOnSink=*/BT == LeakWithinFunction ||
+ BT == LeakAtReturn),
+ BT(BT) {}
static bool isNumericLiteralExpression(const Expr *E) {
// FIXME: This set of cases was copied from SemaExprObjC.
@@ -84,7 +85,7 @@ static std::string getPrettyTypeName(QualType QT) {
QualType PT = QT->getPointeeType();
if (!PT.isNull() && !QT->getAs<TypedefType>())
if (const auto *RD = PT->getAsCXXRecordDecl())
- return RD->getName();
+ return std::string(RD->getName());
return QT.getAsString();
}
@@ -453,8 +454,6 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
PathSensitiveBugReport &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;
@@ -545,11 +544,11 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
const ProgramPointTag *Tag = N->getLocation().getTag();
- if (Tag == &Checker->getCastFailTag()) {
+ if (Tag == &RetainCountChecker::getCastFailTag()) {
os << "Assuming dynamic cast returns null due to type mismatch";
}
- if (Tag == &Checker->getDeallocSentTag()) {
+ if (Tag == &RetainCountChecker::getDeallocSentTag()) {
// We only have summaries attached to nodes after evaluating CallExpr and
// ObjCMessageExprs.
const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h
index e9e277754054..286a8ae2ef7d 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h
@@ -26,7 +26,7 @@ namespace retaincountchecker {
class RefCountBug : public BugType {
public:
- enum RefCountBugType {
+ enum RefCountBugKind {
UseAfterRelease,
ReleaseNotOwned,
DeallocNotOwned,
@@ -36,21 +36,14 @@ public:
LeakWithinFunction,
LeakAtReturn,
};
- RefCountBug(const CheckerBase *checker, RefCountBugType BT);
+ RefCountBug(CheckerNameRef Checker, RefCountBugKind BT);
StringRef getDescription() const;
- RefCountBugType getBugType() const {
- return BT;
- }
-
- const CheckerBase *getChecker() const {
- return Checker;
- }
+ RefCountBugKind getBugType() const { return BT; }
private:
- RefCountBugType BT;
- const CheckerBase *Checker;
- static StringRef bugTypeToName(RefCountBugType BT);
+ RefCountBugKind BT;
+ static StringRef bugTypeToName(RefCountBugKind BT);
};
class RefCountReport : public PathSensitiveBugReport {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
index abd1a074b487..599d4f306aa1 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
@@ -16,6 +16,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
using namespace clang;
@@ -51,15 +52,14 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
// pointer casts.
if (Idx.isZeroConstant())
return;
+
// FIXME: All of this out-of-bounds checking should eventually be refactored
// into a common place.
+ DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
+ state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType());
- DefinedOrUnknownSVal NumElements
- = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(),
- ER->getValueType());
-
- ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true);
- ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false);
+ ProgramStateRef StInBound = state->assumeInBound(Idx, ElementCount, true);
+ ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false);
if (StOutBound && !StInBound) {
ExplodedNode *N = C.generateErrorNode(StOutBound);
@@ -91,6 +91,6 @@ void ento::registerReturnPointerRangeChecker(CheckerManager &mgr) {
mgr.registerChecker<ReturnPointerRangeChecker>();
}
-bool ento::shouldRegisterReturnPointerRangeChecker(const LangOptions &LO) {
+bool ento::shouldRegisterReturnPointerRangeChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
index fbd15d864424..5266cbf86b44 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
@@ -122,6 +122,6 @@ void ento::registerReturnUndefChecker(CheckerManager &mgr) {
mgr.registerChecker<ReturnUndefChecker>();
}
-bool ento::shouldRegisterReturnUndefChecker(const LangOptions &LO) {
+bool ento::shouldRegisterReturnUndefChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
index 103208d8b5a5..14ecede17083 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
@@ -99,13 +99,13 @@ void ReturnValueChecker::checkPostCall(const CallEvent &Call,
std::string Name = getName(Call);
const NoteTag *CallTag = C.getNoteTag(
- [Name, ExpectedValue](BugReport &) -> std::string {
+ [Name, ExpectedValue](PathSensitiveBugReport &) -> std::string {
SmallString<128> Msg;
llvm::raw_svector_ostream Out(Msg);
Out << '\'' << Name << "' returns "
<< (ExpectedValue ? "true" : "false");
- return Out.str();
+ return std::string(Out.str());
},
/*IsPrunable=*/true);
@@ -154,7 +154,7 @@ void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS,
Out << '\'' << Name << "' returns "
<< (ExpectedValue ? "false" : "true");
- return Out.str();
+ return std::string(Out.str());
},
/*IsPrunable=*/false);
@@ -165,6 +165,6 @@ void ento::registerReturnValueChecker(CheckerManager &Mgr) {
Mgr.registerChecker<ReturnValueChecker>();
}
-bool ento::shouldRegisterReturnValueChecker(const LangOptions &LO) {
+bool ento::shouldRegisterReturnValueChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp
index 5e305aa709b6..d9dc72ddaa21 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp
@@ -203,6 +203,6 @@ void ento::registerRunLoopAutoreleaseLeakChecker(CheckerManager &mgr) {
mgr.registerChecker<RunLoopAutoreleaseLeakChecker>();
}
-bool ento::shouldRegisterRunLoopAutoreleaseLeakChecker(const LangOptions &LO) {
+bool ento::shouldRegisterRunLoopAutoreleaseLeakChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp
new file mode 100644
index 000000000000..933e0146ff59
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp
@@ -0,0 +1,180 @@
+//===-- STLAlgorithmModeling.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Models STL algorithms.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+#include "Iterator.h"
+
+using namespace clang;
+using namespace ento;
+using namespace iterator;
+
+namespace {
+
+class STLAlgorithmModeling : public Checker<eval::Call> {
+ bool evalFind(CheckerContext &C, const CallExpr *CE) const;
+
+ void Find(CheckerContext &C, const CallExpr *CE, unsigned paramNum) const;
+
+ using FnCheck = bool (STLAlgorithmModeling::*)(CheckerContext &,
+ const CallExpr *) const;
+
+ const CallDescriptionMap<FnCheck> Callbacks = {
+ {{{"std", "find"}, 3}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "find"}, 4}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "find_if"}, 3}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "find_if"}, 4}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "find_if_not"}, 3}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "find_if_not"}, 4}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "find_first_of"}, 4}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "find_first_of"}, 5}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "find_first_of"}, 6}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "find_end"}, 4}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "find_end"}, 5}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "find_end"}, 6}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "lower_bound"}, 3}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "lower_bound"}, 4}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "upper_bound"}, 3}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "upper_bound"}, 4}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "search"}, 3}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "search"}, 4}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "search"}, 5}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "search"}, 6}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "search_n"}, 4}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "search_n"}, 5}, &STLAlgorithmModeling::evalFind},
+ {{{"std", "search_n"}, 6}, &STLAlgorithmModeling::evalFind},
+ };
+
+public:
+ STLAlgorithmModeling() = default;
+
+ bool AggressiveStdFindModeling;
+
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const;
+}; //
+
+bool STLAlgorithmModeling::evalCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return false;
+
+ const FnCheck *Handler = Callbacks.lookup(Call);
+ if (!Handler)
+ return false;
+
+ return (this->**Handler)(C, CE);
+}
+
+bool STLAlgorithmModeling::evalFind(CheckerContext &C,
+ const CallExpr *CE) const {
+ // std::find()-like functions either take their primary range in the first
+ // two parameters, or if the first parameter is "execution policy" then in
+ // the second and third. This means that the second parameter must always be
+ // an iterator.
+ if (!isIteratorType(CE->getArg(1)->getType()))
+ return false;
+
+ // If no "execution policy" parameter is used then the first argument is the
+ // beginning of the range.
+ if (isIteratorType(CE->getArg(0)->getType())) {
+ Find(C, CE, 0);
+ return true;
+ }
+
+ // If "execution policy" parameter is used then the second argument is the
+ // beginning of the range.
+ if (isIteratorType(CE->getArg(2)->getType())) {
+ Find(C, CE, 1);
+ return true;
+ }
+
+ return false;
+}
+
+void STLAlgorithmModeling::Find(CheckerContext &C, const CallExpr *CE,
+ unsigned paramNum) const {
+ auto State = C.getState();
+ auto &SVB = C.getSValBuilder();
+ const auto *LCtx = C.getLocationContext();
+
+ SVal RetVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
+ SVal Param = State->getSVal(CE->getArg(paramNum), LCtx);
+
+ auto StateFound = State->BindExpr(CE, LCtx, RetVal);
+
+ // If we have an iterator position for the range-begin argument then we can
+ // assume that in case of successful search the position of the found element
+ // is not ahead of it.
+ // FIXME: Reverse iterators
+ const auto *Pos = getIteratorPosition(State, Param);
+ if (Pos) {
+ StateFound = createIteratorPosition(StateFound, RetVal, Pos->getContainer(),
+ CE, LCtx, C.blockCount());
+ const auto *NewPos = getIteratorPosition(StateFound, RetVal);
+ assert(NewPos && "Failed to create new iterator position.");
+
+ SVal GreaterOrEqual = SVB.evalBinOp(StateFound, BO_GE,
+ nonloc::SymbolVal(NewPos->getOffset()),
+ nonloc::SymbolVal(Pos->getOffset()),
+ SVB.getConditionType());
+ assert(GreaterOrEqual.getAs<DefinedSVal>() &&
+ "Symbol comparison must be a `DefinedSVal`");
+ StateFound = StateFound->assume(GreaterOrEqual.castAs<DefinedSVal>(), true);
+ }
+
+ Param = State->getSVal(CE->getArg(paramNum + 1), LCtx);
+
+ // If we have an iterator position for the range-end argument then we can
+ // assume that in case of successful search the position of the found element
+ // is ahead of it.
+ // FIXME: Reverse iterators
+ Pos = getIteratorPosition(State, Param);
+ if (Pos) {
+ StateFound = createIteratorPosition(StateFound, RetVal, Pos->getContainer(),
+ CE, LCtx, C.blockCount());
+ const auto *NewPos = getIteratorPosition(StateFound, RetVal);
+ assert(NewPos && "Failed to create new iterator position.");
+
+ SVal Less = SVB.evalBinOp(StateFound, BO_LT,
+ nonloc::SymbolVal(NewPos->getOffset()),
+ nonloc::SymbolVal(Pos->getOffset()),
+ SVB.getConditionType());
+ assert(Less.getAs<DefinedSVal>() &&
+ "Symbol comparison must be a `DefinedSVal`");
+ StateFound = StateFound->assume(Less.castAs<DefinedSVal>(), true);
+ }
+
+ C.addTransition(StateFound);
+
+ if (AggressiveStdFindModeling) {
+ auto StateNotFound = State->BindExpr(CE, LCtx, Param);
+ C.addTransition(StateNotFound);
+ }
+}
+
+} // namespace
+
+void ento::registerSTLAlgorithmModeling(CheckerManager &Mgr) {
+ auto *Checker = Mgr.registerChecker<STLAlgorithmModeling>();
+ Checker->AggressiveStdFindModeling =
+ Mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker,
+ "AggressiveStdFindModeling");
+}
+
+bool ento::shouldRegisterSTLAlgorithmModeling(const CheckerManager &mgr) {
+ return true;
+}
+
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
index 8193bcbef4cd..8d380ed1b93d 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
@@ -271,6 +271,6 @@ void ento::registerSimpleStreamChecker(CheckerManager &mgr) {
}
// This checker should be enabled regardless of how language options are set.
-bool ento::shouldRegisterSimpleStreamChecker(const LangOptions &LO) {
+bool ento::shouldRegisterSimpleStreamChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h
new file mode 100644
index 000000000000..ec43a23e30a9
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h
@@ -0,0 +1,33 @@
+//=== SmartPtr.h - Tracking smart pointer state. -------------------*- 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 smart pointer modeling. It allows
+// dependent checkers to figure out if an smart pointer is null or not.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_SMARTPTR_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_SMARTPTR_H
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+
+namespace clang {
+namespace ento {
+namespace smartptr {
+
+/// Returns true if the event call is on smart pointer.
+bool isStdSmartPtrCall(const CallEvent &Call);
+
+/// Returns whether the smart pointer is null or not.
+bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion);
+
+} // namespace smartptr
+} // namespace ento
+} // namespace clang
+
+#endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_SMARTPTR_H
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp
new file mode 100644
index 000000000000..7bb25f397d01
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp
@@ -0,0 +1,80 @@
+// SmartPtrChecker.cpp - Check for smart pointer dereference - 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 check for null dereference of C++ smart
+// pointer.
+//
+//===----------------------------------------------------------------------===//
+#include "SmartPtr.h"
+
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/Type.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class SmartPtrChecker : public Checker<check::PreCall> {
+ BugType NullDereferenceBugType{this, "Null SmartPtr dereference",
+ "C++ Smart Pointer"};
+
+public:
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+
+private:
+ void reportBug(CheckerContext &C, const CallEvent &Call) const;
+};
+} // end of anonymous namespace
+
+void SmartPtrChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (!smartptr::isStdSmartPtrCall(Call))
+ return;
+ ProgramStateRef State = C.getState();
+ const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call);
+ if (!OC)
+ return;
+ const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion();
+ if (!ThisRegion)
+ return;
+
+ OverloadedOperatorKind OOK = OC->getOverloadedOperator();
+ if (OOK == OO_Star || OOK == OO_Arrow) {
+ if (smartptr::isNullSmartPtr(State, ThisRegion))
+ reportBug(C, Call);
+ }
+}
+
+void SmartPtrChecker::reportBug(CheckerContext &C,
+ const CallEvent &Call) const {
+ ExplodedNode *ErrNode = C.generateErrorNode();
+ if (!ErrNode)
+ return;
+
+ auto R = std::make_unique<PathSensitiveBugReport>(
+ NullDereferenceBugType, "Dereference of null smart pointer", ErrNode);
+ C.emitReport(std::move(R));
+}
+
+void ento::registerSmartPtrChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<SmartPtrChecker>();
+}
+
+bool ento::shouldRegisterSmartPtrChecker(const CheckerManager &mgr) {
+ const LangOptions &LO = mgr.getLangOpts();
+ return LO.CPlusPlus;
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
index fd372aafa50d..bcc7d4103c1c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
@@ -12,27 +12,81 @@
//===----------------------------------------------------------------------===//
#include "Move.h"
+#include "SmartPtr.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/AST/Type.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
using namespace clang;
using namespace ento;
namespace {
-class SmartPtrModeling : public Checker<eval::Call> {
+class SmartPtrModeling : public Checker<eval::Call, check::DeadSymbols> {
+
bool isNullAfterMoveMethod(const CallEvent &Call) const;
public:
+ // Whether the checker should model for null dereferences of smart pointers.
+ DefaultBool ModelSmartPtrDereference;
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+
+private:
+ ProgramStateRef updateTrackedRegion(const CallEvent &Call, CheckerContext &C,
+ const MemRegion *ThisValRegion) const;
+ void handleReset(const CallEvent &Call, CheckerContext &C) const;
+ void handleRelease(const CallEvent &Call, CheckerContext &C) const;
+ void handleSwap(const CallEvent &Call, CheckerContext &C) const;
+
+ using SmartPtrMethodHandlerFn =
+ void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
+ CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
+ {{"reset"}, &SmartPtrModeling::handleReset},
+ {{"release"}, &SmartPtrModeling::handleRelease},
+ {{"swap", 1}, &SmartPtrModeling::handleSwap}};
};
} // end of anonymous namespace
+REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
+
+// Define the inter-checker API.
+namespace clang {
+namespace ento {
+namespace smartptr {
+bool isStdSmartPtrCall(const CallEvent &Call) {
+ const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
+ if (!MethodDecl || !MethodDecl->getParent())
+ return false;
+
+ const auto *RecordDecl = MethodDecl->getParent();
+ if (!RecordDecl || !RecordDecl->getDeclContext()->isStdNamespace())
+ return false;
+
+ if (RecordDecl->getDeclName().isIdentifier()) {
+ StringRef Name = RecordDecl->getName();
+ return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr";
+ }
+ return false;
+}
+
+bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
+ const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
+ return InnerPointVal && InnerPointVal->isZeroConstant();
+}
+} // namespace smartptr
+} // namespace ento
+} // namespace clang
+
bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const {
// TODO: Update CallDescription to support anonymous calls?
// TODO: Handle other methods, such as .get() or .release().
@@ -44,29 +98,136 @@ bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const {
bool SmartPtrModeling::evalCall(const CallEvent &Call,
CheckerContext &C) const {
- if (!isNullAfterMoveMethod(Call))
+
+ if (!smartptr::isStdSmartPtrCall(Call))
return false;
- ProgramStateRef State = C.getState();
- const MemRegion *ThisR =
- cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
+ if (isNullAfterMoveMethod(Call)) {
+ 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;
+ }
- if (!move::isMovedFrom(State, ThisR)) {
- // TODO: Model this case as well. At least, avoid invalidation of globals.
+ if (!ModelSmartPtrDereference)
return false;
+
+ if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
+ if (CC->getDecl()->isCopyOrMoveConstructor())
+ return false;
+
+ const MemRegion *ThisValRegion = CC->getCXXThisVal().getAsRegion();
+ if (!ThisValRegion)
+ return false;
+
+ auto State = updateTrackedRegion(Call, C, ThisValRegion);
+ C.addTransition(State);
+ return true;
+ }
+
+ const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
+ if (!Handler)
+ return false;
+ (this->**Handler)(Call, C);
+
+ return C.isDifferent();
+}
+
+void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ // Clean up dead regions from the region map.
+ TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
+ for (auto E : TrackedRegions) {
+ const MemRegion *Region = E.first;
+ bool IsRegDead = !SymReaper.isLiveRegion(Region);
+
+ if (IsRegDead)
+ State = State->remove<TrackedRegionMap>(Region);
+ }
+ C.addTransition(State);
+}
+
+void SmartPtrModeling::handleReset(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
+ if (!IC)
+ return;
+
+ const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion();
+ if (!ThisValRegion)
+ return;
+ auto State = updateTrackedRegion(Call, C, ThisValRegion);
+ C.addTransition(State);
+ // TODO: Make sure to ivalidate the the region in the Store if we don't have
+ // time to model all methods.
+}
+
+void SmartPtrModeling::handleRelease(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
+ if (!IC)
+ return;
+
+ const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion();
+ if (!ThisValRegion)
+ return;
+
+ auto State = updateTrackedRegion(Call, C, ThisValRegion);
+
+ const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisValRegion);
+ if (InnerPointVal) {
+ State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
+ *InnerPointVal);
+ }
+ C.addTransition(State);
+ // TODO: Add support to enable MallocChecker to start tracking the raw
+ // pointer.
+}
+
+void SmartPtrModeling::handleSwap(const CallEvent &Call,
+ CheckerContext &C) const {
+ // TODO: Add support to handle swap method.
+}
+
+ProgramStateRef
+SmartPtrModeling::updateTrackedRegion(const CallEvent &Call, CheckerContext &C,
+ const MemRegion *ThisValRegion) const {
+ // TODO: Refactor and clean up handling too many things.
+ ProgramStateRef State = C.getState();
+ auto NumArgs = Call.getNumArgs();
+
+ if (NumArgs == 0) {
+ auto NullSVal = C.getSValBuilder().makeNull();
+ State = State->set<TrackedRegionMap>(ThisValRegion, NullSVal);
+ } else if (NumArgs == 1) {
+ auto ArgVal = Call.getArgSVal(0);
+ assert(Call.getArgExpr(0)->getType()->isPointerType() &&
+ "Adding a non pointer value to TrackedRegionMap");
+ State = State->set<TrackedRegionMap>(ThisValRegion, ArgVal);
}
- // 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;
+ return State;
}
void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
- Mgr.registerChecker<SmartPtrModeling>();
+ auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
+ Checker->ModelSmartPtrDereference =
+ Mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ Checker, "ModelSmartPtrDereference");
}
-bool ento::shouldRegisterSmartPtrModeling(const LangOptions &LO) {
+bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
+ const LangOptions &LO = mgr.getLangOpts();
return LO.CPlusPlus;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index 7285d27495a7..b5c9356322fc 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -43,6 +43,7 @@ public:
};
DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ CheckerNameRef CheckNames[CK_NumCheckKinds];
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
@@ -156,7 +157,8 @@ void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
return;
if (!BT_returnstack)
BT_returnstack = std::make_unique<BuiltinBug>(
- this, "Return of address to stack-allocated memory");
+ CheckNames[CK_StackAddrEscapeChecker],
+ "Return of address to stack-allocated memory");
// Generate a report for this bug.
SmallString<128> buf;
llvm::raw_svector_ostream os(buf);
@@ -195,7 +197,8 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
continue;
if (!BT_capturedstackasync)
BT_capturedstackasync = std::make_unique<BuiltinBug>(
- this, "Address of stack-allocated memory is captured");
+ CheckNames[CK_StackAddrAsyncEscapeChecker],
+ "Address of stack-allocated memory is captured");
SmallString<128> Buf;
llvm::raw_svector_ostream Out(Buf);
SourceRange Range = genName(Out, Region, C.getASTContext());
@@ -218,7 +221,8 @@ void StackAddrEscapeChecker::checkReturnedBlockCaptures(
continue;
if (!BT_capturedstackret)
BT_capturedstackret = std::make_unique<BuiltinBug>(
- this, "Address of stack-allocated memory is captured");
+ CheckNames[CK_StackAddrEscapeChecker],
+ "Address of stack-allocated memory is captured");
SmallString<128> Buf;
llvm::raw_svector_ostream Out(Buf);
SourceRange Range = genName(Out, Region, C.getASTContext());
@@ -277,7 +281,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
// The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied
// so the stack address is not escaping here.
- if (auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
+ if (const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
if (isa<BlockDataRegion>(R) &&
ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) {
return;
@@ -333,7 +337,8 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
if (!BT_stackleak)
BT_stackleak = std::make_unique<BuiltinBug>(
- this, "Stack address stored into global variable",
+ CheckNames[CK_StackAddrEscapeChecker],
+ "Stack address stored into global variable",
"Stack address was saved into a global variable. "
"This is dangerous because the address will become "
"invalid after returning from the function");
@@ -365,20 +370,19 @@ void ento::registerStackAddrEscapeBase(CheckerManager &mgr) {
mgr.registerChecker<StackAddrEscapeChecker>();
}
-bool ento::shouldRegisterStackAddrEscapeBase(const LangOptions &LO) {
+bool ento::shouldRegisterStackAddrEscapeBase(const CheckerManager &mgr) {
return true;
}
#define REGISTER_CHECKER(name) \
void ento::register##name(CheckerManager &Mgr) { \
- StackAddrEscapeChecker *Chk = \
- Mgr.getChecker<StackAddrEscapeChecker>(); \
+ StackAddrEscapeChecker *Chk = Mgr.getChecker<StackAddrEscapeChecker>(); \
Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \
+ Chk->CheckNames[StackAddrEscapeChecker::CK_##name] = \
+ Mgr.getCurrentCheckerName(); \
} \
\
- bool ento::shouldRegister##name(const LangOptions &LO) { \
- return true; \
- }
+ bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
REGISTER_CHECKER(StackAddrEscapeChecker)
REGISTER_CHECKER(StackAddrAsyncEscapeChecker)
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index 2cdee8da375e..8b575f4f4759 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -7,9 +7,8 @@
//===----------------------------------------------------------------------===//
//
// This checker improves modeling of a few simple library functions.
-// It does not generate warnings.
//
-// This checker provides a specification format - `FunctionSummaryTy' - and
+// This checker provides a specification format - `Summary' - and
// contains descriptions of some library functions in this format. Each
// specification contains a list of branches for splitting the program state
// upon call, and range constraints on argument and return-value symbols that
@@ -21,7 +20,7 @@
// consider standard C function `ispunct(int x)', which returns a non-zero value
// iff `x' is a punctuation character, that is, when `x' is in range
// ['!', '/'] [':', '@'] U ['[', '\`'] U ['{', '~'].
-// `FunctionSummaryTy' provides only two branches for this function. However,
+// `Summary' provides only two branches for this function. However,
// any attempt to describe this range with if-statements in the body farm
// would result in many more branches. Because each branch needs to be analyzed
// independently, this significantly reduces performance. Additionally,
@@ -30,13 +29,13 @@
// which may lead to false positives because considering this particular path
// was not consciously intended, and therefore it might have been unreachable.
//
-// This checker uses eval::Call for modeling "pure" functions, for which
-// their `FunctionSummaryTy' is a precise model. This avoids unnecessary
-// invalidation passes. Conflicts with other checkers are unlikely because
-// if the function has no other effects, other checkers would probably never
-// want to improve upon the modeling done by this checker.
+// This checker uses eval::Call for modeling pure functions (functions without
+// side effets), for which their `Summary' is a precise model. This avoids
+// unnecessary invalidation passes. Conflicts with other checkers are unlikely
+// because if the function has no other effects, other checkers would probably
+// never want to improve upon the modeling done by this checker.
//
-// Non-"pure" functions, for which only partial improvement over the default
+// Non-pure functions, for which only partial improvement over the default
// behavior is expected, are modeled via check::PostCall, non-intrusively.
//
// The following standard C functions are currently supported:
@@ -51,203 +50,461 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
using namespace clang;
using namespace clang::ento;
namespace {
-class StdLibraryFunctionsChecker : public Checker<check::PostCall, eval::Call> {
- /// Below is a series of typedefs necessary to define function specs.
- /// We avoid nesting types here because each additional qualifier
- /// would need to be repeated in every function spec.
- struct FunctionSummaryTy;
+class StdLibraryFunctionsChecker
+ : public Checker<check::PreCall, check::PostCall, eval::Call> {
+
+ class Summary;
/// Specify how much the analyzer engine should entrust modeling this function
/// to us. If he doesn't, he performs additional invalidations.
- enum InvalidationKindTy { NoEvalCall, EvalCallAsPure };
-
- /// A pair of ValueRangeKindTy and IntRangeVectorTy would describe a range
- /// imposed on a particular argument or return value symbol.
- ///
- /// Given a range, should the argument stay inside or outside this range?
- /// The special `ComparesToArgument' value indicates that we should
- /// impose a constraint that involves other argument or return value symbols.
- enum ValueRangeKindTy { OutOfRange, WithinRange, ComparesToArgument };
+ enum InvalidationKind { NoEvalCall, EvalCallAsPure };
// The universal integral type to use in value range descriptions.
// Unsigned to make sure overflows are well-defined.
- typedef uint64_t RangeIntTy;
+ typedef uint64_t RangeInt;
/// Normally, describes a single range constraint, eg. {{0, 1}, {3, 4}} is
/// a non-negative integer, which less than 5 and not equal to 2. For
/// `ComparesToArgument', holds information about how exactly to compare to
/// the argument.
- typedef std::vector<std::pair<RangeIntTy, RangeIntTy>> IntRangeVectorTy;
+ typedef std::vector<std::pair<RangeInt, RangeInt>> IntRangeVector;
/// A reference to an argument or return value by its number.
/// ArgNo in CallExpr and CallEvent is defined as Unsigned, but
/// obviously uint32_t should be enough for all practical purposes.
- typedef uint32_t ArgNoTy;
- static const ArgNoTy Ret = std::numeric_limits<ArgNoTy>::max();
-
- /// Incapsulates a single range on a single symbol within a branch.
- class ValueRange {
- ArgNoTy ArgNo; // Argument to which we apply the range.
- ValueRangeKindTy Kind; // Kind of range definition.
- IntRangeVectorTy Args; // Polymorphic arguments.
-
+ typedef uint32_t ArgNo;
+ static const ArgNo Ret;
+
+ class ValueConstraint;
+
+ // Pointer to the ValueConstraint. We need a copyable, polymorphic and
+ // default initialize able type (vector needs that). A raw pointer was good,
+ // however, we cannot default initialize that. unique_ptr makes the Summary
+ // class non-copyable, therefore not an option. Releasing the copyability
+ // requirement would render the initialization of the Summary map infeasible.
+ using ValueConstraintPtr = std::shared_ptr<ValueConstraint>;
+
+ /// Polymorphic base class that represents a constraint on a given argument
+ /// (or return value) of a function. Derived classes implement different kind
+ /// of constraints, e.g range constraints or correlation between two
+ /// arguments.
+ class ValueConstraint {
public:
- ValueRange(ArgNoTy ArgNo, ValueRangeKindTy Kind,
- const IntRangeVectorTy &Args)
- : ArgNo(ArgNo), Kind(Kind), Args(Args) {}
-
- ArgNoTy getArgNo() const { return ArgNo; }
- ValueRangeKindTy getKind() const { return Kind; }
-
- BinaryOperator::Opcode getOpcode() const {
- assert(Kind == ComparesToArgument);
- assert(Args.size() == 1);
- BinaryOperator::Opcode Op =
- static_cast<BinaryOperator::Opcode>(Args[0].first);
- assert(BinaryOperator::isComparisonOp(Op) &&
- "Only comparison ops are supported for ComparesToArgument");
- return Op;
+ ValueConstraint(ArgNo ArgN) : ArgN(ArgN) {}
+ virtual ~ValueConstraint() {}
+ /// Apply the effects of the constraint on the given program state. If null
+ /// is returned then the constraint is not feasible.
+ virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
+ const Summary &Summary,
+ CheckerContext &C) const = 0;
+ virtual ValueConstraintPtr negate() const {
+ llvm_unreachable("Not implemented");
+ };
+
+ // Check whether the constraint is malformed or not. It is malformed if the
+ // specified argument has a mismatch with the given FunctionDecl (e.g. the
+ // arg number is out-of-range of the function's argument list).
+ bool checkValidity(const FunctionDecl *FD) const {
+ const bool ValidArg = ArgN == Ret || ArgN < FD->getNumParams();
+ assert(ValidArg && "Arg out of range!");
+ if (!ValidArg)
+ return false;
+ // Subclasses may further refine the validation.
+ return checkSpecificValidity(FD);
}
+ ArgNo getArgNo() const { return ArgN; }
- ArgNoTy getOtherArgNo() const {
- assert(Kind == ComparesToArgument);
- assert(Args.size() == 1);
- return static_cast<ArgNoTy>(Args[0].second);
+ protected:
+ ArgNo ArgN; // Argument to which we apply the constraint.
+
+ /// Do polymorphic sanity check on the constraint.
+ virtual bool checkSpecificValidity(const FunctionDecl *FD) const {
+ return true;
}
+ };
+
+ /// Given a range, should the argument stay inside or outside this range?
+ enum RangeKind { OutOfRange, WithinRange };
+
+ /// Encapsulates a single range on a single symbol within a branch.
+ class RangeConstraint : public ValueConstraint {
+ RangeKind Kind; // Kind of range definition.
+ IntRangeVector Args; // Polymorphic arguments.
- const IntRangeVectorTy &getRanges() const {
- assert(Kind != ComparesToArgument);
+ public:
+ RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Args)
+ : ValueConstraint(ArgN), Kind(Kind), Args(Args) {}
+
+ const IntRangeVector &getRanges() const {
return Args;
}
- // We avoid creating a virtual apply() method because
- // it makes initializer lists harder to write.
private:
- ProgramStateRef
- applyAsOutOfRange(ProgramStateRef State, const CallEvent &Call,
- const FunctionSummaryTy &Summary) const;
- ProgramStateRef
- applyAsWithinRange(ProgramStateRef State, const CallEvent &Call,
- const FunctionSummaryTy &Summary) const;
- ProgramStateRef
- applyAsComparesToArgument(ProgramStateRef State, const CallEvent &Call,
- const FunctionSummaryTy &Summary) const;
-
+ ProgramStateRef applyAsOutOfRange(ProgramStateRef State,
+ const CallEvent &Call,
+ const Summary &Summary) const;
+ ProgramStateRef applyAsWithinRange(ProgramStateRef State,
+ const CallEvent &Call,
+ const Summary &Summary) const;
public:
ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
- const FunctionSummaryTy &Summary) const {
+ const Summary &Summary,
+ CheckerContext &C) const override {
switch (Kind) {
case OutOfRange:
return applyAsOutOfRange(State, Call, Summary);
case WithinRange:
return applyAsWithinRange(State, Call, Summary);
- case ComparesToArgument:
- return applyAsComparesToArgument(State, Call, Summary);
}
- llvm_unreachable("Unknown ValueRange kind!");
+ llvm_unreachable("Unknown range kind!");
+ }
+
+ ValueConstraintPtr negate() const override {
+ RangeConstraint Tmp(*this);
+ switch (Kind) {
+ case OutOfRange:
+ Tmp.Kind = WithinRange;
+ break;
+ case WithinRange:
+ Tmp.Kind = OutOfRange;
+ break;
+ }
+ return std::make_shared<RangeConstraint>(Tmp);
+ }
+
+ bool checkSpecificValidity(const FunctionDecl *FD) const override {
+ const bool ValidArg =
+ getArgType(FD, ArgN)->isIntegralType(FD->getASTContext());
+ assert(ValidArg &&
+ "This constraint should be applied on an integral type");
+ return ValidArg;
}
};
- /// The complete list of ranges that defines a single branch.
- typedef std::vector<ValueRange> ValueRangeSet;
+ class ComparisonConstraint : public ValueConstraint {
+ BinaryOperator::Opcode Opcode;
+ ArgNo OtherArgN;
+
+ public:
+ ComparisonConstraint(ArgNo ArgN, BinaryOperator::Opcode Opcode,
+ ArgNo OtherArgN)
+ : ValueConstraint(ArgN), Opcode(Opcode), OtherArgN(OtherArgN) {}
+ ArgNo getOtherArgNo() const { return OtherArgN; }
+ BinaryOperator::Opcode getOpcode() const { return Opcode; }
+ ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
+ const Summary &Summary,
+ CheckerContext &C) const override;
+ };
+
+ class NotNullConstraint : public ValueConstraint {
+ using ValueConstraint::ValueConstraint;
+ // This variable has a role when we negate the constraint.
+ bool CannotBeNull = true;
- /// Includes information about function prototype (which is necessary to
- /// ensure we're modeling the right function and casting values properly),
- /// approach to invalidation, and a list of branches - essentially, a list
- /// of list of ranges - essentially, a list of lists of lists of segments.
- struct FunctionSummaryTy {
- const std::vector<QualType> ArgTypes;
- const QualType RetType;
- const InvalidationKindTy InvalidationKind;
- const std::vector<ValueRangeSet> Ranges;
+ public:
+ ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
+ const Summary &Summary,
+ CheckerContext &C) const override {
+ SVal V = getArgSVal(Call, getArgNo());
+ if (V.isUndef())
+ return State;
+
+ DefinedOrUnknownSVal L = V.castAs<DefinedOrUnknownSVal>();
+ if (!L.getAs<Loc>())
+ return State;
+
+ return State->assume(L, CannotBeNull);
+ }
+
+ ValueConstraintPtr negate() const override {
+ NotNullConstraint Tmp(*this);
+ Tmp.CannotBeNull = !this->CannotBeNull;
+ return std::make_shared<NotNullConstraint>(Tmp);
+ }
+
+ bool checkSpecificValidity(const FunctionDecl *FD) const override {
+ const bool ValidArg = getArgType(FD, ArgN)->isPointerType();
+ assert(ValidArg &&
+ "This constraint should be applied only on a pointer type");
+ return ValidArg;
+ }
+ };
+
+ // Represents a buffer argument with an additional size argument.
+ // E.g. the first two arguments here:
+ // ctime_s(char *buffer, rsize_t bufsz, const time_t *time);
+ // Another example:
+ // size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
+ // // Here, ptr is the buffer, and its minimum size is `size * nmemb`.
+ class BufferSizeConstraint : public ValueConstraint {
+ // The argument which holds the size of the buffer.
+ ArgNo SizeArgN;
+ // The argument which is a multiplier to size. This is set in case of
+ // `fread` like functions where the size is computed as a multiplication of
+ // two arguments.
+ llvm::Optional<ArgNo> SizeMultiplierArgN;
+ // The operator we use in apply. This is negated in negate().
+ BinaryOperator::Opcode Op = BO_LE;
+
+ public:
+ BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize)
+ : ValueConstraint(Buffer), SizeArgN(BufSize) {}
+
+ BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize, ArgNo BufSizeMultiplier)
+ : ValueConstraint(Buffer), SizeArgN(BufSize),
+ SizeMultiplierArgN(BufSizeMultiplier) {}
+
+ ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
+ const Summary &Summary,
+ CheckerContext &C) const override {
+ SValBuilder &SvalBuilder = C.getSValBuilder();
+ // The buffer argument.
+ SVal BufV = getArgSVal(Call, getArgNo());
+ // The size argument.
+ SVal SizeV = getArgSVal(Call, SizeArgN);
+ // Multiply with another argument if given.
+ if (SizeMultiplierArgN) {
+ SVal SizeMulV = getArgSVal(Call, *SizeMultiplierArgN);
+ SizeV = SvalBuilder.evalBinOp(State, BO_Mul, SizeV, SizeMulV,
+ Summary.getArgType(SizeArgN));
+ }
+ // The dynamic size of the buffer argument, got from the analyzer engine.
+ SVal BufDynSize = getDynamicSizeWithOffset(State, BufV);
+
+ SVal Feasible = SvalBuilder.evalBinOp(State, Op, SizeV, BufDynSize,
+ SvalBuilder.getContext().BoolTy);
+ if (auto F = Feasible.getAs<DefinedOrUnknownSVal>())
+ return State->assume(*F, true);
+
+ // We can get here only if the size argument or the dynamic size is
+ // undefined. But the dynamic size should never be undefined, only
+ // unknown. So, here, the size of the argument is undefined, i.e. we
+ // cannot apply the constraint. Actually, other checkers like
+ // CallAndMessage should catch this situation earlier, because we call a
+ // function with an uninitialized argument.
+ llvm_unreachable("Size argument or the dynamic size is Undefined");
+ }
+
+ ValueConstraintPtr negate() const override {
+ BufferSizeConstraint Tmp(*this);
+ Tmp.Op = BinaryOperator::negateComparisonOp(Op);
+ return std::make_shared<BufferSizeConstraint>(Tmp);
+ }
+ };
+
+ /// The complete list of constraints that defines a single branch.
+ typedef std::vector<ValueConstraintPtr> ConstraintSet;
+
+ using ArgTypes = std::vector<QualType>;
+
+ // A placeholder type, we use it whenever we do not care about the concrete
+ // type in a Signature.
+ const QualType Irrelevant{};
+ bool static isIrrelevant(QualType T) { return T.isNull(); }
+
+ // The signature of a function we want to describe with a summary. This is a
+ // concessive signature, meaning there may be irrelevant types in the
+ // signature which we do not check against a function with concrete types.
+ struct Signature {
+ const ArgTypes ArgTys;
+ const QualType RetTy;
+ Signature(ArgTypes ArgTys, QualType RetTy) : ArgTys(ArgTys), RetTy(RetTy) {
+ assertRetTypeSuitableForSignature(RetTy);
+ for (size_t I = 0, E = ArgTys.size(); I != E; ++I) {
+ QualType ArgTy = ArgTys[I];
+ assertArgTypeSuitableForSignature(ArgTy);
+ }
+ }
+ bool matches(const FunctionDecl *FD) const;
private:
- static void assertTypeSuitableForSummary(QualType T) {
- assert(!T->isVoidType() &&
- "We should have had no significant void types in the spec");
- assert(T.isCanonical() &&
+ static void assertArgTypeSuitableForSignature(QualType T) {
+ assert((T.isNull() || !T->isVoidType()) &&
+ "We should have no void types in the spec");
+ assert((T.isNull() || T.isCanonical()) &&
+ "We should only have canonical types in the spec");
+ }
+ static void assertRetTypeSuitableForSignature(QualType T) {
+ assert((T.isNull() || T.isCanonical()) &&
"We should only have canonical types in the spec");
- // FIXME: lift this assert (but not the ones above!)
- assert(T->isIntegralOrEnumerationType() &&
- "We only support integral ranges in the spec");
}
+ };
+
+ static QualType getArgType(const FunctionDecl *FD, ArgNo ArgN) {
+ assert(FD && "Function must be set");
+ QualType T = (ArgN == Ret)
+ ? FD->getReturnType().getCanonicalType()
+ : FD->getParamDecl(ArgN)->getType().getCanonicalType();
+ return T;
+ }
+
+ using Cases = std::vector<ConstraintSet>;
+
+ /// A summary includes information about
+ /// * function prototype (signature)
+ /// * approach to invalidation,
+ /// * a list of branches - a list of list of ranges -
+ /// A branch represents a path in the exploded graph of a function (which
+ /// is a tree). So, a branch is a series of assumptions. In other words,
+ /// branches represent split states and additional assumptions on top of
+ /// the splitting assumption.
+ /// For example, consider the branches in `isalpha(x)`
+ /// Branch 1)
+ /// x is in range ['A', 'Z'] or in ['a', 'z']
+ /// then the return value is not 0. (I.e. out-of-range [0, 0])
+ /// Branch 2)
+ /// x is out-of-range ['A', 'Z'] and out-of-range ['a', 'z']
+ /// then the return value is 0.
+ /// * a list of argument constraints, that must be true on every branch.
+ /// If these constraints are not satisfied that means a fatal error
+ /// usually resulting in undefined behaviour.
+ ///
+ /// Application of a summary:
+ /// The signature and argument constraints together contain information
+ /// about which functions are handled by the summary. The signature can use
+ /// "wildcards", i.e. Irrelevant types. Irrelevant type of a parameter in
+ /// a signature means that type is not compared to the type of the parameter
+ /// in the found FunctionDecl. Argument constraints may specify additional
+ /// rules for the given parameter's type, those rules are checked once the
+ /// signature is matched.
+ class Summary {
+ const Signature Sign;
+ const InvalidationKind InvalidationKd;
+ Cases CaseConstraints;
+ ConstraintSet ArgConstraints;
+
+ // The function to which the summary applies. This is set after lookup and
+ // match to the signature.
+ const FunctionDecl *FD = nullptr;
public:
- QualType getArgType(ArgNoTy ArgNo) const {
- QualType T = (ArgNo == Ret) ? RetType : ArgTypes[ArgNo];
- assertTypeSuitableForSummary(T);
- return T;
+ Summary(ArgTypes ArgTys, QualType RetTy, InvalidationKind InvalidationKd)
+ : Sign(ArgTys, RetTy), InvalidationKd(InvalidationKd) {}
+
+ Summary &Case(ConstraintSet&& CS) {
+ CaseConstraints.push_back(std::move(CS));
+ return *this;
+ }
+ Summary &ArgConstraint(ValueConstraintPtr VC) {
+ ArgConstraints.push_back(VC);
+ return *this;
}
- /// Try our best to figure out if the call expression is the call of
- /// *the* library function to which this specification applies.
- bool matchesCall(const CallExpr *CE) const;
- };
+ InvalidationKind getInvalidationKd() const { return InvalidationKd; }
+ const Cases &getCaseConstraints() const { return CaseConstraints; }
+ const ConstraintSet &getArgConstraints() const { return ArgConstraints; }
- // The same function (as in, function identifier) may have different
- // summaries assigned to it, with different argument and return value types.
- // We call these "variants" of the function. This can be useful for handling
- // C++ function overloads, and also it can be used when the same function
- // may have different definitions on different platforms.
- typedef std::vector<FunctionSummaryTy> FunctionVariantsTy;
+ QualType getArgType(ArgNo ArgN) const {
+ return StdLibraryFunctionsChecker::getArgType(FD, ArgN);
+ }
+
+ // Returns true if the summary should be applied to the given function.
+ // And if yes then store the function declaration.
+ bool matchesAndSet(const FunctionDecl *FD) {
+ bool Result = Sign.matches(FD) && validateByConstraints(FD);
+ if (Result) {
+ assert(!this->FD && "FD must not be set more than once");
+ this->FD = FD;
+ }
+ return Result;
+ }
+
+ private:
+ // Once we know the exact type of the function then do sanity check on all
+ // the given constraints.
+ bool validateByConstraints(const FunctionDecl *FD) const {
+ for (const ConstraintSet &Case : CaseConstraints)
+ for (const ValueConstraintPtr &Constraint : Case)
+ if (!Constraint->checkValidity(FD))
+ return false;
+ for (const ValueConstraintPtr &Constraint : ArgConstraints)
+ if (!Constraint->checkValidity(FD))
+ return false;
+ return true;
+ }
+ };
// The map of all functions supported by the checker. It is initialized
// lazily, and it doesn't change after initialization.
- typedef llvm::StringMap<FunctionVariantsTy> FunctionSummaryMapTy;
- mutable FunctionSummaryMapTy FunctionSummaryMap;
+ using FunctionSummaryMapType = llvm::DenseMap<const FunctionDecl *, Summary>;
+ mutable FunctionSummaryMapType FunctionSummaryMap;
- // Auxiliary functions to support ArgNoTy within all structures
- // in a unified manner.
- static QualType getArgType(const FunctionSummaryTy &Summary, ArgNoTy ArgNo) {
- return Summary.getArgType(ArgNo);
- }
- static QualType getArgType(const CallEvent &Call, ArgNoTy ArgNo) {
- return ArgNo == Ret ? Call.getResultType().getCanonicalType()
- : Call.getArgExpr(ArgNo)->getType().getCanonicalType();
- }
- static QualType getArgType(const CallExpr *CE, ArgNoTy ArgNo) {
- return ArgNo == Ret ? CE->getType().getCanonicalType()
- : CE->getArg(ArgNo)->getType().getCanonicalType();
- }
- static SVal getArgSVal(const CallEvent &Call, ArgNoTy ArgNo) {
- return ArgNo == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgNo);
+ mutable std::unique_ptr<BugType> BT_InvalidArg;
+
+ static SVal getArgSVal(const CallEvent &Call, ArgNo ArgN) {
+ return ArgN == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgN);
}
public:
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
-private:
- Optional<FunctionSummaryTy> findFunctionSummary(const FunctionDecl *FD,
- const CallExpr *CE,
- CheckerContext &C) const;
+ enum CheckKind {
+ CK_StdCLibraryFunctionArgsChecker,
+ CK_StdCLibraryFunctionsTesterChecker,
+ CK_NumCheckKinds
+ };
+ DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ CheckerNameRef CheckNames[CK_NumCheckKinds];
+
+ bool DisplayLoadedSummaries = false;
+ bool ModelPOSIX = false;
- void initFunctionSummaries(BasicValueFactory &BVF) const;
+private:
+ Optional<Summary> findFunctionSummary(const FunctionDecl *FD,
+ CheckerContext &C) const;
+ Optional<Summary> findFunctionSummary(const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void initFunctionSummaries(CheckerContext &C) const;
+
+ void reportBug(const CallEvent &Call, ExplodedNode *N,
+ CheckerContext &C) const {
+ if (!ChecksEnabled[CK_StdCLibraryFunctionArgsChecker])
+ return;
+ // TODO Add detailed diagnostic.
+ StringRef Msg = "Function argument constraint is not satisfied";
+ if (!BT_InvalidArg)
+ BT_InvalidArg = std::make_unique<BugType>(
+ CheckNames[CK_StdCLibraryFunctionArgsChecker],
+ "Unsatisfied argument constraints", categories::LogicError);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT_InvalidArg, Msg, N);
+ bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *R);
+ C.emitReport(std::move(R));
+ }
};
+
+const StdLibraryFunctionsChecker::ArgNo StdLibraryFunctionsChecker::Ret =
+ std::numeric_limits<ArgNo>::max();
+
} // end of anonymous namespace
-ProgramStateRef StdLibraryFunctionsChecker::ValueRange::applyAsOutOfRange(
+ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsOutOfRange(
ProgramStateRef State, const CallEvent &Call,
- const FunctionSummaryTy &Summary) const {
+ const Summary &Summary) const {
ProgramStateManager &Mgr = State->getStateManager();
SValBuilder &SVB = Mgr.getSValBuilder();
BasicValueFactory &BVF = SVB.getBasicValueFactory();
ConstraintManager &CM = Mgr.getConstraintManager();
- QualType T = getArgType(Summary, getArgNo());
+ QualType T = Summary.getArgType(getArgNo());
SVal V = getArgSVal(Call, getArgNo());
if (auto N = V.getAs<NonLoc>()) {
- const IntRangeVectorTy &R = getRanges();
+ const IntRangeVector &R = getRanges();
size_t E = R.size();
for (size_t I = 0; I != E; ++I) {
const llvm::APSInt &Min = BVF.getValue(R[I].first, T);
@@ -262,23 +519,28 @@ ProgramStateRef StdLibraryFunctionsChecker::ValueRange::applyAsOutOfRange(
return State;
}
-ProgramStateRef
-StdLibraryFunctionsChecker::ValueRange::applyAsWithinRange(
+ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsWithinRange(
ProgramStateRef State, const CallEvent &Call,
- const FunctionSummaryTy &Summary) const {
+ const Summary &Summary) const {
ProgramStateManager &Mgr = State->getStateManager();
SValBuilder &SVB = Mgr.getSValBuilder();
BasicValueFactory &BVF = SVB.getBasicValueFactory();
ConstraintManager &CM = Mgr.getConstraintManager();
- QualType T = getArgType(Summary, getArgNo());
+ QualType T = Summary.getArgType(getArgNo());
SVal V = getArgSVal(Call, getArgNo());
// "WithinRange R" is treated as "outside [T_MIN, T_MAX] \ R".
// We cut off [T_MIN, min(R) - 1] and [max(R) + 1, T_MAX] if necessary,
// and then cut away all holes in R one by one.
+ //
+ // E.g. consider a range list R as [A, B] and [C, D]
+ // -------+--------+------------------+------------+----------->
+ // A B C D
+ // Then we assume that the value is not in [-inf, A - 1],
+ // then not in [D + 1, +inf], then not in [B + 1, C - 1]
if (auto N = V.getAs<NonLoc>()) {
- const IntRangeVectorTy &R = getRanges();
+ const IntRangeVector &R = getRanges();
size_t E = R.size();
const llvm::APSInt &MinusInf = BVF.getMinValue(T);
@@ -303,31 +565,31 @@ StdLibraryFunctionsChecker::ValueRange::applyAsWithinRange(
for (size_t I = 1; I != E; ++I) {
const llvm::APSInt &Min = BVF.getValue(R[I - 1].second + 1ULL, T);
const llvm::APSInt &Max = BVF.getValue(R[I].first - 1ULL, T);
- assert(Min <= Max);
- State = CM.assumeInclusiveRange(State, *N, Min, Max, false);
- if (!State)
- return nullptr;
+ if (Min <= Max) {
+ State = CM.assumeInclusiveRange(State, *N, Min, Max, false);
+ if (!State)
+ return nullptr;
+ }
}
}
return State;
}
-ProgramStateRef
-StdLibraryFunctionsChecker::ValueRange::applyAsComparesToArgument(
- ProgramStateRef State, const CallEvent &Call,
- const FunctionSummaryTy &Summary) const {
+ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply(
+ ProgramStateRef State, const CallEvent &Call, const Summary &Summary,
+ CheckerContext &C) const {
ProgramStateManager &Mgr = State->getStateManager();
SValBuilder &SVB = Mgr.getSValBuilder();
QualType CondT = SVB.getConditionType();
- QualType T = getArgType(Summary, getArgNo());
+ QualType T = Summary.getArgType(getArgNo());
SVal V = getArgSVal(Call, getArgNo());
BinaryOperator::Opcode Op = getOpcode();
- ArgNoTy OtherArg = getOtherArgNo();
+ ArgNo OtherArg = getOtherArgNo();
SVal OtherV = getArgSVal(Call, OtherArg);
- QualType OtherT = getArgType(Call, OtherArg);
+ QualType OtherT = Summary.getArgType(OtherArg);
// Note: we avoid integral promotion for comparison.
OtherV = SVB.evalCast(OtherV, T, OtherT);
if (auto CompV = SVB.evalBinOp(State, Op, V, OtherV, CondT)
@@ -336,28 +598,53 @@ StdLibraryFunctionsChecker::ValueRange::applyAsComparesToArgument(
return State;
}
-void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
- CheckerContext &C) const {
- const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
- if (!FD)
+void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ Optional<Summary> FoundSummary = findFunctionSummary(Call, C);
+ if (!FoundSummary)
return;
- const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
- if (!CE)
- return;
+ const Summary &Summary = *FoundSummary;
+ ProgramStateRef State = C.getState();
+
+ ProgramStateRef NewState = State;
+ for (const ValueConstraintPtr &Constraint : Summary.getArgConstraints()) {
+ ProgramStateRef SuccessSt = Constraint->apply(NewState, Call, Summary, C);
+ ProgramStateRef FailureSt =
+ Constraint->negate()->apply(NewState, Call, Summary, C);
+ // The argument constraint is not satisfied.
+ if (FailureSt && !SuccessSt) {
+ if (ExplodedNode *N = C.generateErrorNode(NewState))
+ reportBug(Call, N, C);
+ break;
+ } else {
+ // We will apply the constraint even if we cannot reason about the
+ // argument. This means both SuccessSt and FailureSt can be true. If we
+ // weren't applying the constraint that would mean that symbolic
+ // execution continues on a code whose behaviour is undefined.
+ assert(SuccessSt);
+ NewState = SuccessSt;
+ }
+ }
+ if (NewState && NewState != State)
+ C.addTransition(NewState);
+}
- Optional<FunctionSummaryTy> FoundSummary = findFunctionSummary(FD, CE, C);
+void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ Optional<Summary> FoundSummary = findFunctionSummary(Call, C);
if (!FoundSummary)
return;
- // Now apply ranges.
- const FunctionSummaryTy &Summary = *FoundSummary;
+ // Now apply the constraints.
+ const Summary &Summary = *FoundSummary;
ProgramStateRef State = C.getState();
- for (const auto &VRS: Summary.Ranges) {
+ // Apply case/branch specifications.
+ for (const ConstraintSet &Case : Summary.getCaseConstraints()) {
ProgramStateRef NewState = State;
- for (const auto &VR: VRS) {
- NewState = VR.apply(NewState, Call, Summary);
+ for (const ValueConstraintPtr &Constraint : Case) {
+ NewState = Constraint->apply(NewState, Call, Summary, C);
if (!NewState)
break;
}
@@ -369,23 +656,16 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call,
CheckerContext &C) const {
- 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);
+ Optional<Summary> FoundSummary = findFunctionSummary(Call, C);
if (!FoundSummary)
return false;
- const FunctionSummaryTy &Summary = *FoundSummary;
- switch (Summary.InvalidationKind) {
+ const Summary &Summary = *FoundSummary;
+ switch (Summary.getInvalidationKd()) {
case EvalCallAsPure: {
ProgramStateRef State = C.getState();
const LocationContext *LC = C.getLocationContext();
+ const auto *CE = cast_or_null<CallExpr>(Call.getOriginExpr());
SVal V = C.getSValBuilder().conjureSymbolVal(
CE, LC, CE->getType().getCanonicalType(), C.blockCount());
State = State->BindExpr(CE, LC, V);
@@ -400,79 +680,86 @@ bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call,
llvm_unreachable("Unknown invalidation kind!");
}
-bool StdLibraryFunctionsChecker::FunctionSummaryTy::matchesCall(
- const CallExpr *CE) const {
+bool StdLibraryFunctionsChecker::Signature::matches(
+ const FunctionDecl *FD) const {
// Check number of arguments:
- if (CE->getNumArgs() != ArgTypes.size())
+ if (FD->param_size() != ArgTys.size())
return false;
- // Check return type if relevant:
- if (!RetType.isNull() && RetType != CE->getType().getCanonicalType())
- return false;
+ // Check return type.
+ if (!isIrrelevant(RetTy))
+ if (RetTy != FD->getReturnType().getCanonicalType())
+ return false;
- // Check argument types when relevant:
- for (size_t I = 0, E = ArgTypes.size(); I != E; ++I) {
- QualType FormalT = ArgTypes[I];
- // Null type marks irrelevant arguments.
- if (FormalT.isNull())
+ // Check argument types.
+ for (size_t I = 0, E = ArgTys.size(); I != E; ++I) {
+ QualType ArgTy = ArgTys[I];
+ if (isIrrelevant(ArgTy))
continue;
-
- assertTypeSuitableForSummary(FormalT);
-
- QualType ActualT = StdLibraryFunctionsChecker::getArgType(CE, I);
- assert(ActualT.isCanonical());
- if (ActualT != FormalT)
+ if (ArgTy != FD->getParamDecl(I)->getType().getCanonicalType())
return false;
}
return true;
}
-Optional<StdLibraryFunctionsChecker::FunctionSummaryTy>
+Optional<StdLibraryFunctionsChecker::Summary>
StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD,
- const CallExpr *CE,
CheckerContext &C) const {
- // Note: we cannot always obtain FD from CE
- // (eg. virtual call, or call by pointer).
- assert(CE);
-
if (!FD)
return None;
- SValBuilder &SVB = C.getSValBuilder();
- BasicValueFactory &BVF = SVB.getBasicValueFactory();
- initFunctionSummaries(BVF);
+ initFunctionSummaries(C);
- IdentifierInfo *II = FD->getIdentifier();
- if (!II)
- return None;
- StringRef Name = II->getName();
- if (Name.empty() || !C.isCLibraryFunction(FD, Name))
+ auto FSMI = FunctionSummaryMap.find(FD->getCanonicalDecl());
+ if (FSMI == FunctionSummaryMap.end())
return None;
+ return FSMI->second;
+}
- auto FSMI = FunctionSummaryMap.find(Name);
- if (FSMI == FunctionSummaryMap.end())
+Optional<StdLibraryFunctionsChecker::Summary>
+StdLibraryFunctionsChecker::findFunctionSummary(const CallEvent &Call,
+ CheckerContext &C) const {
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!FD)
return None;
+ return findFunctionSummary(FD, C);
+}
- // Verify that function signature matches the spec in advance.
- // Otherwise we might be modeling the wrong function.
- // Strict checking is important because we will be conducting
- // very integral-type-sensitive operations on arguments and
- // return values.
- const FunctionVariantsTy &SpecVariants = FSMI->second;
- for (const FunctionSummaryTy &Spec : SpecVariants)
- if (Spec.matchesCall(CE))
- return Spec;
+static llvm::Optional<QualType> lookupType(StringRef Name,
+ const ASTContext &ACtx) {
+ IdentifierInfo &II = ACtx.Idents.get(Name);
+ auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
+ if (LookupRes.size() == 0)
+ return None;
+ // Prioritze typedef declarations.
+ // This is needed in case of C struct typedefs. E.g.:
+ // typedef struct FILE FILE;
+ // In this case, we have a RecordDecl 'struct FILE' with the name 'FILE' and
+ // we have a TypedefDecl with the name 'FILE'.
+ for (Decl *D : LookupRes)
+ if (auto *TD = dyn_cast<TypedefNameDecl>(D))
+ return ACtx.getTypeDeclType(TD).getCanonicalType();
+
+ // Find the first TypeDecl.
+ // There maybe cases when a function has the same name as a struct.
+ // E.g. in POSIX: `struct stat` and the function `stat()`:
+ // int stat(const char *restrict path, struct stat *restrict buf);
+ for (Decl *D : LookupRes)
+ if (auto *TD = dyn_cast<TypeDecl>(D))
+ return ACtx.getTypeDeclType(TD).getCanonicalType();
return None;
}
void StdLibraryFunctionsChecker::initFunctionSummaries(
- BasicValueFactory &BVF) const {
+ CheckerContext &C) const {
if (!FunctionSummaryMap.empty())
return;
- ASTContext &ACtx = BVF.getContext();
+ SValBuilder &SVB = C.getSValBuilder();
+ BasicValueFactory &BVF = SVB.getBasicValueFactory();
+ const ASTContext &ACtx = BVF.getContext();
// These types are useful for writing specifications quickly,
// New specifications should probably introduce more types.
@@ -481,15 +768,105 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
// of function summary for common cases (eg. ssize_t could be int or long
// or long long, so three summary variants would be enough).
// Of course, function variants are also useful for C++ overloads.
- QualType Irrelevant; // A placeholder, whenever we do not care about the type.
- QualType IntTy = ACtx.IntTy;
- QualType LongTy = ACtx.LongTy;
- QualType LongLongTy = ACtx.LongLongTy;
- QualType SizeTy = ACtx.getSizeType();
+ const QualType VoidTy = ACtx.VoidTy;
+ const QualType IntTy = ACtx.IntTy;
+ const QualType UnsignedIntTy = ACtx.UnsignedIntTy;
+ const QualType LongTy = ACtx.LongTy;
+ const QualType LongLongTy = ACtx.LongLongTy;
+ const QualType SizeTy = ACtx.getSizeType();
+
+ const QualType VoidPtrTy = ACtx.VoidPtrTy; // void *
+ const QualType IntPtrTy = ACtx.getPointerType(IntTy); // int *
+ const QualType UnsignedIntPtrTy =
+ ACtx.getPointerType(UnsignedIntTy); // unsigned int *
+ const QualType VoidPtrRestrictTy =
+ ACtx.getLangOpts().C99 ? ACtx.getRestrictType(VoidPtrTy) // void *restrict
+ : VoidPtrTy;
+ const QualType ConstVoidPtrTy =
+ ACtx.getPointerType(ACtx.VoidTy.withConst()); // const void *
+ const QualType CharPtrTy = ACtx.getPointerType(ACtx.CharTy); // char *
+ const QualType CharPtrRestrictTy =
+ ACtx.getLangOpts().C99 ? ACtx.getRestrictType(CharPtrTy) // char *restrict
+ : CharPtrTy;
+ const QualType ConstCharPtrTy =
+ ACtx.getPointerType(ACtx.CharTy.withConst()); // const char *
+ const QualType ConstCharPtrRestrictTy =
+ ACtx.getLangOpts().C99
+ ? ACtx.getRestrictType(ConstCharPtrTy) // const char *restrict
+ : ConstCharPtrTy;
+ const QualType Wchar_tPtrTy = ACtx.getPointerType(ACtx.WCharTy); // wchar_t *
+ const QualType ConstWchar_tPtrTy =
+ ACtx.getPointerType(ACtx.WCharTy.withConst()); // const wchar_t *
+ const QualType ConstVoidPtrRestrictTy =
+ ACtx.getLangOpts().C99
+ ? ACtx.getRestrictType(ConstVoidPtrTy) // const void *restrict
+ : ConstVoidPtrTy;
+
+ const RangeInt IntMax = BVF.getMaxValue(IntTy).getLimitedValue();
+ const RangeInt UnsignedIntMax =
+ BVF.getMaxValue(UnsignedIntTy).getLimitedValue();
+ const RangeInt LongMax = BVF.getMaxValue(LongTy).getLimitedValue();
+ const RangeInt LongLongMax = BVF.getMaxValue(LongLongTy).getLimitedValue();
+ const RangeInt SizeMax = BVF.getMaxValue(SizeTy).getLimitedValue();
+
+ // Set UCharRangeMax to min of int or uchar maximum value.
+ // The C standard states that the arguments of functions like isalpha must
+ // be representable as an unsigned char. Their type is 'int', so the max
+ // value of the argument should be min(UCharMax, IntMax). This just happen
+ // to be true for commonly used and well tested instruction set
+ // architectures, but not for others.
+ const RangeInt UCharRangeMax =
+ std::min(BVF.getMaxValue(ACtx.UnsignedCharTy).getLimitedValue(), IntMax);
+
+ // The platform dependent value of EOF.
+ // Try our best to parse this from the Preprocessor, otherwise fallback to -1.
+ const auto EOFv = [&C]() -> RangeInt {
+ if (const llvm::Optional<int> OptInt =
+ tryExpandAsInteger("EOF", C.getPreprocessor()))
+ return *OptInt;
+ return -1;
+ }();
+
+ // Auxiliary class to aid adding summaries to the summary map.
+ struct AddToFunctionSummaryMap {
+ const ASTContext &ACtx;
+ FunctionSummaryMapType &Map;
+ bool DisplayLoadedSummaries;
+ AddToFunctionSummaryMap(const ASTContext &ACtx, FunctionSummaryMapType &FSM,
+ bool DisplayLoadedSummaries)
+ : ACtx(ACtx), Map(FSM), DisplayLoadedSummaries(DisplayLoadedSummaries) {
+ }
- RangeIntTy IntMax = BVF.getMaxValue(IntTy).getLimitedValue();
- RangeIntTy LongMax = BVF.getMaxValue(LongTy).getLimitedValue();
- RangeIntTy LongLongMax = BVF.getMaxValue(LongLongTy).getLimitedValue();
+ // Add a summary to a FunctionDecl found by lookup. The lookup is performed
+ // by the given Name, and in the global scope. The summary will be attached
+ // to the found FunctionDecl only if the signatures match.
+ void operator()(StringRef Name, Summary S) {
+ IdentifierInfo &II = ACtx.Idents.get(Name);
+ auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
+ if (LookupRes.size() == 0)
+ return;
+ for (Decl *D : LookupRes) {
+ if (auto *FD = dyn_cast<FunctionDecl>(D)) {
+ if (S.matchesAndSet(FD)) {
+ auto Res = Map.insert({FD->getCanonicalDecl(), S});
+ assert(Res.second && "Function already has a summary set!");
+ (void)Res;
+ if (DisplayLoadedSummaries) {
+ llvm::errs() << "Loaded summary for: ";
+ FD->print(llvm::errs());
+ llvm::errs() << "\n";
+ }
+ return;
+ }
+ }
+ }
+ }
+ // Add several summaries for the given name.
+ void operator()(StringRef Name, const std::vector<Summary> &Summaries) {
+ for (const Summary &S : Summaries)
+ operator()(Name, S);
+ }
+ } addToFunctionSummaryMap(ACtx, FunctionSummaryMap, DisplayLoadedSummaries);
// We are finally ready to define specifications for all supported functions.
//
@@ -516,550 +893,876 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
// return value, however the correct range is [-1, 10].
//
// Please update the list of functions in the header after editing!
- //
- // The format is as follows:
- //
- //{ "function name",
- // { spec:
- // { argument types list, ... },
- // return type, purity, { range set list:
- // { range list:
- // { argument index, within or out of, {{from, to}, ...} },
- // { argument index, compares to argument, {{how, which}} },
- // ...
- // }
- // }
- // }
- //}
-
-#define SUMMARY_WITH_VARIANTS(identifier) {#identifier, {
-#define END_SUMMARY_WITH_VARIANTS }},
-#define VARIANT(argument_types, return_type, invalidation_approach) \
- { argument_types, return_type, invalidation_approach, {
-#define END_VARIANT } },
-#define SUMMARY(identifier, argument_types, return_type, \
- invalidation_approach) \
- { #identifier, { { argument_types, return_type, invalidation_approach, {
-#define END_SUMMARY } } } },
-#define ARGUMENT_TYPES(...) { __VA_ARGS__ }
-#define RETURN_TYPE(x) x
-#define INVALIDATION_APPROACH(x) x
-#define CASE {
-#define END_CASE },
-#define ARGUMENT_CONDITION(argument_number, condition_kind) \
- { argument_number, condition_kind, {
-#define END_ARGUMENT_CONDITION }},
-#define RETURN_VALUE_CONDITION(condition_kind) \
- { Ret, condition_kind, {
-#define END_RETURN_VALUE_CONDITION }},
-#define ARG_NO(x) x##U
-#define RANGE(x, y) { x, y },
-#define SINGLE_VALUE(x) RANGE(x, x)
-#define IS_LESS_THAN(arg) { BO_LE, arg }
-
- FunctionSummaryMap = {
- // The isascii() family of functions.
- SUMMARY(isalnum, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE // Boils down to isupper() or islower() or isdigit()
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE('0', '9')
- RANGE('A', 'Z')
- RANGE('a', 'z')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE // The locale-specific range.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(128, 255)
- END_ARGUMENT_CONDITION
- // No post-condition. We are completely unaware of
- // locale-specific return values.
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE('0', '9')
- RANGE('A', 'Z')
- RANGE('a', 'z')
- RANGE(128, 255)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isalpha, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE // isupper() or islower(). Note that 'Z' is less than 'a'.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE('A', 'Z')
- RANGE('a', 'z')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE // The locale-specific range.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(128, 255)
- END_ARGUMENT_CONDITION
- END_CASE
- CASE // Other.
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE('A', 'Z')
- RANGE('a', 'z')
- RANGE(128, 255)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isascii, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE // Is ASCII.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(0, 127)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE(0, 127)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isblank, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- SINGLE_VALUE('\t')
- SINGLE_VALUE(' ')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- SINGLE_VALUE('\t')
- SINGLE_VALUE(' ')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(iscntrl, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE // 0..31 or 127
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(0, 32)
- SINGLE_VALUE(127)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE(0, 32)
- SINGLE_VALUE(127)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE // Is a digit.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE('0', '9')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE('0', '9')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isgraph, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(33, 126)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE(33, 126)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(islower, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE // Is certainly lowercase.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE('a', 'z')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE // Is ascii but not lowercase.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(0, 127)
- END_ARGUMENT_CONDITION
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE('a', 'z')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE // The locale-specific range.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(128, 255)
- END_ARGUMENT_CONDITION
- END_CASE
- CASE // Is not an unsigned char.
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE(0, 255)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isprint, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(32, 126)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE(32, 126)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(ispunct, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE('!', '/')
- RANGE(':', '@')
- RANGE('[', '`')
- RANGE('{', '~')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE('!', '/')
- RANGE(':', '@')
- RANGE('[', '`')
- RANGE('{', '~')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isspace, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE // Space, '\f', '\n', '\r', '\t', '\v'.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(9, 13)
- SINGLE_VALUE(' ')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE // The locale-specific range.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(128, 255)
- END_ARGUMENT_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE(9, 13)
- SINGLE_VALUE(' ')
- RANGE(128, 255)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isupper, ARGUMENT_TYPES(IntTy), RETURN_TYPE (IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE // Is certainly uppercase.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE('A', 'Z')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE // The locale-specific range.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(128, 255)
- END_ARGUMENT_CONDITION
- END_CASE
- CASE // Other.
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE('A', 'Z') RANGE(128, 255)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isxdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE('0', '9')
- RANGE('A', 'F')
- RANGE('a', 'f')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE('0', '9')
- RANGE('A', 'F')
- RANGE('a', 'f')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
-
- // The getc() family of functions that returns either a char or an EOF.
- SUMMARY(getc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(NoEvalCall))
- CASE // FIXME: EOF is assumed to be defined as -1.
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, 255)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(fgetc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(NoEvalCall))
- CASE // FIXME: EOF is assumed to be defined as -1.
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, 255)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(getchar, ARGUMENT_TYPES(), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(NoEvalCall))
- CASE // FIXME: EOF is assumed to be defined as -1.
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, 255)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
-
- // read()-like functions that never return more than buffer size.
- // We are not sure how ssize_t is defined on every platform, so we provide
- // three variants that should cover common cases.
- SUMMARY_WITH_VARIANTS(read)
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
- RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(ComparesToArgument)
- IS_LESS_THAN(ARG_NO(2))
- END_RETURN_VALUE_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, IntMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
- RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(ComparesToArgument)
- IS_LESS_THAN(ARG_NO(2))
- END_RETURN_VALUE_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, LongMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
- RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(ComparesToArgument)
- IS_LESS_THAN(ARG_NO(2))
- END_RETURN_VALUE_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, LongLongMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- END_SUMMARY_WITH_VARIANTS
- SUMMARY_WITH_VARIANTS(write)
- // Again, due to elusive nature of ssize_t, we have duplicate
- // our summaries to cover different variants.
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
- RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(ComparesToArgument)
- IS_LESS_THAN(ARG_NO(2))
- END_RETURN_VALUE_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, IntMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
- RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(ComparesToArgument)
- IS_LESS_THAN(ARG_NO(2))
- END_RETURN_VALUE_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, LongMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
- RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(ComparesToArgument)
- IS_LESS_THAN(ARG_NO(2))
- END_RETURN_VALUE_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, LongLongMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- END_SUMMARY_WITH_VARIANTS
- SUMMARY(fread,
- ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant),
- RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(ComparesToArgument)
- IS_LESS_THAN(ARG_NO(2))
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(fwrite,
- ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant),
- RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(ComparesToArgument)
- IS_LESS_THAN(ARG_NO(2))
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
-
- // getline()-like functions either fail or read at least the delimiter.
- SUMMARY_WITH_VARIANTS(getline)
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant),
- RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(-1)
- RANGE(1, IntMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant),
- RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(-1)
- RANGE(1, LongMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant),
- RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(-1)
- RANGE(1, LongLongMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- END_SUMMARY_WITH_VARIANTS
- SUMMARY_WITH_VARIANTS(getdelim)
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant),
- RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(-1)
- RANGE(1, IntMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant),
- RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(-1)
- RANGE(1, LongMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant),
- RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(-1)
- RANGE(1, LongLongMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- END_SUMMARY_WITH_VARIANTS
+
+ // Below are helpers functions to create the summaries.
+ auto ArgumentCondition = [](ArgNo ArgN, RangeKind Kind,
+ IntRangeVector Ranges) {
+ return std::make_shared<RangeConstraint>(ArgN, Kind, Ranges);
+ };
+ auto BufferSize = [](auto... Args) {
+ return std::make_shared<BufferSizeConstraint>(Args...);
+ };
+ struct {
+ auto operator()(RangeKind Kind, IntRangeVector Ranges) {
+ return std::make_shared<RangeConstraint>(Ret, Kind, Ranges);
+ }
+ auto operator()(BinaryOperator::Opcode Op, ArgNo OtherArgN) {
+ return std::make_shared<ComparisonConstraint>(Ret, Op, OtherArgN);
+ }
+ } ReturnValueCondition;
+ auto Range = [](RangeInt b, RangeInt e) {
+ return IntRangeVector{std::pair<RangeInt, RangeInt>{b, e}};
+ };
+ auto SingleValue = [](RangeInt v) {
+ return IntRangeVector{std::pair<RangeInt, RangeInt>{v, v}};
};
+ auto LessThanOrEq = BO_LE;
+ auto NotNull = [&](ArgNo ArgN) {
+ return std::make_shared<NotNullConstraint>(ArgN);
+ };
+
+ Optional<QualType> FileTy = lookupType("FILE", ACtx);
+ Optional<QualType> FilePtrTy, FilePtrRestrictTy;
+ if (FileTy) {
+ // FILE *
+ FilePtrTy = ACtx.getPointerType(*FileTy);
+ // FILE *restrict
+ FilePtrRestrictTy =
+ ACtx.getLangOpts().C99 ? ACtx.getRestrictType(*FilePtrTy) : *FilePtrTy;
+ }
+
+ using RetType = QualType;
+ // Templates for summaries that are reused by many functions.
+ auto Getc = [&]() {
+ return Summary(ArgTypes{*FilePtrTy}, RetType{IntTy}, NoEvalCall)
+ .Case({ReturnValueCondition(WithinRange,
+ {{EOFv, EOFv}, {0, UCharRangeMax}})});
+ };
+ auto Read = [&](RetType R, RangeInt Max) {
+ return Summary(ArgTypes{Irrelevant, Irrelevant, SizeTy}, RetType{R},
+ NoEvalCall)
+ .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
+ ReturnValueCondition(WithinRange, Range(-1, Max))});
+ };
+ auto Fread = [&]() {
+ return Summary(
+ ArgTypes{VoidPtrRestrictTy, SizeTy, SizeTy, *FilePtrRestrictTy},
+ RetType{SizeTy}, NoEvalCall)
+ .Case({
+ ReturnValueCondition(LessThanOrEq, ArgNo(2)),
+ })
+ .ArgConstraint(NotNull(ArgNo(0)));
+ };
+ auto Fwrite = [&]() {
+ return Summary(ArgTypes{ConstVoidPtrRestrictTy, SizeTy, SizeTy,
+ *FilePtrRestrictTy},
+ RetType{SizeTy}, NoEvalCall)
+ .Case({
+ ReturnValueCondition(LessThanOrEq, ArgNo(2)),
+ })
+ .ArgConstraint(NotNull(ArgNo(0)));
+ };
+ auto Getline = [&](RetType R, RangeInt Max) {
+ return Summary(ArgTypes{Irrelevant, Irrelevant, Irrelevant}, RetType{R},
+ NoEvalCall)
+ .Case({ReturnValueCondition(WithinRange, {{-1, -1}, {1, Max}})});
+ };
+
+ // The isascii() family of functions.
+ // The behavior is undefined if the value of the argument is not
+ // representable as unsigned char or is not equal to EOF. See e.g. C99
+ // 7.4.1.2 The isalpha function (p: 181-182).
+ addToFunctionSummaryMap(
+ "isalnum",
+ Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ // Boils down to isupper() or islower() or isdigit().
+ .Case({ArgumentCondition(0U, WithinRange,
+ {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}}),
+ ReturnValueCondition(OutOfRange, SingleValue(0))})
+ // The locale-specific range.
+ // No post-condition. We are completely unaware of
+ // locale-specific return values.
+ .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})})
+ .Case(
+ {ArgumentCondition(
+ 0U, OutOfRange,
+ {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}),
+ ReturnValueCondition(WithinRange, SingleValue(0))})
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})));
+ addToFunctionSummaryMap(
+ "isalpha",
+ Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ .Case({ArgumentCondition(0U, WithinRange, {{'A', 'Z'}, {'a', 'z'}}),
+ ReturnValueCondition(OutOfRange, SingleValue(0))})
+ // The locale-specific range.
+ .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})})
+ .Case({ArgumentCondition(
+ 0U, OutOfRange,
+ {{'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}),
+ ReturnValueCondition(WithinRange, SingleValue(0))}));
+ addToFunctionSummaryMap(
+ "isascii",
+ Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ .Case({ArgumentCondition(0U, WithinRange, Range(0, 127)),
+ ReturnValueCondition(OutOfRange, SingleValue(0))})
+ .Case({ArgumentCondition(0U, OutOfRange, Range(0, 127)),
+ ReturnValueCondition(WithinRange, SingleValue(0))}));
+ addToFunctionSummaryMap(
+ "isblank",
+ Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ .Case({ArgumentCondition(0U, WithinRange, {{'\t', '\t'}, {' ', ' '}}),
+ ReturnValueCondition(OutOfRange, SingleValue(0))})
+ .Case({ArgumentCondition(0U, OutOfRange, {{'\t', '\t'}, {' ', ' '}}),
+ ReturnValueCondition(WithinRange, SingleValue(0))}));
+ addToFunctionSummaryMap(
+ "iscntrl",
+ Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ .Case({ArgumentCondition(0U, WithinRange, {{0, 32}, {127, 127}}),
+ ReturnValueCondition(OutOfRange, SingleValue(0))})
+ .Case({ArgumentCondition(0U, OutOfRange, {{0, 32}, {127, 127}}),
+ ReturnValueCondition(WithinRange, SingleValue(0))}));
+ addToFunctionSummaryMap(
+ "isdigit",
+ Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ .Case({ArgumentCondition(0U, WithinRange, Range('0', '9')),
+ ReturnValueCondition(OutOfRange, SingleValue(0))})
+ .Case({ArgumentCondition(0U, OutOfRange, Range('0', '9')),
+ ReturnValueCondition(WithinRange, SingleValue(0))}));
+ addToFunctionSummaryMap(
+ "isgraph",
+ Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ .Case({ArgumentCondition(0U, WithinRange, Range(33, 126)),
+ ReturnValueCondition(OutOfRange, SingleValue(0))})
+ .Case({ArgumentCondition(0U, OutOfRange, Range(33, 126)),
+ ReturnValueCondition(WithinRange, SingleValue(0))}));
+ addToFunctionSummaryMap(
+ "islower",
+ Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ // Is certainly lowercase.
+ .Case({ArgumentCondition(0U, WithinRange, Range('a', 'z')),
+ ReturnValueCondition(OutOfRange, SingleValue(0))})
+ // Is ascii but not lowercase.
+ .Case({ArgumentCondition(0U, WithinRange, Range(0, 127)),
+ ArgumentCondition(0U, OutOfRange, Range('a', 'z')),
+ ReturnValueCondition(WithinRange, SingleValue(0))})
+ // The locale-specific range.
+ .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})})
+ // Is not an unsigned char.
+ .Case({ArgumentCondition(0U, OutOfRange, Range(0, UCharRangeMax)),
+ ReturnValueCondition(WithinRange, SingleValue(0))}));
+ addToFunctionSummaryMap(
+ "isprint",
+ Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ .Case({ArgumentCondition(0U, WithinRange, Range(32, 126)),
+ ReturnValueCondition(OutOfRange, SingleValue(0))})
+ .Case({ArgumentCondition(0U, OutOfRange, Range(32, 126)),
+ ReturnValueCondition(WithinRange, SingleValue(0))}));
+ addToFunctionSummaryMap(
+ "ispunct",
+ Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ .Case({ArgumentCondition(
+ 0U, WithinRange,
+ {{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}),
+ ReturnValueCondition(OutOfRange, SingleValue(0))})
+ .Case({ArgumentCondition(
+ 0U, OutOfRange,
+ {{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}),
+ ReturnValueCondition(WithinRange, SingleValue(0))}));
+ addToFunctionSummaryMap(
+ "isspace",
+ Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ // Space, '\f', '\n', '\r', '\t', '\v'.
+ .Case({ArgumentCondition(0U, WithinRange, {{9, 13}, {' ', ' '}}),
+ ReturnValueCondition(OutOfRange, SingleValue(0))})
+ // The locale-specific range.
+ .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})})
+ .Case({ArgumentCondition(0U, OutOfRange,
+ {{9, 13}, {' ', ' '}, {128, UCharRangeMax}}),
+ ReturnValueCondition(WithinRange, SingleValue(0))}));
+ addToFunctionSummaryMap(
+ "isupper",
+ Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ // Is certainly uppercase.
+ .Case({ArgumentCondition(0U, WithinRange, Range('A', 'Z')),
+ ReturnValueCondition(OutOfRange, SingleValue(0))})
+ // The locale-specific range.
+ .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})})
+ // Other.
+ .Case({ArgumentCondition(0U, OutOfRange,
+ {{'A', 'Z'}, {128, UCharRangeMax}}),
+ ReturnValueCondition(WithinRange, SingleValue(0))}));
+ addToFunctionSummaryMap(
+ "isxdigit",
+ Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ .Case({ArgumentCondition(0U, WithinRange,
+ {{'0', '9'}, {'A', 'F'}, {'a', 'f'}}),
+ ReturnValueCondition(OutOfRange, SingleValue(0))})
+ .Case({ArgumentCondition(0U, OutOfRange,
+ {{'0', '9'}, {'A', 'F'}, {'a', 'f'}}),
+ ReturnValueCondition(WithinRange, SingleValue(0))}));
+
+ // The getc() family of functions that returns either a char or an EOF.
+ if (FilePtrTy) {
+ addToFunctionSummaryMap("getc", Getc());
+ addToFunctionSummaryMap("fgetc", Getc());
+ }
+ addToFunctionSummaryMap(
+ "getchar", Summary(ArgTypes{}, RetType{IntTy}, NoEvalCall)
+ .Case({ReturnValueCondition(
+ WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})}));
+
+ // read()-like functions that never return more than buffer size.
+ if (FilePtrRestrictTy) {
+ addToFunctionSummaryMap("fread", Fread());
+ addToFunctionSummaryMap("fwrite", Fwrite());
+ }
+
+ // We are not sure how ssize_t is defined on every platform, so we
+ // provide three variants that should cover common cases.
+ // FIXME these are actually defined by POSIX and not by the C standard, we
+ // should handle them together with the rest of the POSIX functions.
+ addToFunctionSummaryMap("read", {Read(IntTy, IntMax), Read(LongTy, LongMax),
+ Read(LongLongTy, LongLongMax)});
+ addToFunctionSummaryMap("write", {Read(IntTy, IntMax), Read(LongTy, LongMax),
+ Read(LongLongTy, LongLongMax)});
+
+ // getline()-like functions either fail or read at least the delimiter.
+ // FIXME these are actually defined by POSIX and not by the C standard, we
+ // should handle them together with the rest of the POSIX functions.
+ addToFunctionSummaryMap("getline",
+ {Getline(IntTy, IntMax), Getline(LongTy, LongMax),
+ Getline(LongLongTy, LongLongMax)});
+ addToFunctionSummaryMap("getdelim",
+ {Getline(IntTy, IntMax), Getline(LongTy, LongMax),
+ Getline(LongLongTy, LongLongMax)});
+
+ if (ModelPOSIX) {
+
+ // long a64l(const char *str64);
+ addToFunctionSummaryMap(
+ "a64l", Summary(ArgTypes{ConstCharPtrTy}, RetType{LongTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // char *l64a(long value);
+ addToFunctionSummaryMap(
+ "l64a", Summary(ArgTypes{LongTy}, RetType{CharPtrTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, LongMax))));
+
+ // int access(const char *pathname, int amode);
+ addToFunctionSummaryMap("access", Summary(ArgTypes{ConstCharPtrTy, IntTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int faccessat(int dirfd, const char *pathname, int mode, int flags);
+ addToFunctionSummaryMap(
+ "faccessat", Summary(ArgTypes{IntTy, ConstCharPtrTy, IntTy, IntTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int dup(int fildes);
+ addToFunctionSummaryMap(
+ "dup", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
+ // int dup2(int fildes1, int filedes2);
+ addToFunctionSummaryMap(
+ "dup2",
+ Summary(ArgTypes{IntTy, IntTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(
+ ArgumentCondition(1, WithinRange, Range(0, IntMax))));
+
+ // int fdatasync(int fildes);
+ addToFunctionSummaryMap(
+ "fdatasync", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange,
+ Range(0, IntMax))));
+
+ // int fnmatch(const char *pattern, const char *string, int flags);
+ addToFunctionSummaryMap(
+ "fnmatch", Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, IntTy},
+ RetType{IntTy}, EvalCallAsPure)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int fsync(int fildes);
+ addToFunctionSummaryMap(
+ "fsync", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
+ Optional<QualType> Off_tTy = lookupType("off_t", ACtx);
+
+ if (Off_tTy)
+ // int truncate(const char *path, off_t length);
+ addToFunctionSummaryMap("truncate",
+ Summary(ArgTypes{ConstCharPtrTy, *Off_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int symlink(const char *oldpath, const char *newpath);
+ addToFunctionSummaryMap("symlink",
+ Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int symlinkat(const char *oldpath, int newdirfd, const char *newpath);
+ addToFunctionSummaryMap(
+ "symlinkat",
+ Summary(ArgTypes{ConstCharPtrTy, IntTy, ConstCharPtrTy}, RetType{IntTy},
+ NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(ArgumentCondition(1, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(2))));
+
+ if (Off_tTy)
+ // int lockf(int fd, int cmd, off_t len);
+ addToFunctionSummaryMap(
+ "lockf",
+ Summary(ArgTypes{IntTy, IntTy, *Off_tTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
+ Optional<QualType> Mode_tTy = lookupType("mode_t", ACtx);
+
+ if (Mode_tTy)
+ // int creat(const char *pathname, mode_t mode);
+ addToFunctionSummaryMap("creat",
+ Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // unsigned int sleep(unsigned int seconds);
+ addToFunctionSummaryMap(
+ "sleep",
+ Summary(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax))));
+
+ Optional<QualType> DirTy = lookupType("DIR", ACtx);
+ Optional<QualType> DirPtrTy;
+ if (DirTy)
+ DirPtrTy = ACtx.getPointerType(*DirTy);
+
+ if (DirPtrTy)
+ // int dirfd(DIR *dirp);
+ addToFunctionSummaryMap(
+ "dirfd", Summary(ArgTypes{*DirPtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // unsigned int alarm(unsigned int seconds);
+ addToFunctionSummaryMap(
+ "alarm",
+ Summary(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax))));
+
+ if (DirPtrTy)
+ // int closedir(DIR *dir);
+ addToFunctionSummaryMap(
+ "closedir", Summary(ArgTypes{*DirPtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // char *strdup(const char *s);
+ addToFunctionSummaryMap("strdup", Summary(ArgTypes{ConstCharPtrTy},
+ RetType{CharPtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // char *strndup(const char *s, size_t n);
+ addToFunctionSummaryMap(
+ "strndup", Summary(ArgTypes{ConstCharPtrTy, SizeTy}, RetType{CharPtrTy},
+ NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(ArgumentCondition(1, WithinRange,
+ Range(0, SizeMax))));
+
+ // wchar_t *wcsdup(const wchar_t *s);
+ addToFunctionSummaryMap("wcsdup", Summary(ArgTypes{ConstWchar_tPtrTy},
+ RetType{Wchar_tPtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int mkstemp(char *template);
+ addToFunctionSummaryMap(
+ "mkstemp", Summary(ArgTypes{CharPtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // char *mkdtemp(char *template);
+ addToFunctionSummaryMap(
+ "mkdtemp", Summary(ArgTypes{CharPtrTy}, RetType{CharPtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // char *getcwd(char *buf, size_t size);
+ addToFunctionSummaryMap(
+ "getcwd",
+ Summary(ArgTypes{CharPtrTy, SizeTy}, RetType{CharPtrTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(1, WithinRange, Range(0, SizeMax))));
+
+ if (Mode_tTy) {
+ // int mkdir(const char *pathname, mode_t mode);
+ addToFunctionSummaryMap("mkdir",
+ Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int mkdirat(int dirfd, const char *pathname, mode_t mode);
+ addToFunctionSummaryMap(
+ "mkdirat", Summary(ArgTypes{IntTy, ConstCharPtrTy, *Mode_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(1))));
+ }
+
+ Optional<QualType> Dev_tTy = lookupType("dev_t", ACtx);
+
+ if (Mode_tTy && Dev_tTy) {
+ // int mknod(const char *pathname, mode_t mode, dev_t dev);
+ addToFunctionSummaryMap(
+ "mknod", Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy, *Dev_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev);
+ addToFunctionSummaryMap("mknodat", Summary(ArgTypes{IntTy, ConstCharPtrTy,
+ *Mode_tTy, *Dev_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(1))));
+ }
+
+ if (Mode_tTy) {
+ // int chmod(const char *path, mode_t mode);
+ addToFunctionSummaryMap("chmod",
+ Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags);
+ addToFunctionSummaryMap(
+ "fchmodat", Summary(ArgTypes{IntTy, ConstCharPtrTy, *Mode_tTy, IntTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange,
+ Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int fchmod(int fildes, mode_t mode);
+ addToFunctionSummaryMap(
+ "fchmod",
+ Summary(ArgTypes{IntTy, *Mode_tTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+ }
+
+ Optional<QualType> Uid_tTy = lookupType("uid_t", ACtx);
+ Optional<QualType> Gid_tTy = lookupType("gid_t", ACtx);
+
+ if (Uid_tTy && Gid_tTy) {
+ // int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group,
+ // int flags);
+ addToFunctionSummaryMap(
+ "fchownat",
+ Summary(ArgTypes{IntTy, ConstCharPtrTy, *Uid_tTy, *Gid_tTy, IntTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int chown(const char *path, uid_t owner, gid_t group);
+ addToFunctionSummaryMap(
+ "chown", Summary(ArgTypes{ConstCharPtrTy, *Uid_tTy, *Gid_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int lchown(const char *path, uid_t owner, gid_t group);
+ addToFunctionSummaryMap(
+ "lchown", Summary(ArgTypes{ConstCharPtrTy, *Uid_tTy, *Gid_tTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int fchown(int fildes, uid_t owner, gid_t group);
+ addToFunctionSummaryMap(
+ "fchown", Summary(ArgTypes{IntTy, *Uid_tTy, *Gid_tTy}, RetType{IntTy},
+ NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange,
+ Range(0, IntMax))));
+ }
+
+ // int rmdir(const char *pathname);
+ addToFunctionSummaryMap(
+ "rmdir", Summary(ArgTypes{ConstCharPtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int chdir(const char *path);
+ addToFunctionSummaryMap(
+ "chdir", Summary(ArgTypes{ConstCharPtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int link(const char *oldpath, const char *newpath);
+ addToFunctionSummaryMap("link",
+ Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int linkat(int fd1, const char *path1, int fd2, const char *path2,
+ // int flag);
+ addToFunctionSummaryMap(
+ "linkat",
+ Summary(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy, IntTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(ArgumentCondition(2, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(3))));
+
+ // int unlink(const char *pathname);
+ addToFunctionSummaryMap(
+ "unlink", Summary(ArgTypes{ConstCharPtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int unlinkat(int fd, const char *path, int flag);
+ addToFunctionSummaryMap(
+ "unlinkat",
+ Summary(ArgTypes{IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy},
+ NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ Optional<QualType> StructStatTy = lookupType("stat", ACtx);
+ Optional<QualType> StructStatPtrTy, StructStatPtrRestrictTy;
+ if (StructStatTy) {
+ StructStatPtrTy = ACtx.getPointerType(*StructStatTy);
+ StructStatPtrRestrictTy = ACtx.getLangOpts().C99
+ ? ACtx.getRestrictType(*StructStatPtrTy)
+ : *StructStatPtrTy;
+ }
+
+ if (StructStatPtrTy)
+ // int fstat(int fd, struct stat *statbuf);
+ addToFunctionSummaryMap(
+ "fstat",
+ Summary(ArgTypes{IntTy, *StructStatPtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ if (StructStatPtrRestrictTy) {
+ // int stat(const char *restrict path, struct stat *restrict buf);
+ addToFunctionSummaryMap(
+ "stat",
+ Summary(ArgTypes{ConstCharPtrRestrictTy, *StructStatPtrRestrictTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int lstat(const char *restrict path, struct stat *restrict buf);
+ addToFunctionSummaryMap(
+ "lstat",
+ Summary(ArgTypes{ConstCharPtrRestrictTy, *StructStatPtrRestrictTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int fstatat(int fd, const char *restrict path,
+ // struct stat *restrict buf, int flag);
+ addToFunctionSummaryMap(
+ "fstatat", Summary(ArgTypes{IntTy, ConstCharPtrRestrictTy,
+ *StructStatPtrRestrictTy, IntTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange,
+ Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(NotNull(ArgNo(2))));
+ }
+
+ if (DirPtrTy) {
+ // DIR *opendir(const char *name);
+ addToFunctionSummaryMap("opendir", Summary(ArgTypes{ConstCharPtrTy},
+ RetType{*DirPtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // DIR *fdopendir(int fd);
+ addToFunctionSummaryMap(
+ "fdopendir", Summary(ArgTypes{IntTy}, RetType{*DirPtrTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange,
+ Range(0, IntMax))));
+ }
+
+ // int isatty(int fildes);
+ addToFunctionSummaryMap(
+ "isatty", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
+ if (FilePtrTy) {
+ // FILE *popen(const char *command, const char *type);
+ addToFunctionSummaryMap("popen",
+ Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy},
+ RetType{*FilePtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int pclose(FILE *stream);
+ addToFunctionSummaryMap(
+ "pclose", Summary(ArgTypes{*FilePtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+ }
+
+ // int close(int fildes);
+ addToFunctionSummaryMap(
+ "close", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
+ // long fpathconf(int fildes, int name);
+ addToFunctionSummaryMap(
+ "fpathconf",
+ Summary(ArgTypes{IntTy, IntTy}, RetType{LongTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
+ // long pathconf(const char *path, int name);
+ addToFunctionSummaryMap("pathconf", Summary(ArgTypes{ConstCharPtrTy, IntTy},
+ RetType{LongTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ if (FilePtrTy)
+ // FILE *fdopen(int fd, const char *mode);
+ addToFunctionSummaryMap(
+ "fdopen", Summary(ArgTypes{IntTy, ConstCharPtrTy},
+ RetType{*FilePtrTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ if (DirPtrTy) {
+ // void rewinddir(DIR *dir);
+ addToFunctionSummaryMap(
+ "rewinddir", Summary(ArgTypes{*DirPtrTy}, RetType{VoidTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // void seekdir(DIR *dirp, long loc);
+ addToFunctionSummaryMap("seekdir", Summary(ArgTypes{*DirPtrTy, LongTy},
+ RetType{VoidTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+ }
+
+ // int rand_r(unsigned int *seedp);
+ addToFunctionSummaryMap("rand_r", Summary(ArgTypes{UnsignedIntPtrTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int strcasecmp(const char *s1, const char *s2);
+ addToFunctionSummaryMap("strcasecmp",
+ Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy},
+ RetType{IntTy}, EvalCallAsPure)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int strncasecmp(const char *s1, const char *s2, size_t n);
+ addToFunctionSummaryMap(
+ "strncasecmp", Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, SizeTy},
+ RetType{IntTy}, EvalCallAsPure)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(ArgumentCondition(
+ 2, WithinRange, Range(0, SizeMax))));
+
+ if (FilePtrTy && Off_tTy) {
+
+ // int fileno(FILE *stream);
+ addToFunctionSummaryMap(
+ "fileno", Summary(ArgTypes{*FilePtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int fseeko(FILE *stream, off_t offset, int whence);
+ addToFunctionSummaryMap("fseeko",
+ Summary(ArgTypes{*FilePtrTy, *Off_tTy, IntTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // off_t ftello(FILE *stream);
+ addToFunctionSummaryMap(
+ "ftello", Summary(ArgTypes{*FilePtrTy}, RetType{*Off_tTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+ }
+
+ if (Off_tTy) {
+ Optional<RangeInt> Off_tMax = BVF.getMaxValue(*Off_tTy).getLimitedValue();
+
+ // void *mmap(void *addr, size_t length, int prot, int flags, int fd,
+ // off_t offset);
+ addToFunctionSummaryMap(
+ "mmap",
+ Summary(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, *Off_tTy},
+ RetType{VoidPtrTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(1, WithinRange, Range(1, SizeMax)))
+ .ArgConstraint(
+ ArgumentCondition(4, WithinRange, Range(0, *Off_tMax))));
+ }
+
+ Optional<QualType> Off64_tTy = lookupType("off64_t", ACtx);
+ Optional<RangeInt> Off64_tMax;
+ if (Off64_tTy) {
+ Off64_tMax = BVF.getMaxValue(*Off_tTy).getLimitedValue();
+ // void *mmap64(void *addr, size_t length, int prot, int flags, int fd,
+ // off64_t offset);
+ addToFunctionSummaryMap(
+ "mmap64",
+ Summary(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, *Off64_tTy},
+ RetType{VoidPtrTy}, NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(1, WithinRange, Range(1, SizeMax)))
+ .ArgConstraint(
+ ArgumentCondition(4, WithinRange, Range(0, *Off64_tMax))));
+ }
+
+ // int pipe(int fildes[2]);
+ addToFunctionSummaryMap(
+ "pipe", Summary(ArgTypes{IntPtrTy}, RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ if (Off_tTy)
+ // off_t lseek(int fildes, off_t offset, int whence);
+ addToFunctionSummaryMap(
+ "lseek", Summary(ArgTypes{IntTy, *Off_tTy, IntTy}, RetType{*Off_tTy},
+ NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange,
+ Range(0, IntMax))));
+
+ Optional<QualType> Ssize_tTy = lookupType("ssize_t", ACtx);
+
+ if (Ssize_tTy) {
+ // ssize_t readlink(const char *restrict path, char *restrict buf,
+ // size_t bufsize);
+ addToFunctionSummaryMap(
+ "readlink",
+ Summary(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy},
+ RetType{*Ssize_tTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1),
+ /*BufSize=*/ArgNo(2)))
+ .ArgConstraint(
+ ArgumentCondition(2, WithinRange, Range(0, SizeMax))));
+
+ // ssize_t readlinkat(int fd, const char *restrict path,
+ // char *restrict buf, size_t bufsize);
+ addToFunctionSummaryMap(
+ "readlinkat", Summary(ArgTypes{IntTy, ConstCharPtrRestrictTy,
+ CharPtrRestrictTy, SizeTy},
+ RetType{*Ssize_tTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange,
+ Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(NotNull(ArgNo(2)))
+ .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(2),
+ /*BufSize=*/ArgNo(3)))
+ .ArgConstraint(ArgumentCondition(
+ 3, WithinRange, Range(0, SizeMax))));
+ }
+
+ // int renameat(int olddirfd, const char *oldpath, int newdirfd, const char
+ // *newpath);
+ addToFunctionSummaryMap("renameat", Summary(ArgTypes{IntTy, ConstCharPtrTy,
+ IntTy, ConstCharPtrTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(NotNull(ArgNo(3))));
+
+ // char *realpath(const char *restrict file_name,
+ // char *restrict resolved_name);
+ addToFunctionSummaryMap(
+ "realpath", Summary(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy},
+ RetType{CharPtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ QualType CharPtrConstPtr = ACtx.getPointerType(CharPtrTy.withConst());
+
+ // int execv(const char *path, char *const argv[]);
+ addToFunctionSummaryMap("execv",
+ Summary(ArgTypes{ConstCharPtrTy, CharPtrConstPtr},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int execvp(const char *file, char *const argv[]);
+ addToFunctionSummaryMap("execvp",
+ Summary(ArgTypes{ConstCharPtrTy, CharPtrConstPtr},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int getopt(int argc, char * const argv[], const char *optstring);
+ addToFunctionSummaryMap(
+ "getopt",
+ Summary(ArgTypes{IntTy, CharPtrConstPtr, ConstCharPtrTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(NotNull(ArgNo(2))));
+ }
+
+ // Functions for testing.
+ if (ChecksEnabled[CK_StdCLibraryFunctionsTesterChecker]) {
+ addToFunctionSummaryMap(
+ "__two_constrained_args",
+ Summary(ArgTypes{IntTy, IntTy}, RetType{IntTy}, EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(1)))
+ .ArgConstraint(ArgumentCondition(1U, WithinRange, SingleValue(1))));
+ addToFunctionSummaryMap(
+ "__arg_constrained_twice",
+ Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(1)))
+ .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(2))));
+ addToFunctionSummaryMap(
+ "__defaultparam",
+ Summary(ArgTypes{Irrelevant, IntTy}, RetType{IntTy}, EvalCallAsPure)
+ .ArgConstraint(NotNull(ArgNo(0))));
+ addToFunctionSummaryMap("__variadic",
+ Summary(ArgTypes{VoidPtrTy, ConstCharPtrTy},
+ RetType{IntTy}, EvalCallAsPure)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+ addToFunctionSummaryMap(
+ "__buf_size_arg_constraint",
+ Summary(ArgTypes{ConstVoidPtrTy, SizeTy}, RetType{IntTy},
+ EvalCallAsPure)
+ .ArgConstraint(
+ BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1))));
+ addToFunctionSummaryMap(
+ "__buf_size_arg_constraint_mul",
+ Summary(ArgTypes{ConstVoidPtrTy, SizeTy, SizeTy}, RetType{IntTy},
+ EvalCallAsPure)
+ .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1),
+ /*BufSizeMultiplier=*/ArgNo(2))));
+ }
}
void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) {
- // If this checker grows large enough to support C++, Objective-C, or other
- // standard libraries, we could use multiple register...Checker() functions,
- // which would register various checkers with the help of the same Checker
- // class, turning on different function summaries.
- mgr.registerChecker<StdLibraryFunctionsChecker>();
+ auto *Checker = mgr.registerChecker<StdLibraryFunctionsChecker>();
+ Checker->DisplayLoadedSummaries =
+ mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ Checker, "DisplayLoadedSummaries");
+ Checker->ModelPOSIX =
+ mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, "ModelPOSIX");
}
-bool ento::shouldRegisterStdCLibraryFunctionsChecker(const LangOptions &LO) {
+bool ento::shouldRegisterStdCLibraryFunctionsChecker(const CheckerManager &mgr) {
return true;
}
+
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &mgr) { \
+ StdLibraryFunctionsChecker *checker = \
+ mgr.getChecker<StdLibraryFunctionsChecker>(); \
+ checker->ChecksEnabled[StdLibraryFunctionsChecker::CK_##name] = true; \
+ checker->CheckNames[StdLibraryFunctionsChecker::CK_##name] = \
+ mgr.getCurrentCheckerName(); \
+ } \
+ \
+ bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
+
+REGISTER_CHECKER(StdCLibraryFunctionArgsChecker)
+REGISTER_CHECKER(StdCLibraryFunctionsTesterChecker)
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index 47099f2afb6a..f6abbe4f8f03 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -27,142 +27,453 @@ using namespace std::placeholders;
namespace {
+struct FnDescription;
+
+/// State of the stream error flags.
+/// Sometimes it is not known to the checker what error flags are set.
+/// This is indicated by setting more than one flag to true.
+/// This is an optimization to avoid state splits.
+/// A stream can either be in FEOF or FERROR but not both at the same time.
+/// Multiple flags are set to handle the corresponding states together.
+struct StreamErrorState {
+ /// The stream can be in state where none of the error flags set.
+ bool NoError = true;
+ /// The stream can be in state where the EOF indicator is set.
+ bool FEof = false;
+ /// The stream can be in state where the error indicator is set.
+ bool FError = false;
+
+ bool isNoError() const { return NoError && !FEof && !FError; }
+ bool isFEof() const { return !NoError && FEof && !FError; }
+ bool isFError() const { return !NoError && !FEof && FError; }
+
+ bool operator==(const StreamErrorState &ES) const {
+ return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError;
+ }
+
+ bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); }
+
+ StreamErrorState operator|(const StreamErrorState &E) const {
+ return {NoError || E.NoError, FEof || E.FEof, FError || E.FError};
+ }
+
+ StreamErrorState operator&(const StreamErrorState &E) const {
+ return {NoError && E.NoError, FEof && E.FEof, FError && E.FError};
+ }
+
+ StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; }
+
+ /// Returns if the StreamErrorState is a valid object.
+ operator bool() const { return NoError || FEof || FError; }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddBoolean(NoError);
+ ID.AddBoolean(FEof);
+ ID.AddBoolean(FError);
+ }
+};
+
+const StreamErrorState ErrorNone{true, false, false};
+const StreamErrorState ErrorFEof{false, true, false};
+const StreamErrorState ErrorFError{false, false, true};
+
+/// Full state information about a stream pointer.
struct StreamState {
- enum Kind { Opened, Closed, OpenFailed, Escaped } K;
+ /// The last file operation called in the stream.
+ const FnDescription *LastOperation;
- StreamState(Kind k) : K(k) {}
+ /// State of a stream symbol.
+ /// FIXME: We need maybe an "escaped" state later.
+ enum KindTy {
+ Opened, /// Stream is opened.
+ Closed, /// Closed stream (an invalid stream pointer after it was closed).
+ OpenFailed /// The last open operation has failed.
+ } State;
- bool isOpened() const { return K == Opened; }
- bool isClosed() const { return K == Closed; }
- //bool isOpenFailed() const { return K == OpenFailed; }
- //bool isEscaped() const { return K == Escaped; }
+ /// State of the error flags.
+ /// Ignored in non-opened stream state but must be NoError.
+ StreamErrorState const ErrorState;
- bool operator==(const StreamState &X) const { return K == X.K; }
+ /// Indicate if the file has an "indeterminate file position indicator".
+ /// This can be set at a failing read or write or seek operation.
+ /// If it is set no more read or write is allowed.
+ /// This value is not dependent on the stream error flags:
+ /// The error flag may be cleared with `clearerr` but the file position
+ /// remains still indeterminate.
+ /// This value applies to all error states in ErrorState except FEOF.
+ /// An EOF+indeterminate state is the same as EOF state.
+ bool const FilePositionIndeterminate = false;
- static StreamState getOpened() { return StreamState(Opened); }
- static StreamState getClosed() { return StreamState(Closed); }
- static StreamState getOpenFailed() { return StreamState(OpenFailed); }
- static StreamState getEscaped() { return StreamState(Escaped); }
+ StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES,
+ bool IsFilePositionIndeterminate)
+ : LastOperation(L), State(S), ErrorState(ES),
+ FilePositionIndeterminate(IsFilePositionIndeterminate) {
+ assert((!ES.isFEof() || !IsFilePositionIndeterminate) &&
+ "FilePositionIndeterminate should be false in FEof case.");
+ assert((State == Opened || ErrorState.isNoError()) &&
+ "ErrorState should be None in non-opened stream state.");
+ }
+
+ bool isOpened() const { return State == Opened; }
+ bool isClosed() const { return State == Closed; }
+ bool isOpenFailed() const { return State == OpenFailed; }
+
+ bool operator==(const StreamState &X) const {
+ // In not opened state error state should always NoError, so comparison
+ // here is no problem.
+ return LastOperation == X.LastOperation && State == X.State &&
+ ErrorState == X.ErrorState &&
+ FilePositionIndeterminate == X.FilePositionIndeterminate;
+ }
+
+ static StreamState getOpened(const FnDescription *L,
+ const StreamErrorState &ES = ErrorNone,
+ bool IsFilePositionIndeterminate = false) {
+ return StreamState{L, Opened, ES, IsFilePositionIndeterminate};
+ }
+ static StreamState getClosed(const FnDescription *L) {
+ return StreamState{L, Closed, {}, false};
+ }
+ static StreamState getOpenFailed(const FnDescription *L) {
+ return StreamState{L, OpenFailed, {}, false};
+ }
void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddInteger(K);
+ ID.AddPointer(LastOperation);
+ ID.AddInteger(State);
+ ID.AddInteger(ErrorState);
+ ID.AddBoolean(FilePositionIndeterminate);
}
};
-class StreamChecker : public Checker<eval::Call,
- check::DeadSymbols > {
- mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence,
- BT_doubleclose, BT_ResourceLeak;
+class StreamChecker;
+using FnCheck = std::function<void(const StreamChecker *, const FnDescription *,
+ const CallEvent &, CheckerContext &)>;
+
+using ArgNoTy = unsigned int;
+static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max();
+
+struct FnDescription {
+ FnCheck PreFn;
+ FnCheck EvalFn;
+ ArgNoTy StreamArgNo;
+};
+
+/// Get the value of the stream argument out of the passed call event.
+/// The call should contain a function that is described by Desc.
+SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) {
+ assert(Desc && Desc->StreamArgNo != ArgNone &&
+ "Try to get a non-existing stream argument.");
+ return Call.getArgSVal(Desc->StreamArgNo);
+}
+
+/// Create a conjured symbol return value for a call expression.
+DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) {
+ assert(CE && "Expecting a call expression.");
+
+ const LocationContext *LCtx = C.getLocationContext();
+ return C.getSValBuilder()
+ .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
+ .castAs<DefinedSVal>();
+}
+
+ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C,
+ const CallExpr *CE) {
+ DefinedSVal RetVal = makeRetVal(C, CE);
+ State = State->BindExpr(CE, C.getLocationContext(), RetVal);
+ State = State->assume(RetVal, true);
+ assert(State && "Assumption on new value should not fail.");
+ return State;
+}
+
+ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State,
+ CheckerContext &C, const CallExpr *CE) {
+ State = State->BindExpr(CE, C.getLocationContext(),
+ C.getSValBuilder().makeIntVal(Value, false));
+ return State;
+}
+
+class StreamChecker : public Checker<check::PreCall, eval::Call,
+ check::DeadSymbols, check::PointerEscape> {
+ BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"};
+ BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"};
+ BugType BT_UseAfterOpenFailed{this, "Invalid stream",
+ "Stream handling error"};
+ BugType BT_IndeterminatePosition{this, "Invalid stream state",
+ "Stream handling error"};
+ BugType BT_IllegalWhence{this, "Illegal whence argument",
+ "Stream handling error"};
+ BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"};
+ BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error"};
public:
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+ ProgramStateRef checkPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const;
+
+ /// If true, evaluate special testing stream functions.
+ bool TestMode = false;
private:
- using FnCheck = std::function<void(const StreamChecker *, const CallEvent &,
- CheckerContext &)>;
-
- CallDescriptionMap<FnCheck> Callbacks = {
- {{"fopen"}, &StreamChecker::evalFopen},
- {{"freopen", 3}, &StreamChecker::evalFreopen},
- {{"tmpfile"}, &StreamChecker::evalFopen},
- {{"fclose", 1}, &StreamChecker::evalFclose},
+ CallDescriptionMap<FnDescription> FnDescriptions = {
+ {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
+ {{"freopen", 3},
+ {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
+ {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
+ {{"fclose", 1},
+ {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
{{"fread", 4},
- std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 3)},
+ {&StreamChecker::preFread,
+ std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},
{{"fwrite", 4},
- std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 3)},
- {{"fseek", 3}, &StreamChecker::evalFseek},
- {{"ftell", 1},
- std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
- {{"rewind", 1},
- std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
- {{"fgetpos", 2},
- std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
- {{"fsetpos", 2},
- std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
+ {&StreamChecker::preFwrite,
+ std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},
+ {{"fseek", 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
+ {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0}},
+ {{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0}},
+ {{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}},
+ {{"fsetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}},
{{"clearerr", 1},
- std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
+ {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
{{"feof", 1},
- std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
+ {&StreamChecker::preDefault,
+ std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
+ 0}},
{{"ferror", 1},
- std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
- {{"fileno", 1},
- std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 0)},
+ {&StreamChecker::preDefault,
+ std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
+ 0}},
+ {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0}},
+ };
+
+ CallDescriptionMap<FnDescription> FnTestDescriptions = {
+ {{"StreamTesterChecker_make_feof_stream", 1},
+ {nullptr,
+ std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof),
+ 0}},
+ {{"StreamTesterChecker_make_ferror_stream", 1},
+ {nullptr,
+ std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
+ ErrorFError),
+ 0}},
+ };
+
+ void evalFopen(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void preFreopen(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+ void evalFreopen(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void evalFclose(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void preFread(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void preFwrite(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C, bool IsFread) const;
+
+ void preFseek(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+ void evalFseek(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void preDefault(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void evalClearerr(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C,
+ const StreamErrorState &ErrorKind) const;
+
+ void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C,
+ const StreamErrorState &ErrorKind) const;
+
+ /// Check that the stream (in StreamVal) is not NULL.
+ /// If it can only be NULL a fatal error is emitted and nullptr returned.
+ /// Otherwise the return value is a new state where the stream is constrained
+ /// to be non-null.
+ ProgramStateRef ensureStreamNonNull(SVal StreamVal, CheckerContext &C,
+ ProgramStateRef State) const;
+
+ /// Check that the stream is the opened state.
+ /// If the stream is known to be not opened an error is generated
+ /// and nullptr returned, otherwise the original state is returned.
+ ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C,
+ ProgramStateRef State) const;
+
+ /// Check that the stream has not an invalid ("indeterminate") file position,
+ /// generate warning for it.
+ /// (EOF is not an invalid position.)
+ /// The returned state can be nullptr if a fatal error was generated.
+ /// It can return non-null state if the stream has not an invalid position or
+ /// there is execution path with non-invalid position.
+ ProgramStateRef
+ ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C,
+ ProgramStateRef State) const;
+
+ /// Check the legality of the 'whence' argument of 'fseek'.
+ /// Generate error and return nullptr if it is found to be illegal.
+ /// Otherwise returns the state.
+ /// (State is not changed here because the "whence" value is already known.)
+ ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
+ ProgramStateRef State) const;
+
+ /// Generate warning about stream in EOF state.
+ /// There will be always a state transition into the passed State,
+ /// by the new non-fatal error node or (if failed) a normal transition,
+ /// to ensure uniform handling.
+ void reportFEofWarning(CheckerContext &C, ProgramStateRef State) const;
+
+ /// Find the description data of the function called by a call event.
+ /// Returns nullptr if no function is recognized.
+ const FnDescription *lookupFn(const CallEvent &Call) const {
+ // Recognize "global C functions" with only integral or pointer arguments
+ // (and matching name) as stream functions.
+ if (!Call.isGlobalCFunction())
+ return nullptr;
+ for (auto P : Call.parameters()) {
+ QualType T = P->getType();
+ if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
+ return nullptr;
+ }
+
+ return FnDescriptions.lookup(Call);
+ }
+
+ /// Generate a message for BugReporterVisitor if the stored symbol is
+ /// marked as interesting by the actual bug report.
+ struct NoteFn {
+ const CheckerNameRef CheckerName;
+ SymbolRef StreamSym;
+ std::string Message;
+
+ std::string operator()(PathSensitiveBugReport &BR) const {
+ if (BR.isInteresting(StreamSym) &&
+ CheckerName == BR.getBugType().getCheckerName())
+ return Message;
+
+ return "";
+ }
};
- void evalFopen(const CallEvent &Call, CheckerContext &C) const;
- void evalFreopen(const CallEvent &Call, CheckerContext &C) const;
- void evalFclose(const CallEvent &Call, CheckerContext &C) const;
- void evalFseek(const CallEvent &Call, CheckerContext &C) const;
-
- void checkArgNullStream(const CallEvent &Call, CheckerContext &C,
- unsigned ArgI) const;
- bool checkNullStream(SVal SV, CheckerContext &C,
- ProgramStateRef &State) const;
- void checkFseekWhence(SVal SV, CheckerContext &C,
- ProgramStateRef &State) const;
- bool checkDoubleClose(const CallEvent &Call, CheckerContext &C,
- ProgramStateRef &State) const;
+ const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym,
+ const std::string &Message) const {
+ return C.getNoteTag(NoteFn{getCheckerName(), StreamSym, Message});
+ }
+
+ /// Searches for the ExplodedNode where the file descriptor was acquired for
+ /// StreamSym.
+ static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,
+ SymbolRef StreamSym,
+ CheckerContext &C);
};
} // end anonymous namespace
REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
+inline void assertStreamStateOpened(const StreamState *SS) {
+ assert(SS->isOpened() &&
+ "Previous create of error node for non-opened stream failed?");
+}
-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 ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
+ SymbolRef StreamSym,
+ CheckerContext &C) {
+ ProgramStateRef State = N->getState();
+ // When bug type is resource leak, exploded node N may not have state info
+ // for leaked file descriptor, but predecessor should have it.
+ if (!State->get<StreamMap>(StreamSym))
+ N = N->getFirstPred();
- // Recognize "global C functions" with only integral or pointer arguments
- // (and matching name) as stream functions.
- if (!Call.isGlobalCFunction())
- return false;
- for (auto P : Call.parameters()) {
- QualType T = P->getType();
- if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
- return false;
+ const ExplodedNode *Pred = N;
+ while (N) {
+ State = N->getState();
+ if (!State->get<StreamMap>(StreamSym))
+ return Pred;
+ Pred = N;
+ N = N->getFirstPred();
}
- const FnCheck *Callback = Callbacks.lookup(Call);
- if (!Callback)
+ return nullptr;
+}
+
+void StreamChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const FnDescription *Desc = lookupFn(Call);
+ if (!Desc || !Desc->PreFn)
+ return;
+
+ Desc->PreFn(this, Desc, Call, C);
+}
+
+bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
+ const FnDescription *Desc = lookupFn(Call);
+ if (!Desc && TestMode)
+ Desc = FnTestDescriptions.lookup(Call);
+ if (!Desc || !Desc->EvalFn)
return false;
- (*Callback)(this, Call, C);
+ Desc->EvalFn(this, Desc, Call, C);
return C.isDifferent();
}
-void StreamChecker::evalFopen(const CallEvent &Call, CheckerContext &C) const {
- ProgramStateRef state = C.getState();
- SValBuilder &svalBuilder = C.getSValBuilder();
- const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
- auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
if (!CE)
return;
- DefinedSVal RetVal =
- svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
- .castAs<DefinedSVal>();
- state = state->BindExpr(CE, C.getLocationContext(), RetVal);
+ DefinedSVal RetVal = makeRetVal(C, CE);
+ SymbolRef RetSym = RetVal.getAsSymbol();
+ assert(RetSym && "RetVal must be a symbol here.");
+
+ State = State->BindExpr(CE, C.getLocationContext(), RetVal);
- ConstraintManager &CM = C.getConstraintManager();
// Bifurcate the state into two: one with a valid FILE* pointer, the other
// with a NULL.
- ProgramStateRef stateNotNull, stateNull;
- std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
+ ProgramStateRef StateNotNull, StateNull;
+ std::tie(StateNotNull, StateNull) =
+ C.getConstraintManager().assumeDual(State, RetVal);
+
+ StateNotNull =
+ StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
+ StateNull =
+ StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
+
+ C.addTransition(StateNotNull,
+ constructNoteTag(C, RetSym, "Stream opened here"));
+ C.addTransition(StateNull);
+}
- SymbolRef Sym = RetVal.getAsSymbol();
- assert(Sym && "RetVal must be a symbol here.");
- stateNotNull = stateNotNull->set<StreamMap>(Sym, StreamState::getOpened());
- stateNull = stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed());
+void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
+ // Do not allow NULL as passed stream pointer but allow a closed stream.
+ ProgramStateRef State = C.getState();
+ State = ensureStreamNonNull(getStreamArg(Desc, Call), C, State);
+ if (!State)
+ return;
- C.addTransition(stateNotNull);
- C.addTransition(stateNull);
+ C.addTransition(State);
}
-void StreamChecker::evalFreopen(const CallEvent &Call,
+void StreamChecker::evalFreopen(const FnDescription *Desc,
+ const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
@@ -170,21 +481,21 @@ void StreamChecker::evalFreopen(const CallEvent &Call,
if (!CE)
return;
- Optional<DefinedSVal> StreamVal = Call.getArgSVal(2).getAs<DefinedSVal>();
+ Optional<DefinedSVal> StreamVal =
+ getStreamArg(Desc, Call).getAs<DefinedSVal>();
if (!StreamVal)
return;
- // Do not allow NULL as passed stream pointer.
- // This is not specified in the man page but may crash on some system.
- checkNullStream(*StreamVal, C, State);
- // Check if error was generated.
- if (C.isDifferent())
- return;
SymbolRef StreamSym = StreamVal->getAsSymbol();
- // Do not care about special values for stream ("(FILE *)0x12345"?).
+ // Do not care about concrete values for stream ("(FILE *)0x12345"?).
+ // FIXME: Can be stdin, stdout, stderr such values?
if (!StreamSym)
return;
+ // Do not handle untracked stream. It is probably escaped.
+ if (!State->get<StreamMap>(StreamSym))
+ return;
+
// Generate state for non-failed case.
// Return value is the passed stream pointer.
// According to the documentations, the stream is closed first
@@ -197,129 +508,452 @@ void StreamChecker::evalFreopen(const CallEvent &Call,
C.getSValBuilder().makeNull());
StateRetNotNull =
- StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened());
+ StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
StateRetNull =
- StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed());
+ StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
- C.addTransition(StateRetNotNull);
+ C.addTransition(StateRetNotNull,
+ constructNoteTag(C, StreamSym, "Stream reopened here"));
C.addTransition(StateRetNull);
}
-void StreamChecker::evalFclose(const CallEvent &Call, CheckerContext &C) const {
+void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
ProgramStateRef State = C.getState();
- if (checkDoubleClose(Call, C, State))
+ SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
+ if (!Sym)
+ return;
+
+ const StreamState *SS = State->get<StreamMap>(Sym);
+ if (!SS)
+ return;
+
+ assertStreamStateOpened(SS);
+
+ // Close the File Descriptor.
+ // Regardless if the close fails or not, stream becomes "closed"
+ // and can not be used any more.
+ State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc));
+
+ C.addTransition(State);
+}
+
+void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SVal StreamVal = getStreamArg(Desc, Call);
+ State = ensureStreamNonNull(StreamVal, C, State);
+ if (!State)
+ return;
+ State = ensureStreamOpened(StreamVal, C, State);
+ if (!State)
+ return;
+ State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
+ if (!State)
+ return;
+
+ SymbolRef Sym = StreamVal.getAsSymbol();
+ if (Sym && State->get<StreamMap>(Sym)) {
+ const StreamState *SS = State->get<StreamMap>(Sym);
+ if (SS->ErrorState & ErrorFEof)
+ reportFEofWarning(C, State);
+ } else {
C.addTransition(State);
+ }
}
-void StreamChecker::evalFseek(const CallEvent &Call, CheckerContext &C) const {
- const Expr *AE2 = Call.getArgExpr(2);
- if (!AE2)
+void StreamChecker::preFwrite(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SVal StreamVal = getStreamArg(Desc, Call);
+ State = ensureStreamNonNull(StreamVal, C, State);
+ if (!State)
return;
+ State = ensureStreamOpened(StreamVal, C, State);
+ if (!State)
+ return;
+ State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
+ if (!State)
+ return;
+
+ C.addTransition(State);
+}
+void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
+ const CallEvent &Call, CheckerContext &C,
+ bool IsFread) const {
ProgramStateRef State = C.getState();
+ SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
+ if (!StreamSym)
+ return;
+
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ Optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
+ if (!SizeVal)
+ return;
+ Optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
+ if (!NMembVal)
+ return;
- bool StateChanged = checkNullStream(Call.getArgSVal(0), C, State);
- // Check if error was generated.
- if (C.isDifferent())
+ const StreamState *SS = State->get<StreamMap>(StreamSym);
+ if (!SS)
return;
- // Check the legality of the 'whence' argument of 'fseek'.
- checkFseekWhence(State->getSVal(AE2, C.getLocationContext()), C, State);
+ assertStreamStateOpened(SS);
+
+ // C'99 standard, §7.19.8.1.3, the return value of fread:
+ // The fread function returns the number of elements successfully read, which
+ // may be less than nmemb if a read error or end-of-file is encountered. If
+ // size or nmemb is zero, fread returns zero and the contents of the array and
+ // the state of the stream remain unchanged.
- if (!C.isDifferent() && StateChanged)
+ if (State->isNull(*SizeVal).isConstrainedTrue() ||
+ State->isNull(*NMembVal).isConstrainedTrue()) {
+ // This is the "size or nmemb is zero" case.
+ // Just return 0, do nothing more (not clear the error flags).
+ State = bindInt(0, State, C, CE);
C.addTransition(State);
+ return;
+ }
- return;
+ // Generate a transition for the success state.
+ // If we know the state to be FEOF at fread, do not add a success state.
+ if (!IsFread || (SS->ErrorState != ErrorFEof)) {
+ ProgramStateRef StateNotFailed =
+ State->BindExpr(CE, C.getLocationContext(), *NMembVal);
+ if (StateNotFailed) {
+ StateNotFailed = StateNotFailed->set<StreamMap>(
+ StreamSym, StreamState::getOpened(Desc));
+ C.addTransition(StateNotFailed);
+ }
+ }
+
+ // Add transition for the failed state.
+ Optional<NonLoc> RetVal = makeRetVal(C, CE).castAs<NonLoc>();
+ assert(RetVal && "Value should be NonLoc.");
+ ProgramStateRef StateFailed =
+ State->BindExpr(CE, C.getLocationContext(), *RetVal);
+ if (!StateFailed)
+ return;
+ auto Cond = C.getSValBuilder()
+ .evalBinOpNN(State, BO_LT, *RetVal, *NMembVal,
+ C.getASTContext().IntTy)
+ .getAs<DefinedOrUnknownSVal>();
+ if (!Cond)
+ return;
+ StateFailed = StateFailed->assume(*Cond, true);
+ if (!StateFailed)
+ return;
+
+ StreamErrorState NewES;
+ if (IsFread)
+ NewES = (SS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError;
+ else
+ NewES = ErrorFError;
+ // If a (non-EOF) error occurs, the resulting value of the file position
+ // indicator for the stream is indeterminate.
+ StreamState NewState = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
+ StateFailed = StateFailed->set<StreamMap>(StreamSym, NewState);
+ C.addTransition(StateFailed);
}
-void StreamChecker::checkArgNullStream(const CallEvent &Call, CheckerContext &C,
- unsigned ArgI) const {
+void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
ProgramStateRef State = C.getState();
- if (checkNullStream(Call.getArgSVal(ArgI), C, State))
- C.addTransition(State);
+ SVal StreamVal = getStreamArg(Desc, Call);
+ State = ensureStreamNonNull(StreamVal, C, State);
+ if (!State)
+ return;
+ State = ensureStreamOpened(StreamVal, C, State);
+ if (!State)
+ return;
+ State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);
+ if (!State)
+ return;
+
+ C.addTransition(State);
}
-bool StreamChecker::checkNullStream(SVal SV, CheckerContext &C,
- ProgramStateRef &State) const {
- Optional<DefinedSVal> DV = SV.getAs<DefinedSVal>();
- if (!DV)
- return false;
+void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
+ if (!StreamSym)
+ return;
+
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ // Ignore the call if the stream is not tracked.
+ if (!State->get<StreamMap>(StreamSym))
+ return;
+
+ DefinedSVal RetVal = makeRetVal(C, CE);
+
+ // Make expression result.
+ State = State->BindExpr(CE, C.getLocationContext(), RetVal);
+
+ // Bifurcate the state into failed and non-failed.
+ // Return zero on success, nonzero on error.
+ ProgramStateRef StateNotFailed, StateFailed;
+ std::tie(StateFailed, StateNotFailed) =
+ C.getConstraintManager().assumeDual(State, RetVal);
+
+ // Reset the state to opened with no error.
+ StateNotFailed =
+ StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
+ // We get error.
+ // It is possible that fseek fails but sets none of the error flags.
+ // If fseek failed, assume that the file position becomes indeterminate in any
+ // case.
+ StateFailed = StateFailed->set<StreamMap>(
+ StreamSym,
+ StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError, true));
+
+ C.addTransition(StateNotFailed);
+ C.addTransition(StateFailed);
+}
+
+void StreamChecker::evalClearerr(const FnDescription *Desc,
+ const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
+ if (!StreamSym)
+ return;
+
+ const StreamState *SS = State->get<StreamMap>(StreamSym);
+ if (!SS)
+ return;
+
+ assertStreamStateOpened(SS);
+
+ // FilePositionIndeterminate is not cleared.
+ State = State->set<StreamMap>(
+ StreamSym,
+ StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate));
+ C.addTransition(State);
+}
+
+void StreamChecker::evalFeofFerror(const FnDescription *Desc,
+ const CallEvent &Call, CheckerContext &C,
+ const StreamErrorState &ErrorKind) const {
+ ProgramStateRef State = C.getState();
+ SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
+ if (!StreamSym)
+ return;
+
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ const StreamState *SS = State->get<StreamMap>(StreamSym);
+ if (!SS)
+ return;
+
+ assertStreamStateOpened(SS);
+
+ if (SS->ErrorState & ErrorKind) {
+ // Execution path with error of ErrorKind.
+ // Function returns true.
+ // From now on it is the only one error state.
+ ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE);
+ C.addTransition(TrueState->set<StreamMap>(
+ StreamSym, StreamState::getOpened(Desc, ErrorKind,
+ SS->FilePositionIndeterminate &&
+ !ErrorKind.isFEof())));
+ }
+ if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) {
+ // Execution path(s) with ErrorKind not set.
+ // Function returns false.
+ // New error state is everything before minus ErrorKind.
+ ProgramStateRef FalseState = bindInt(0, State, C, CE);
+ C.addTransition(FalseState->set<StreamMap>(
+ StreamSym,
+ StreamState::getOpened(
+ Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof())));
+ }
+}
+
+void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SVal StreamVal = getStreamArg(Desc, Call);
+ State = ensureStreamNonNull(StreamVal, C, State);
+ if (!State)
+ return;
+ State = ensureStreamOpened(StreamVal, C, State);
+ if (!State)
+ return;
+
+ C.addTransition(State);
+}
+
+void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
+ const CallEvent &Call, CheckerContext &C,
+ const StreamErrorState &ErrorKind) const {
+ ProgramStateRef State = C.getState();
+ SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
+ assert(StreamSym && "Operation not permitted on non-symbolic stream value.");
+ const StreamState *SS = State->get<StreamMap>(StreamSym);
+ assert(SS && "Stream should be tracked by the checker.");
+ State = State->set<StreamMap>(
+ StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind));
+ C.addTransition(State);
+}
+
+ProgramStateRef
+StreamChecker::ensureStreamNonNull(SVal StreamVal, CheckerContext &C,
+ ProgramStateRef State) const {
+ auto Stream = StreamVal.getAs<DefinedSVal>();
+ if (!Stream)
+ return State;
ConstraintManager &CM = C.getConstraintManager();
+
ProgramStateRef StateNotNull, StateNull;
- std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *DV);
+ std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream);
if (!StateNotNull && StateNull) {
if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
- if (!BT_nullfp)
- BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer",
- "Stream pointer might be NULL."));
C.emitReport(std::make_unique<PathSensitiveBugReport>(
- *BT_nullfp, BT_nullfp->getDescription(), N));
+ BT_FileNull, "Stream pointer might be NULL.", N));
}
- return false;
- }
-
- if (StateNotNull) {
- State = StateNotNull;
- return true;
+ return nullptr;
}
- return false;
+ return StateNotNull;
}
-void StreamChecker::checkFseekWhence(SVal SV, CheckerContext &C,
- ProgramStateRef &State) const {
- Optional<nonloc::ConcreteInt> CI = SV.getAs<nonloc::ConcreteInt>();
- if (!CI)
- return;
+ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,
+ CheckerContext &C,
+ ProgramStateRef State) const {
+ SymbolRef Sym = StreamVal.getAsSymbol();
+ if (!Sym)
+ return State;
- int64_t X = CI->getValue().getSExtValue();
- if (X >= 0 && X <= 2)
- return;
+ const StreamState *SS = State->get<StreamMap>(Sym);
+ if (!SS)
+ return State;
- if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
- if (!BT_illegalwhence)
- BT_illegalwhence.reset(
- new BuiltinBug(this, "Illegal whence argument",
- "The whence argument to fseek() should be "
- "SEEK_SET, SEEK_END, or SEEK_CUR."));
- C.emitReport(std::make_unique<PathSensitiveBugReport>(
- *BT_illegalwhence, BT_illegalwhence->getDescription(), N));
+ if (SS->isClosed()) {
+ // Using a stream pointer after 'fclose' causes undefined behavior
+ // according to cppreference.com .
+ ExplodedNode *N = C.generateErrorNode();
+ if (N) {
+ C.emitReport(std::make_unique<PathSensitiveBugReport>(
+ BT_UseAfterClose,
+ "Stream might be already closed. Causes undefined behaviour.", N));
+ return nullptr;
+ }
+
+ return State;
}
+
+ if (SS->isOpenFailed()) {
+ // Using a stream that has failed to open is likely to cause problems.
+ // This should usually not occur because stream pointer is NULL.
+ // But freopen can cause a state when stream pointer remains non-null but
+ // failed to open.
+ ExplodedNode *N = C.generateErrorNode();
+ if (N) {
+ C.emitReport(std::make_unique<PathSensitiveBugReport>(
+ BT_UseAfterOpenFailed,
+ "Stream might be invalid after "
+ "(re-)opening it has failed. "
+ "Can cause undefined behaviour.",
+ N));
+ return nullptr;
+ }
+ return State;
+ }
+
+ return State;
}
-bool StreamChecker::checkDoubleClose(const CallEvent &Call, CheckerContext &C,
- ProgramStateRef &State) const {
- SymbolRef Sym = Call.getArgSVal(0).getAsSymbol();
+ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(
+ SVal StreamVal, CheckerContext &C, ProgramStateRef State) const {
+ static const char *BugMessage =
+ "File position of the stream might be 'indeterminate' "
+ "after a failed operation. "
+ "Can cause undefined behavior.";
+
+ SymbolRef Sym = StreamVal.getAsSymbol();
if (!Sym)
- return false;
+ return State;
const StreamState *SS = State->get<StreamMap>(Sym);
-
- // If the file stream is not tracked, return.
if (!SS)
- return false;
+ return State;
+
+ assert(SS->isOpened() && "First ensure that stream is opened.");
+
+ if (SS->FilePositionIndeterminate) {
+ if (SS->ErrorState & ErrorFEof) {
+ // The error is unknown but may be FEOF.
+ // Continue analysis with the FEOF error state.
+ // Report warning because the other possible error states.
+ ExplodedNode *N = C.generateNonFatalErrorNode(State);
+ if (!N)
+ return nullptr;
- // Check: Double close a File Descriptor could cause undefined behaviour.
- // Conforming to man-pages
- if (SS->isClosed()) {
- ExplodedNode *N = C.generateErrorNode();
- if (N) {
- if (!BT_doubleclose)
- BT_doubleclose.reset(new BuiltinBug(
- this, "Double fclose", "Try to close a file Descriptor already"
- " closed. Cause undefined behaviour."));
C.emitReport(std::make_unique<PathSensitiveBugReport>(
- *BT_doubleclose, BT_doubleclose->getDescription(), N));
+ BT_IndeterminatePosition, BugMessage, N));
+ return State->set<StreamMap>(
+ Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false));
}
- return false;
+
+ // Known or unknown error state without FEOF possible.
+ // Stop analysis, report error.
+ ExplodedNode *N = C.generateErrorNode(State);
+ if (N)
+ C.emitReport(std::make_unique<PathSensitiveBugReport>(
+ BT_IndeterminatePosition, BugMessage, N));
+
+ return nullptr;
}
- // Close the File Descriptor.
- State = State->set<StreamMap>(Sym, StreamState::getClosed());
+ return State;
+}
- return true;
+ProgramStateRef
+StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
+ ProgramStateRef State) const {
+ Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>();
+ if (!CI)
+ return State;
+
+ int64_t X = CI->getValue().getSExtValue();
+ if (X >= 0 && X <= 2)
+ return State;
+
+ if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
+ C.emitReport(std::make_unique<PathSensitiveBugReport>(
+ BT_IllegalWhence,
+ "The whence argument to fseek() should be "
+ "SEEK_SET, SEEK_END, or SEEK_CUR.",
+ N));
+ return nullptr;
+ }
+
+ return State;
+}
+
+void StreamChecker::reportFEofWarning(CheckerContext &C,
+ ProgramStateRef State) const {
+ if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
+ C.emitReport(std::make_unique<PathSensitiveBugReport>(
+ BT_StreamEof,
+ "Read function called when stream is in EOF state. "
+ "Function has no effect.",
+ N));
+ return;
+ }
+ C.addTransition(State);
}
void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
@@ -328,7 +962,7 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
// TODO: Clean up the state.
const StreamMapTy &Map = State->get<StreamMap>();
- for (const auto &I: Map) {
+ for (const auto &I : Map) {
SymbolRef Sym = I.first;
const StreamState &SS = I.second;
if (!SymReaper.isDead(Sym) || !SS.isOpened())
@@ -338,19 +972,77 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
if (!N)
continue;
- if (!BT_ResourceLeak)
- BT_ResourceLeak.reset(
- new BuiltinBug(this, "Resource Leak",
- "Opened File never closed. Potential Resource leak."));
- C.emitReport(std::make_unique<PathSensitiveBugReport>(
- *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N));
+ // Do not warn for non-closed stream at program exit.
+ ExplodedNode *Pred = C.getPredecessor();
+ if (Pred && Pred->getCFGBlock() &&
+ Pred->getCFGBlock()->hasNoReturnElement())
+ continue;
+
+ // Resource leaks can result in multiple warning that describe the same kind
+ // of programming error:
+ // void f() {
+ // FILE *F = fopen("a.txt");
+ // if (rand()) // state split
+ // return; // warning
+ // } // warning
+ // While this isn't necessarily true (leaking the same stream could result
+ // from a different kinds of errors), the reduction in redundant reports
+ // makes this a worthwhile heuristic.
+ // FIXME: Add a checker option to turn this uniqueing feature off.
+
+ const ExplodedNode *StreamOpenNode = getAcquisitionSite(N, Sym, C);
+ assert(StreamOpenNode && "Could not find place of stream opening.");
+ PathDiagnosticLocation LocUsedForUniqueing =
+ PathDiagnosticLocation::createBegin(
+ StreamOpenNode->getStmtForDiagnostics(), C.getSourceManager(),
+ StreamOpenNode->getLocationContext());
+
+ std::unique_ptr<PathSensitiveBugReport> R =
+ std::make_unique<PathSensitiveBugReport>(
+ BT_ResourceLeak,
+ "Opened stream never closed. Potential resource leak.", N,
+ LocUsedForUniqueing,
+ StreamOpenNode->getLocationContext()->getDecl());
+ R->markInteresting(Sym);
+ C.emitReport(std::move(R));
+ }
+}
+
+ProgramStateRef StreamChecker::checkPointerEscape(
+ ProgramStateRef State, const InvalidatedSymbols &Escaped,
+ const CallEvent *Call, PointerEscapeKind Kind) const {
+ // Check for file-handling system call that is not handled by the checker.
+ // FIXME: The checker should be updated to handle all system calls that take
+ // 'FILE*' argument. These are now ignored.
+ if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader())
+ return State;
+
+ for (SymbolRef Sym : Escaped) {
+ // The symbol escaped.
+ // From now the stream can be manipulated in unknown way to the checker,
+ // it is not possible to handle it any more.
+ // Optimistically, assume that the corresponding file handle will be closed
+ // somewhere else.
+ // Remove symbol from state so the following stream calls on this symbol are
+ // not handled by the checker.
+ State = State->remove<StreamMap>(Sym);
}
+ return State;
+}
+
+void ento::registerStreamChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<StreamChecker>();
+}
+
+bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) {
+ return true;
}
-void ento::registerStreamChecker(CheckerManager &mgr) {
- mgr.registerChecker<StreamChecker>();
+void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
+ auto *Checker = Mgr.getChecker<StreamChecker>();
+ Checker->TestMode = true;
}
-bool ento::shouldRegisterStreamChecker(const LangOptions &LO) {
+bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
index f81705304f3a..916977c10c0c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
@@ -63,6 +63,6 @@ void ento::registerTaintTesterChecker(CheckerManager &mgr) {
mgr.registerChecker<TaintTesterChecker>();
}
-bool ento::shouldRegisterTaintTesterChecker(const LangOptions &LO) {
+bool ento::shouldRegisterTaintTesterChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
index 3663b0963692..eeec807ccee4 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
@@ -261,6 +261,6 @@ void ento::registerTestAfterDivZeroChecker(CheckerManager &mgr) {
mgr.registerChecker<TestAfterDivZeroChecker>();
}
-bool ento::shouldRegisterTestAfterDivZeroChecker(const LangOptions &LO) {
+bool ento::shouldRegisterTestAfterDivZeroChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
index 73183aa468f6..2f316bd3b20d 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
@@ -64,7 +64,7 @@ void ento::registerTraversalDumper(CheckerManager &mgr) {
mgr.registerChecker<TraversalDumper>();
}
-bool ento::shouldRegisterTraversalDumper(const LangOptions &LO) {
+bool ento::shouldRegisterTraversalDumper(const CheckerManager &mgr) {
return true;
}
@@ -116,6 +116,6 @@ void ento::registerCallDumper(CheckerManager &mgr) {
mgr.registerChecker<CallDumper>();
}
-bool ento::shouldRegisterCallDumper(const LangOptions &LO) {
+bool ento::shouldRegisterCallDumper(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp
index 62a4c2ab0209..5cc713172527 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp
@@ -252,6 +252,6 @@ void ento::registerTrustNonnullChecker(CheckerManager &Mgr) {
Mgr.registerChecker<TrustNonnullChecker>(Mgr.getASTContext());
}
-bool ento::shouldRegisterTrustNonnullChecker(const LangOptions &LO) {
+bool ento::shouldRegisterTrustNonnullChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
index 247cba7dc933..3e0caaf79ca0 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
@@ -110,6 +110,6 @@ void ento::registerUndefBranchChecker(CheckerManager &mgr) {
mgr.registerChecker<UndefBranchChecker>();
}
-bool ento::shouldRegisterUndefBranchChecker(const LangOptions &LO) {
+bool ento::shouldRegisterUndefBranchChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
index 7b581bef3900..e457513d8de4 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
@@ -101,6 +101,6 @@ void ento::registerUndefCapturedBlockVarChecker(CheckerManager &mgr) {
mgr.registerChecker<UndefCapturedBlockVarChecker>();
}
-bool ento::shouldRegisterUndefCapturedBlockVarChecker(const LangOptions &LO) {
+bool ento::shouldRegisterUndefCapturedBlockVarChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
index a2f3e0da13fb..392da4818098 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
@@ -16,6 +16,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
@@ -50,10 +51,10 @@ static bool isArrayIndexOutOfBounds(CheckerContext &C, const Expr *Ex) {
return false;
DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
- DefinedOrUnknownSVal NumElements = C.getStoreManager().getSizeInElements(
- state, ER->getSuperRegion(), ER->getValueType());
- ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true);
- ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false);
+ DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
+ state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType());
+ ProgramStateRef StInBound = state->assumeInBound(Idx, ElementCount, true);
+ ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false);
return StOutBound && !StInBound;
}
@@ -186,6 +187,6 @@ void ento::registerUndefResultChecker(CheckerManager &mgr) {
mgr.registerChecker<UndefResultChecker>();
}
-bool ento::shouldRegisterUndefResultChecker(const LangOptions &LO) {
+bool ento::shouldRegisterUndefResultChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
index 2f075eaeb03b..fdefe75e8201 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
@@ -62,6 +62,6 @@ void ento::registerUndefinedArraySubscriptChecker(CheckerManager &mgr) {
mgr.registerChecker<UndefinedArraySubscriptChecker>();
}
-bool ento::shouldRegisterUndefinedArraySubscriptChecker(const LangOptions &LO) {
+bool ento::shouldRegisterUndefinedArraySubscriptChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
index 277a8a143328..05f8f6084c0b 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
@@ -120,6 +120,6 @@ void ento::registerUndefinedAssignmentChecker(CheckerManager &mgr) {
mgr.registerChecker<UndefinedAssignmentChecker>();
}
-bool ento::shouldRegisterUndefinedAssignmentChecker(const LangOptions &LO) {
+bool ento::shouldRegisterUndefinedAssignmentChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
index 020df8a1bb8c..4182b51c02b0 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
@@ -541,14 +541,11 @@ static bool hasUnguardedAccess(const FieldDecl *FD, ProgramStateRef State) {
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")))));
+ hasAnyName("exit", "panic", "error", "Assert", "assert", "ziperr",
+ "assfail", "db_error", "__assert", "__assert2", "_wassert",
+ "__assert_rtn", "__assert_fail", "dtrace_assfail",
+ "yy_fatal_error", "_XCAssertionFailureHandler",
+ "_DTAssertionFailureHandler", "_TSAssertionFailureHandler"))));
auto NoReturnFuncM = callExpr(callee(functionDecl(isNoReturn())));
@@ -602,13 +599,13 @@ std::string clang::ento::getVariableName(const FieldDecl *Field) {
llvm_unreachable("No other capture type is expected!");
}
- return Field->getName();
+ return std::string(Field->getName());
}
void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
auto Chk = Mgr.registerChecker<UninitializedObjectChecker>();
- AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions();
+ const AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions();
UninitObjCheckerOptions &ChOpts = Chk->Opts;
ChOpts.IsPedantic = AnOpts.getCheckerBooleanOption(Chk, "Pedantic");
@@ -617,7 +614,7 @@ void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
ChOpts.CheckPointeeInitialization = AnOpts.getCheckerBooleanOption(
Chk, "CheckPointeeInitialization");
ChOpts.IgnoredRecordsWithFieldPattern =
- AnOpts.getCheckerStringOption(Chk, "IgnoreRecordsWithField");
+ std::string(AnOpts.getCheckerStringOption(Chk, "IgnoreRecordsWithField"));
ChOpts.IgnoreGuardedFields =
AnOpts.getCheckerBooleanOption(Chk, "IgnoreGuardedFields");
@@ -628,6 +625,6 @@ void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
"\"" + ErrorMsg + "\"");
}
-bool ento::shouldRegisterUninitializedObjectChecker(const LangOptions &LO) {
+bool ento::shouldRegisterUninitializedObjectChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
index f4e225d836f3..381334de068e 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
@@ -20,6 +20,7 @@
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
@@ -503,7 +504,7 @@ void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,
mgr.registerChecker<CHECKERNAME>(); \
} \
\
- bool ento::shouldRegister##CHECKERNAME(const LangOptions &LO) { \
+ bool ento::shouldRegister##CHECKERNAME(const CheckerManager &mgr) { \
return true; \
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
index 65dd82675df9..74eec81ffb3e 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
@@ -257,6 +257,6 @@ void ento::registerUnreachableCodeChecker(CheckerManager &mgr) {
mgr.registerChecker<UnreachableCodeChecker>();
}
-bool ento::shouldRegisterUnreachableCodeChecker(const LangOptions &LO) {
+bool ento::shouldRegisterUnreachableCodeChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
index b92757312dc6..d76b2a06aba5 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
@@ -14,12 +14,13 @@
//===----------------------------------------------------------------------===//
#include "Taint.h"
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/CharUnits.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/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
@@ -29,9 +30,28 @@ using namespace ento;
using namespace taint;
namespace {
-class VLASizeChecker : public Checker< check::PreStmt<DeclStmt> > {
+class VLASizeChecker
+ : public Checker<check::PreStmt<DeclStmt>,
+ check::PreStmt<UnaryExprOrTypeTraitExpr>> {
mutable std::unique_ptr<BugType> BT;
- enum VLASize_Kind { VLA_Garbage, VLA_Zero, VLA_Tainted, VLA_Negative };
+ enum VLASize_Kind {
+ VLA_Garbage,
+ VLA_Zero,
+ VLA_Tainted,
+ VLA_Negative,
+ VLA_Overflow
+ };
+
+ /// Check a VLA for validity.
+ /// Every dimension of the array and the total size is checked for validity.
+ /// Returns null or a new state where the size is validated.
+ /// 'ArraySize' will contain SVal that refers to the total size (in char)
+ /// of the array.
+ ProgramStateRef checkVLA(CheckerContext &C, ProgramStateRef State,
+ const VariableArrayType *VLA, SVal &ArraySize) const;
+ /// Check a single VLA index size expression for validity.
+ ProgramStateRef checkVLAIndexSize(CheckerContext &C, ProgramStateRef State,
+ const Expr *SizeE) const;
void reportBug(VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State,
CheckerContext &C,
@@ -39,9 +59,155 @@ class VLASizeChecker : public Checker< check::PreStmt<DeclStmt> > {
public:
void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const;
+ void checkPreStmt(const UnaryExprOrTypeTraitExpr *UETTE,
+ CheckerContext &C) const;
};
} // end anonymous namespace
+ProgramStateRef VLASizeChecker::checkVLA(CheckerContext &C,
+ ProgramStateRef State,
+ const VariableArrayType *VLA,
+ SVal &ArraySize) const {
+ assert(VLA && "Function should be called with non-null VLA argument.");
+
+ const VariableArrayType *VLALast = nullptr;
+ llvm::SmallVector<const Expr *, 2> VLASizes;
+
+ // Walk over the VLAs for every dimension until a non-VLA is found.
+ // There is a VariableArrayType for every dimension (fixed or variable) until
+ // the most inner array that is variably modified.
+ // Dimension sizes are collected into 'VLASizes'. 'VLALast' is set to the
+ // innermost VLA that was encountered.
+ // In "int vla[x][2][y][3]" this will be the array for index "y" (with type
+ // int[3]). 'VLASizes' contains 'x', '2', and 'y'.
+ while (VLA) {
+ const Expr *SizeE = VLA->getSizeExpr();
+ State = checkVLAIndexSize(C, State, SizeE);
+ if (!State)
+ return nullptr;
+ VLASizes.push_back(SizeE);
+ VLALast = VLA;
+ VLA = C.getASTContext().getAsVariableArrayType(VLA->getElementType());
+ };
+ assert(VLALast &&
+ "Array should have at least one variably-modified dimension.");
+
+ ASTContext &Ctx = C.getASTContext();
+ SValBuilder &SVB = C.getSValBuilder();
+ CanQualType SizeTy = Ctx.getSizeType();
+ uint64_t SizeMax =
+ SVB.getBasicValueFactory().getMaxValue(SizeTy).getZExtValue();
+
+ // Get the element size.
+ CharUnits EleSize = Ctx.getTypeSizeInChars(VLALast->getElementType());
+ NonLoc ArrSize =
+ SVB.makeIntVal(EleSize.getQuantity(), SizeTy).castAs<NonLoc>();
+
+ // Try to calculate the known real size of the array in KnownSize.
+ uint64_t KnownSize = 0;
+ if (const llvm::APSInt *KV = SVB.getKnownValue(State, ArrSize))
+ KnownSize = KV->getZExtValue();
+
+ for (const Expr *SizeE : VLASizes) {
+ auto SizeD = C.getSVal(SizeE).castAs<DefinedSVal>();
+ // Convert the array length to size_t.
+ NonLoc IndexLength =
+ SVB.evalCast(SizeD, SizeTy, SizeE->getType()).castAs<NonLoc>();
+ // Multiply the array length by the element size.
+ SVal Mul = SVB.evalBinOpNN(State, BO_Mul, ArrSize, IndexLength, SizeTy);
+ if (auto MulNonLoc = Mul.getAs<NonLoc>())
+ ArrSize = *MulNonLoc;
+ else
+ // Extent could not be determined.
+ return State;
+
+ if (const llvm::APSInt *IndexLVal = SVB.getKnownValue(State, IndexLength)) {
+ // Check if the array size will overflow.
+ // Size overflow check does not work with symbolic expressions because a
+ // overflow situation can not be detected easily.
+ uint64_t IndexL = IndexLVal->getZExtValue();
+ // FIXME: See https://reviews.llvm.org/D80903 for discussion of
+ // some difference in assume and getKnownValue that leads to
+ // unexpected behavior. Just bail on IndexL == 0 at this point.
+ if (IndexL == 0)
+ return nullptr;
+
+ if (KnownSize <= SizeMax / IndexL) {
+ KnownSize *= IndexL;
+ } else {
+ // Array size does not fit into size_t.
+ reportBug(VLA_Overflow, SizeE, State, C);
+ return nullptr;
+ }
+ } else {
+ KnownSize = 0;
+ }
+ }
+
+ ArraySize = ArrSize;
+
+ return State;
+}
+
+ProgramStateRef VLASizeChecker::checkVLAIndexSize(CheckerContext &C,
+ ProgramStateRef State,
+ const Expr *SizeE) const {
+ SVal SizeV = C.getSVal(SizeE);
+
+ if (SizeV.isUndef()) {
+ reportBug(VLA_Garbage, SizeE, State, C);
+ return nullptr;
+ }
+
+ // See if the size value is known. It can't be undefined because we would have
+ // warned about that already.
+ if (SizeV.isUnknown())
+ return nullptr;
+
+ // Check if the size is tainted.
+ if (isTainted(State, SizeV)) {
+ reportBug(VLA_Tainted, SizeE, nullptr, C,
+ std::make_unique<TaintBugVisitor>(SizeV));
+ return nullptr;
+ }
+
+ // Check if the size is zero.
+ DefinedSVal SizeD = SizeV.castAs<DefinedSVal>();
+
+ ProgramStateRef StateNotZero, StateZero;
+ std::tie(StateNotZero, StateZero) = State->assume(SizeD);
+
+ if (StateZero && !StateNotZero) {
+ reportBug(VLA_Zero, SizeE, StateZero, C);
+ return nullptr;
+ }
+
+ // From this point on, assume that the size is not zero.
+ State = StateNotZero;
+
+ // Check if the size is negative.
+ SValBuilder &SVB = C.getSValBuilder();
+
+ QualType SizeTy = SizeE->getType();
+ DefinedOrUnknownSVal Zero = SVB.makeZeroVal(SizeTy);
+
+ SVal LessThanZeroVal = SVB.evalBinOp(State, BO_LT, SizeD, Zero, SizeTy);
+ if (Optional<DefinedSVal> LessThanZeroDVal =
+ LessThanZeroVal.getAs<DefinedSVal>()) {
+ ConstraintManager &CM = C.getConstraintManager();
+ ProgramStateRef StatePos, StateNeg;
+
+ std::tie(StateNeg, StatePos) = CM.assumeDual(State, *LessThanZeroDVal);
+ if (StateNeg && !StatePos) {
+ reportBug(VLA_Negative, SizeE, State, C);
+ return nullptr;
+ }
+ State = StatePos;
+ }
+
+ return State;
+}
+
void VLASizeChecker::reportBug(
VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State,
CheckerContext &C, std::unique_ptr<BugReporterVisitor> Visitor) const {
@@ -70,6 +236,9 @@ void VLASizeChecker::reportBug(
case VLA_Negative:
os << "has negative size";
break;
+ case VLA_Overflow:
+ os << "has too large size";
+ break;
}
auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
@@ -83,108 +252,89 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
if (!DS->isSingleDecl())
return;
- const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
- if (!VD)
- return;
-
ASTContext &Ctx = C.getASTContext();
- const VariableArrayType *VLA = Ctx.getAsVariableArrayType(VD->getType());
- if (!VLA)
- return;
+ SValBuilder &SVB = C.getSValBuilder();
+ ProgramStateRef State = C.getState();
+ QualType TypeToCheck;
- // FIXME: Handle multi-dimensional VLAs.
- const Expr *SE = VLA->getSizeExpr();
- ProgramStateRef state = C.getState();
- SVal sizeV = C.getSVal(SE);
+ const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
- if (sizeV.isUndef()) {
- reportBug(VLA_Garbage, SE, state, C);
+ if (VD)
+ TypeToCheck = VD->getType().getCanonicalType();
+ else if (const auto *TND = dyn_cast<TypedefNameDecl>(DS->getSingleDecl()))
+ TypeToCheck = TND->getUnderlyingType().getCanonicalType();
+ else
return;
- }
- // See if the size value is known. It can't be undefined because we would have
- // warned about that already.
- if (sizeV.isUnknown())
+ const VariableArrayType *VLA = Ctx.getAsVariableArrayType(TypeToCheck);
+ if (!VLA)
return;
- // Check if the size is tainted.
- if (isTainted(state, sizeV)) {
- reportBug(VLA_Tainted, SE, nullptr, C,
- std::make_unique<TaintBugVisitor>(sizeV));
- return;
- }
+ // Check the VLA sizes for validity.
- // Check if the size is zero.
- DefinedSVal sizeD = sizeV.castAs<DefinedSVal>();
+ SVal ArraySize;
- ProgramStateRef stateNotZero, stateZero;
- std::tie(stateNotZero, stateZero) = state->assume(sizeD);
+ State = checkVLA(C, State, VLA, ArraySize);
+ if (!State)
+ return;
- if (stateZero && !stateNotZero) {
- reportBug(VLA_Zero, SE, stateZero, C);
+ auto ArraySizeNL = ArraySize.getAs<NonLoc>();
+ if (!ArraySizeNL) {
+ // Array size could not be determined but state may contain new assumptions.
+ C.addTransition(State);
return;
}
- // From this point on, assume that the size is not zero.
- state = stateNotZero;
-
// VLASizeChecker is responsible for defining the extent of the array being
// declared. We do this by multiplying the array length by the element size,
// then matching that with the array region's extent symbol.
- // Check if the size is negative.
- SValBuilder &svalBuilder = C.getSValBuilder();
+ if (VD) {
+ // Assume that the array's size matches the region size.
+ const LocationContext *LC = C.getLocationContext();
+ DefinedOrUnknownSVal DynSize =
+ getDynamicSize(State, State->getRegion(VD, LC), SVB);
- QualType Ty = SE->getType();
- DefinedOrUnknownSVal Zero = svalBuilder.makeZeroVal(Ty);
+ DefinedOrUnknownSVal SizeIsKnown = SVB.evalEQ(State, DynSize, *ArraySizeNL);
+ State = State->assume(SizeIsKnown, true);
- SVal LessThanZeroVal = svalBuilder.evalBinOp(state, BO_LT, sizeD, Zero, Ty);
- if (Optional<DefinedSVal> LessThanZeroDVal =
- LessThanZeroVal.getAs<DefinedSVal>()) {
- ConstraintManager &CM = C.getConstraintManager();
- ProgramStateRef StatePos, StateNeg;
-
- std::tie(StateNeg, StatePos) = CM.assumeDual(state, *LessThanZeroDVal);
- if (StateNeg && !StatePos) {
- reportBug(VLA_Negative, SE, state, C);
- return;
- }
- state = StatePos;
+ // Assume should not fail at this point.
+ assert(State);
}
- // Convert the array length to size_t.
- QualType SizeTy = Ctx.getSizeType();
- NonLoc ArrayLength =
- svalBuilder.evalCast(sizeD, SizeTy, SE->getType()).castAs<NonLoc>();
+ // Remember our assumptions!
+ C.addTransition(State);
+}
- // Get the element size.
- CharUnits EleSize = Ctx.getTypeSizeInChars(VLA->getElementType());
- SVal EleSizeVal = svalBuilder.makeIntVal(EleSize.getQuantity(), SizeTy);
+void VLASizeChecker::checkPreStmt(const UnaryExprOrTypeTraitExpr *UETTE,
+ CheckerContext &C) const {
+ // Want to check for sizeof.
+ if (UETTE->getKind() != UETT_SizeOf)
+ return;
- // Multiply the array length by the element size.
- SVal ArraySizeVal = svalBuilder.evalBinOpNN(
- state, BO_Mul, ArrayLength, EleSizeVal.castAs<NonLoc>(), SizeTy);
+ // Ensure a type argument.
+ if (!UETTE->isArgumentType())
+ return;
- // Finally, assume that the array's extent matches the given size.
- const LocationContext *LC = C.getLocationContext();
- DefinedOrUnknownSVal Extent =
- state->getRegion(VD, LC)->getExtent(svalBuilder);
- DefinedOrUnknownSVal ArraySize = ArraySizeVal.castAs<DefinedOrUnknownSVal>();
- DefinedOrUnknownSVal sizeIsKnown =
- svalBuilder.evalEQ(state, Extent, ArraySize);
- state = state->assume(sizeIsKnown, true);
+ const VariableArrayType *VLA = C.getASTContext().getAsVariableArrayType(
+ UETTE->getTypeOfArgument().getCanonicalType());
+ // Ensure that the type is a VLA.
+ if (!VLA)
+ return;
- // Assume should not fail at this point.
- assert(state);
+ ProgramStateRef State = C.getState();
+ SVal ArraySize;
+ State = checkVLA(C, State, VLA, ArraySize);
+ if (!State)
+ return;
- // Remember our assumptions!
- C.addTransition(state);
+ C.addTransition(State);
}
void ento::registerVLASizeChecker(CheckerManager &mgr) {
mgr.registerChecker<VLASizeChecker>();
}
-bool ento::shouldRegisterVLASizeChecker(const LangOptions &LO) {
+bool ento::shouldRegisterVLASizeChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
index a3610514a924..dde5912b6d6e 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
@@ -404,7 +404,7 @@ void ento::registerValistBase(CheckerManager &mgr) {
mgr.registerChecker<ValistChecker>();
}
-bool ento::shouldRegisterValistBase(const LangOptions &LO) {
+bool ento::shouldRegisterValistBase(const CheckerManager &mgr) {
return true;
}
@@ -416,7 +416,7 @@ bool ento::shouldRegisterValistBase(const LangOptions &LO) {
mgr.getCurrentCheckerName(); \
} \
\
- bool ento::shouldRegister##name##Checker(const LangOptions &LO) { \
+ bool ento::shouldRegister##name##Checker(const CheckerManager &mgr) { \
return true; \
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
index 6724eead5072..8f147026ae19 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
@@ -98,12 +98,13 @@ bool VforkChecker::isCallWhitelisted(const IdentifierInfo *II,
if (VforkWhitelist.empty()) {
// According to manpage.
const char *ids[] = {
- "_exit",
"_Exit",
+ "_exit",
"execl",
- "execlp",
"execle",
+ "execlp",
"execv",
+ "execve",
"execvp",
"execvpe",
nullptr
@@ -216,6 +217,6 @@ void ento::registerVforkChecker(CheckerManager &mgr) {
mgr.registerChecker<VforkChecker>();
}
-bool ento::shouldRegisterVforkChecker(const LangOptions &LO) {
+bool ento::shouldRegisterVforkChecker(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
index fd93fc33115f..f49ee5fa5ad3 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
@@ -224,14 +224,17 @@ void ento::registerVirtualCallChecker(CheckerManager &Mgr) {
}
}
-bool ento::shouldRegisterVirtualCallModeling(const LangOptions &LO) {
+bool ento::shouldRegisterVirtualCallModeling(const CheckerManager &mgr) {
+ const LangOptions &LO = mgr.getLangOpts();
return LO.CPlusPlus;
}
-bool ento::shouldRegisterPureVirtualCallChecker(const LangOptions &LO) {
+bool ento::shouldRegisterPureVirtualCallChecker(const CheckerManager &mgr) {
+ const LangOptions &LO = mgr.getLangOpts();
return LO.CPlusPlus;
}
-bool ento::shouldRegisterVirtualCallChecker(const LangOptions &LO) {
+bool ento::shouldRegisterVirtualCallChecker(const CheckerManager &mgr) {
+ const LangOptions &LO = mgr.getLangOpts();
return LO.CPlusPlus;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
new file mode 100644
index 000000000000..34c072ac2241
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -0,0 +1,93 @@
+//=======- ASTUtils.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+
+using llvm::Optional;
+namespace clang {
+
+std::pair<const Expr *, bool>
+tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
+ while (E) {
+ if (auto *cast = dyn_cast<CastExpr>(E)) {
+ if (StopAtFirstRefCountedObj) {
+ if (auto *ConversionFunc =
+ dyn_cast_or_null<FunctionDecl>(cast->getConversionFunction())) {
+ if (isCtorOfRefCounted(ConversionFunc))
+ return {E, true};
+ }
+ }
+ // FIXME: This can give false "origin" that would lead to false negatives
+ // in checkers. See https://reviews.llvm.org/D37023 for reference.
+ E = cast->getSubExpr();
+ continue;
+ }
+ if (auto *call = dyn_cast<CallExpr>(E)) {
+ if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(call)) {
+ if (isGetterOfRefCounted(memberCall->getMethodDecl())) {
+ E = memberCall->getImplicitObjectArgument();
+ if (StopAtFirstRefCountedObj) {
+ return {E, true};
+ }
+ continue;
+ }
+ }
+
+ if (auto *operatorCall = dyn_cast<CXXOperatorCallExpr>(E)) {
+ if (operatorCall->getNumArgs() == 1) {
+ E = operatorCall->getArg(0);
+ continue;
+ }
+ }
+
+ if (auto *callee = call->getDirectCallee()) {
+ if (isCtorOfRefCounted(callee)) {
+ if (StopAtFirstRefCountedObj)
+ return {E, true};
+
+ E = call->getArg(0);
+ continue;
+ }
+
+ if (isPtrConversion(callee)) {
+ E = call->getArg(0);
+ continue;
+ }
+ }
+ }
+ if (auto *unaryOp = dyn_cast<UnaryOperator>(E)) {
+ // FIXME: Currently accepts ANY unary operator. Is it OK?
+ E = unaryOp->getSubExpr();
+ continue;
+ }
+
+ break;
+ }
+ // Some other expression.
+ return {E, false};
+}
+
+bool isASafeCallArg(const Expr *E) {
+ assert(E);
+ if (auto *Ref = dyn_cast<DeclRefExpr>(E)) {
+ if (auto *D = dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
+ if (isa<ParmVarDecl>(D) || D->isLocalVarDecl())
+ return true;
+ }
+ }
+
+ // TODO: checker for method calls on non-refcounted objects
+ return isa<CXXThisExpr>(E);
+}
+
+} // namespace clang
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
new file mode 100644
index 000000000000..ed4577755457
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
@@ -0,0 +1,84 @@
+//=======- ASTUtis.h ---------------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYZER_WEBKIT_ASTUTILS_H
+#define LLVM_CLANG_ANALYZER_WEBKIT_ASTUTILS_H
+
+#include "clang/AST/Decl.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/Support/Casting.h"
+
+#include <string>
+#include <utility>
+
+namespace clang {
+class CXXRecordDecl;
+class CXXBaseSpecifier;
+class FunctionDecl;
+class CXXMethodDecl;
+class Expr;
+
+/// This function de-facto defines a set of transformations that we consider
+/// safe (in heuristical sense). These transformation if passed a safe value as
+/// an input should provide a safe value (or an object that provides safe
+/// values).
+///
+/// For more context see Static Analyzer checkers documentation - specifically
+/// webkit.UncountedCallArgsChecker checker. Whitelist of transformations:
+/// - constructors of ref-counted types (including factory methods)
+/// - getters of ref-counted types
+/// - member overloaded operators
+/// - casts
+/// - unary operators like ``&`` or ``*``
+///
+/// If passed expression is of type uncounted pointer/reference we try to find
+/// the "origin" of the pointer value.
+/// Origin can be for example a local variable, nullptr, constant or
+/// this-pointer.
+///
+/// Certain subexpression nodes represent transformations that don't affect
+/// where the memory address originates from. We try to traverse such
+/// subexpressions to get to the relevant child nodes. Whenever we encounter a
+/// subexpression that either can't be ignored, we don't model its semantics or
+/// that has multiple children we stop.
+///
+/// \p E is an expression of uncounted pointer/reference type.
+/// If \p StopAtFirstRefCountedObj is true and we encounter a subexpression that
+/// represents ref-counted object during the traversal we return relevant
+/// sub-expression and true.
+///
+/// \returns subexpression that we traversed to and if \p
+/// StopAtFirstRefCountedObj is true we also return whether we stopped early.
+std::pair<const clang::Expr *, bool>
+tryToFindPtrOrigin(const clang::Expr *E, bool StopAtFirstRefCountedObj);
+
+/// For \p E referring to a ref-countable/-counted pointer/reference we return
+/// whether it's a safe call argument. Examples: function parameter or
+/// this-pointer. The logic relies on the set of recursive rules we enforce for
+/// WebKit codebase.
+///
+/// \returns Whether \p E is a safe call arugment.
+bool isASafeCallArg(const clang::Expr *E);
+
+/// \returns name of AST node or empty string.
+template <typename T> std::string safeGetName(const T *ASTNode) {
+ const auto *const ND = llvm::dyn_cast_or_null<clang::NamedDecl>(ASTNode);
+ if (!ND)
+ return "";
+
+ // In case F is for example "operator|" the getName() method below would
+ // assert.
+ if (!ND->getDeclName().isIdentifier())
+ return "";
+
+ return ND->getName().str();
+}
+
+} // namespace clang
+
+#endif
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h
new file mode 100644
index 000000000000..781a8d746001
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h
@@ -0,0 +1,36 @@
+//=======- DiagOutputUtils.h -------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYZER_WEBKIT_DIAGPRINTUTILS_H
+#define LLVM_CLANG_ANALYZER_WEBKIT_DIAGPRINTUTILS_H
+
+#include "clang/AST/Decl.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+
+template <typename NamedDeclDerivedT>
+void printQuotedQualifiedName(llvm::raw_ostream &Os,
+ const NamedDeclDerivedT &D) {
+ Os << "'";
+ D->getNameForDiagnostic(Os, D->getASTContext().getPrintingPolicy(),
+ /*Qualified=*/true);
+ Os << "'";
+}
+
+template <typename NamedDeclDerivedT>
+void printQuotedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D) {
+ Os << "'";
+ D->getNameForDiagnostic(Os, D->getASTContext().getPrintingPolicy(),
+ /*Qualified=*/false);
+ Os << "'";
+}
+
+} // namespace clang
+
+#endif
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
new file mode 100644
index 000000000000..3956db933b35
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
@@ -0,0 +1,155 @@
+//=======- NoUncountedMembersChecker.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTUtils.h"
+#include "DiagOutputUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/Support/Casting.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class NoUncountedMemberChecker
+ : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+private:
+ BugType Bug;
+ mutable BugReporter *BR;
+
+public:
+ NoUncountedMemberChecker()
+ : Bug(this,
+ "Member variable is a raw-poiner/reference to reference-countable "
+ "type",
+ "WebKit coding guidelines") {}
+
+ void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+ BugReporter &BRArg) const {
+ BR = &BRArg;
+
+ // The calls to checkAST* from AnalysisConsumer don't
+ // visit template instantiations or lambda classes. We
+ // want to visit those, so we make our own RecursiveASTVisitor.
+ struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+ const NoUncountedMemberChecker *Checker;
+ explicit LocalVisitor(const NoUncountedMemberChecker *Checker)
+ : Checker(Checker) {
+ assert(Checker);
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return false; }
+
+ bool VisitRecordDecl(const RecordDecl *RD) {
+ Checker->visitRecordDecl(RD);
+ return true;
+ }
+ };
+
+ LocalVisitor visitor(this);
+ visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+ }
+
+ void visitRecordDecl(const RecordDecl *RD) const {
+ if (shouldSkipDecl(RD))
+ return;
+
+ for (auto Member : RD->fields()) {
+ const Type *MemberType = Member->getType().getTypePtrOrNull();
+ if (!MemberType)
+ continue;
+
+ if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
+ // If we don't see the definition we just don't know.
+ if (MemberCXXRD->hasDefinition() && isRefCountable(MemberCXXRD))
+ reportBug(Member, MemberType, MemberCXXRD, RD);
+ }
+ }
+ }
+
+ bool shouldSkipDecl(const RecordDecl *RD) const {
+ if (!RD->isThisDeclarationADefinition())
+ return true;
+
+ if (RD->isImplicit())
+ return true;
+
+ if (RD->isLambda())
+ return true;
+
+ // If the construct doesn't have a source file, then it's not something
+ // we want to diagnose.
+ const auto RDLocation = RD->getLocation();
+ if (!RDLocation.isValid())
+ return true;
+
+ const auto Kind = RD->getTagKind();
+ // FIMXE: Should we check union members too?
+ if (Kind != TTK_Struct && Kind != TTK_Class)
+ return true;
+
+ // Ignore CXXRecords that come from system headers.
+ if (BR->getSourceManager().isInSystemHeader(RDLocation))
+ return true;
+
+ // Ref-counted smartpointers actually have raw-pointer to uncounted type as
+ // a member but we trust them to handle it correctly.
+ auto CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
+ if (CXXRD)
+ return isRefCounted(CXXRD);
+
+ return false;
+ }
+
+ void reportBug(const FieldDecl *Member, const Type *MemberType,
+ const CXXRecordDecl *MemberCXXRD,
+ const RecordDecl *ClassCXXRD) const {
+ assert(Member);
+ assert(MemberType);
+ assert(MemberCXXRD);
+
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+
+ Os << "Member variable ";
+ printQuotedName(Os, Member);
+ Os << " in ";
+ printQuotedQualifiedName(Os, ClassCXXRD);
+ Os << " is a "
+ << (isa<PointerType>(MemberType) ? "raw pointer" : "reference")
+ << " to ref-countable type ";
+ printQuotedQualifiedName(Os, MemberCXXRD);
+ Os << "; member variables must be ref-counted.";
+
+ PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(),
+ BR->getSourceManager());
+ auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+ Report->addRange(Member->getSourceRange());
+ BR->emitReport(std::move(Report));
+ }
+};
+} // namespace
+
+void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<NoUncountedMemberChecker>();
+}
+
+bool ento::shouldRegisterNoUncountedMemberChecker(
+ const CheckerManager &Mgr) {
+ return true;
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
new file mode 100644
index 000000000000..168cfd511170
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -0,0 +1,172 @@
+//=======- PtrTypesSemantics.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "PtrTypesSemantics.h"
+#include "ASTUtils.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+
+using llvm::Optional;
+using namespace clang;
+
+namespace {
+
+bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
+ assert(R);
+
+ bool hasRef = false;
+ bool hasDeref = false;
+ for (const CXXMethodDecl *MD : R->methods()) {
+ const auto MethodName = safeGetName(MD);
+
+ if (MethodName == "ref" && MD->getAccess() == AS_public) {
+ if (hasDeref)
+ return true;
+ hasRef = true;
+ } else if (MethodName == "deref" && MD->getAccess() == AS_public) {
+ if (hasRef)
+ return true;
+ hasDeref = true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+namespace clang {
+
+const CXXRecordDecl *isRefCountable(const CXXBaseSpecifier *Base) {
+ assert(Base);
+
+ const Type *T = Base->getType().getTypePtrOrNull();
+ if (!T)
+ return nullptr;
+
+ const CXXRecordDecl *R = T->getAsCXXRecordDecl();
+ if (!R)
+ return nullptr;
+
+ return hasPublicRefAndDeref(R) ? R : nullptr;
+}
+
+bool isRefCountable(const CXXRecordDecl *R) {
+ assert(R);
+
+ R = R->getDefinition();
+ assert(R);
+
+ if (hasPublicRefAndDeref(R))
+ return true;
+
+ CXXBasePaths Paths;
+ Paths.setOrigin(const_cast<CXXRecordDecl *>(R));
+
+ const auto isRefCountableBase = [](const CXXBaseSpecifier *Base,
+ CXXBasePath &) {
+ return clang::isRefCountable(Base);
+ };
+
+ return R->lookupInBases(isRefCountableBase, Paths,
+ /*LookupInDependent =*/true);
+}
+
+bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
+ assert(F);
+ const auto &FunctionName = safeGetName(F);
+
+ return FunctionName == "Ref" || FunctionName == "makeRef"
+
+ || FunctionName == "RefPtr" || FunctionName == "makeRefPtr"
+
+ || FunctionName == "UniqueRef" || FunctionName == "makeUniqueRef" ||
+ FunctionName == "makeUniqueRefWithoutFastMallocCheck"
+
+ || FunctionName == "String" || FunctionName == "AtomString" ||
+ FunctionName == "UniqueString"
+ // FIXME: Implement as attribute.
+ || FunctionName == "Identifier";
+}
+
+bool isUncounted(const CXXRecordDecl *Class) {
+ // Keep isRefCounted first as it's cheaper.
+ return !isRefCounted(Class) && isRefCountable(Class);
+}
+
+bool isUncountedPtr(const Type *T) {
+ assert(T);
+
+ if (T->isPointerType() || T->isReferenceType()) {
+ if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
+ return isUncounted(CXXRD);
+ }
+ }
+ return false;
+}
+
+bool isGetterOfRefCounted(const CXXMethodDecl *M) {
+ assert(M);
+
+ if (isa<CXXMethodDecl>(M)) {
+ const CXXRecordDecl *calleeMethodsClass = M->getParent();
+ auto className = safeGetName(calleeMethodsClass);
+ auto methodName = safeGetName(M);
+
+ if (((className == "Ref" || className == "RefPtr") &&
+ methodName == "get") ||
+ ((className == "String" || className == "AtomString" ||
+ className == "AtomStringImpl" || className == "UniqueString" ||
+ className == "UniqueStringImpl" || className == "Identifier") &&
+ methodName == "impl"))
+ return true;
+
+ // Ref<T> -> T conversion
+ // FIXME: Currently allowing any Ref<T> -> whatever cast.
+ if (className == "Ref" || className == "RefPtr") {
+ if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M)) {
+ if (auto *targetConversionType =
+ maybeRefToRawOperator->getConversionType().getTypePtrOrNull()) {
+ if (isUncountedPtr(targetConversionType)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool isRefCounted(const CXXRecordDecl *R) {
+ assert(R);
+ if (auto *TmplR = R->getTemplateInstantiationPattern()) {
+ // FIXME: String/AtomString/UniqueString
+ const auto &ClassName = safeGetName(TmplR);
+ return ClassName == "RefPtr" || ClassName == "Ref";
+ }
+ return false;
+}
+
+bool isPtrConversion(const FunctionDecl *F) {
+ assert(F);
+ if (isCtorOfRefCounted(F))
+ return true;
+
+ // FIXME: check # of params == 1
+ const auto FunctionName = safeGetName(F);
+ if (FunctionName == "getPtr" || FunctionName == "WeakPtr" ||
+ FunctionName == "makeWeakPtr"
+
+ || FunctionName == "downcast" || FunctionName == "bitwise_cast")
+ return true;
+
+ return false;
+}
+
+} // namespace clang
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
new file mode 100644
index 000000000000..83d9c0bcc13b
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -0,0 +1,59 @@
+//=======- PtrTypesSemantics.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H
+#define LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H
+
+namespace clang {
+class CXXBaseSpecifier;
+class CXXMethodDecl;
+class CXXRecordDecl;
+class Expr;
+class FunctionDecl;
+class Type;
+
+// Ref-countability of a type is implicitly defined by Ref<T> and RefPtr<T>
+// implementation. It can be modeled as: type T having public methods ref() and
+// deref()
+
+// In WebKit there are two ref-counted templated smart pointers: RefPtr<T> and
+// Ref<T>.
+
+/// \returns CXXRecordDecl of the base if the type is ref-countable, nullptr if
+/// not.
+const clang::CXXRecordDecl *isRefCountable(const clang::CXXBaseSpecifier *Base);
+
+/// \returns true if \p Class is ref-countable, false if not.
+/// Asserts that \p Class IS a definition.
+bool isRefCountable(const clang::CXXRecordDecl *Class);
+
+/// \returns true if \p Class is ref-counted, false if not.
+bool isRefCounted(const clang::CXXRecordDecl *Class);
+
+/// \returns true if \p Class is ref-countable AND not ref-counted, false if
+/// not. Asserts that \p Class IS a definition.
+bool isUncounted(const clang::CXXRecordDecl *Class);
+
+/// \returns true if \p T is either a raw pointer or reference to an uncounted
+/// class, false if not.
+bool isUncountedPtr(const clang::Type *T);
+
+/// \returns true if \p F creates ref-countable object from uncounted parameter,
+/// false if not.
+bool isCtorOfRefCounted(const clang::FunctionDecl *F);
+
+/// \returns true if \p M is getter of a ref-counted class, false if not.
+bool isGetterOfRefCounted(const clang::CXXMethodDecl *Method);
+
+/// \returns true if \p F is a conversion between ref-countable or ref-counted
+/// pointer types.
+bool isPtrConversion(const FunctionDecl *F);
+
+} // namespace clang
+
+#endif
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
new file mode 100644
index 000000000000..81ce284c2dc7
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
@@ -0,0 +1,167 @@
+//=======- RefCntblBaseVirtualDtor.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "DiagOutputUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class RefCntblBaseVirtualDtorChecker
+ : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+private:
+ BugType Bug;
+ mutable BugReporter *BR;
+
+public:
+ RefCntblBaseVirtualDtorChecker()
+ : Bug(this,
+ "Reference-countable base class doesn't have virtual destructor",
+ "WebKit coding guidelines") {}
+
+ void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+ BugReporter &BRArg) const {
+ BR = &BRArg;
+
+ // The calls to checkAST* from AnalysisConsumer don't
+ // visit template instantiations or lambda classes. We
+ // want to visit those, so we make our own RecursiveASTVisitor.
+ struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+ const RefCntblBaseVirtualDtorChecker *Checker;
+ explicit LocalVisitor(const RefCntblBaseVirtualDtorChecker *Checker)
+ : Checker(Checker) {
+ assert(Checker);
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return false; }
+
+ bool VisitCXXRecordDecl(const CXXRecordDecl *RD) {
+ Checker->visitCXXRecordDecl(RD);
+ return true;
+ }
+ };
+
+ LocalVisitor visitor(this);
+ visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+ }
+
+ void visitCXXRecordDecl(const CXXRecordDecl *RD) const {
+ if (shouldSkipDecl(RD))
+ return;
+
+ CXXBasePaths Paths;
+ Paths.setOrigin(RD);
+
+ const CXXBaseSpecifier *ProblematicBaseSpecifier = nullptr;
+ const CXXRecordDecl *ProblematicBaseClass = nullptr;
+
+ const auto IsPublicBaseRefCntblWOVirtualDtor =
+ [RD, &ProblematicBaseSpecifier,
+ &ProblematicBaseClass](const CXXBaseSpecifier *Base, CXXBasePath &) {
+ const auto AccSpec = Base->getAccessSpecifier();
+ if (AccSpec == AS_protected || AccSpec == AS_private ||
+ (AccSpec == AS_none && RD->isClass()))
+ return false;
+
+ llvm::Optional<const clang::CXXRecordDecl *> MaybeRefCntblBaseRD =
+ isRefCountable(Base);
+ if (!MaybeRefCntblBaseRD.hasValue())
+ return false;
+
+ const CXXRecordDecl *RefCntblBaseRD = MaybeRefCntblBaseRD.getValue();
+ if (!RefCntblBaseRD)
+ return false;
+
+ const auto *Dtor = RefCntblBaseRD->getDestructor();
+ if (!Dtor || !Dtor->isVirtual()) {
+ ProblematicBaseSpecifier = Base;
+ ProblematicBaseClass = RefCntblBaseRD;
+ return true;
+ }
+
+ return false;
+ };
+
+ if (RD->lookupInBases(IsPublicBaseRefCntblWOVirtualDtor, Paths,
+ /*LookupInDependent =*/true)) {
+ reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
+ }
+ }
+
+ bool shouldSkipDecl(const CXXRecordDecl *RD) const {
+ if (!RD->isThisDeclarationADefinition())
+ return true;
+
+ if (RD->isImplicit())
+ return true;
+
+ if (RD->isLambda())
+ return true;
+
+ // If the construct doesn't have a source file, then it's not something
+ // we want to diagnose.
+ const auto RDLocation = RD->getLocation();
+ if (!RDLocation.isValid())
+ return true;
+
+ const auto Kind = RD->getTagKind();
+ if (Kind != TTK_Struct && Kind != TTK_Class)
+ return true;
+
+ // Ignore CXXRecords that come from system headers.
+ if (BR->getSourceManager().getFileCharacteristic(RDLocation) !=
+ SrcMgr::C_User)
+ return true;
+
+ return false;
+ }
+
+ void reportBug(const CXXRecordDecl *DerivedClass,
+ const CXXBaseSpecifier *BaseSpec,
+ const CXXRecordDecl *ProblematicBaseClass) const {
+ assert(DerivedClass);
+ assert(BaseSpec);
+ assert(ProblematicBaseClass);
+
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+
+ Os << (ProblematicBaseClass->isClass() ? "Class" : "Struct") << " ";
+ printQuotedQualifiedName(Os, ProblematicBaseClass);
+
+ Os << " is used as a base of "
+ << (DerivedClass->isClass() ? "class" : "struct") << " ";
+ printQuotedQualifiedName(Os, DerivedClass);
+
+ Os << " but doesn't have virtual destructor";
+
+ PathDiagnosticLocation BSLoc(BaseSpec->getSourceRange().getBegin(),
+ BR->getSourceManager());
+ auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+ Report->addRange(BaseSpec->getSourceRange());
+ BR->emitReport(std::move(Report));
+ }
+};
+} // namespace
+
+void ento::registerRefCntblBaseVirtualDtorChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<RefCntblBaseVirtualDtorChecker>();
+}
+
+bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker(
+ const CheckerManager &mgr) {
+ return true;
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
new file mode 100644
index 000000000000..940a1f349831
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -0,0 +1,195 @@
+//=======- UncountedCallArgsChecker.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTUtils.h"
+#include "DiagOutputUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "llvm/ADT/DenseSet.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class UncountedCallArgsChecker
+ : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+ BugType Bug{this,
+ "Uncounted call argument for a raw pointer/reference parameter",
+ "WebKit coding guidelines"};
+ mutable BugReporter *BR;
+
+public:
+
+ void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+ BugReporter &BRArg) const {
+ BR = &BRArg;
+
+ // The calls to checkAST* from AnalysisConsumer don't
+ // visit template instantiations or lambda classes. We
+ // want to visit those, so we make our own RecursiveASTVisitor.
+ struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+ const UncountedCallArgsChecker *Checker;
+ explicit LocalVisitor(const UncountedCallArgsChecker *Checker)
+ : Checker(Checker) {
+ assert(Checker);
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return false; }
+
+ bool VisitCallExpr(const CallExpr *CE) {
+ Checker->visitCallExpr(CE);
+ return true;
+ }
+ };
+
+ LocalVisitor visitor(this);
+ visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+ }
+
+ void visitCallExpr(const CallExpr *CE) const {
+ if (shouldSkipCall(CE))
+ return;
+
+ if (auto *F = CE->getDirectCallee()) {
+ // Skip the first argument for overloaded member operators (e. g. lambda
+ // or std::function call operator).
+ unsigned ArgIdx =
+ isa<CXXOperatorCallExpr>(CE) && dyn_cast_or_null<CXXMethodDecl>(F);
+
+ for (auto P = F->param_begin();
+ // FIXME: Also check variadic function parameters.
+ // FIXME: Also check default function arguments. Probably a different
+ // checker. In case there are default arguments the call can have
+ // fewer arguments than the callee has parameters.
+ P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
+ // TODO: attributes.
+ // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
+ // continue;
+
+ const auto *ArgType = (*P)->getType().getTypePtrOrNull();
+ if (!ArgType)
+ continue; // FIXME? Should we bail?
+
+ // FIXME: more complex types (arrays, references to raw pointers, etc)
+ if (!isUncountedPtr(ArgType))
+ continue;
+
+ const auto *Arg = CE->getArg(ArgIdx);
+
+ std::pair<const clang::Expr *, bool> ArgOrigin =
+ tryToFindPtrOrigin(Arg, true);
+
+ // Temporary ref-counted object created as part of the call argument
+ // would outlive the call.
+ if (ArgOrigin.second)
+ continue;
+
+ if (isa<CXXNullPtrLiteralExpr>(ArgOrigin.first)) {
+ // foo(nullptr)
+ continue;
+ }
+ if (isa<IntegerLiteral>(ArgOrigin.first)) {
+ // FIXME: Check the value.
+ // foo(NULL)
+ continue;
+ }
+
+ if (isASafeCallArg(ArgOrigin.first))
+ continue;
+
+ reportBug(Arg, *P);
+ }
+ }
+ }
+
+ bool shouldSkipCall(const CallExpr *CE) const {
+ if (CE->getNumArgs() == 0)
+ return false;
+
+ // If an assignment is problematic we should warn about the sole existence
+ // of object on LHS.
+ if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
+ // Note: assignemnt to built-in type isn't derived from CallExpr.
+ if (MemberOp->isAssignmentOp())
+ return false;
+ }
+
+ const auto *Callee = CE->getDirectCallee();
+ if (!Callee)
+ return false;
+
+ auto overloadedOperatorType = Callee->getOverloadedOperator();
+ if (overloadedOperatorType == OO_EqualEqual ||
+ overloadedOperatorType == OO_ExclaimEqual ||
+ overloadedOperatorType == OO_LessEqual ||
+ overloadedOperatorType == OO_GreaterEqual ||
+ overloadedOperatorType == OO_Spaceship ||
+ overloadedOperatorType == OO_AmpAmp ||
+ overloadedOperatorType == OO_PipePipe)
+ return true;
+
+ if (isCtorOfRefCounted(Callee))
+ return true;
+
+ auto name = safeGetName(Callee);
+ if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
+ name == "makeWeakPtr" || name == "downcast" || name == "bitwise_cast" ||
+ name == "is" || name == "equal" || name == "hash" ||
+ name == "isType"
+ // FIXME: Most/all of these should be implemented via attributes.
+ || name == "equalIgnoringASCIICase" ||
+ name == "equalIgnoringASCIICaseCommon" ||
+ name == "equalIgnoringNullity")
+ return true;
+
+ return false;
+ }
+
+ void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const {
+ assert(CallArg);
+
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+
+ const std::string paramName = safeGetName(Param);
+ Os << "Call argument";
+ if (!paramName.empty()) {
+ Os << " for parameter ";
+ printQuotedQualifiedName(Os, Param);
+ }
+ Os << " is uncounted and unsafe.";
+
+ const SourceLocation SrcLocToReport =
+ isa<CXXDefaultArgExpr>(CallArg) ? Param->getDefaultArg()->getExprLoc()
+ : CallArg->getSourceRange().getBegin();
+
+ PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+ auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+ Report->addRange(CallArg->getSourceRange());
+ BR->emitReport(std::move(Report));
+ }
+};
+} // namespace
+
+void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<UncountedCallArgsChecker>();
+}
+
+bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
+ return true;
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h
index 968c50e33f6d..ec612dde3b8b 100755
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h
@@ -15,6 +15,7 @@
#define LLVM_CLANG_LIB_STATICANALYZER_CHECKER_YAML_H
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/YAMLTraits.h"
namespace clang {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp
new file mode 100644
index 000000000000..1c67bbd77ec8
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp
@@ -0,0 +1,66 @@
+//== PutenvWithAutoChecker.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 PutenvWithAutoChecker which finds calls of ``putenv``
+// function with automatic variable as the argument.
+// https://wiki.sei.cmu.edu/confluence/x/6NYxBQ
+//
+//===----------------------------------------------------------------------===//
+
+#include "../AllocationState.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class PutenvWithAutoChecker : public Checker<check::PostCall> {
+private:
+ BugType BT{this, "'putenv' function should not be called with auto variables",
+ categories::SecurityError};
+ const CallDescription Putenv{"putenv", 1};
+
+public:
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+};
+} // namespace
+
+void PutenvWithAutoChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (!Call.isCalled(Putenv))
+ return;
+
+ SVal ArgV = Call.getArgSVal(0);
+ const Expr *ArgExpr = Call.getArgExpr(0);
+ const MemSpaceRegion *MSR = ArgV.getAsRegion()->getMemorySpace();
+
+ if (!isa<StackSpaceRegion>(MSR))
+ return;
+
+ StringRef ErrorMsg = "The 'putenv' function should not be called with "
+ "arguments that have automatic storage";
+ ExplodedNode *N = C.generateErrorNode();
+ auto Report = std::make_unique<PathSensitiveBugReport>(BT, ErrorMsg, N);
+
+ // Track the argument.
+ bugreporter::trackExpressionValue(Report->getErrorNode(), ArgExpr, *Report);
+
+ C.emitReport(std::move(Report));
+}
+
+void ento::registerPutenvWithAuto(CheckerManager &Mgr) {
+ Mgr.registerChecker<PutenvWithAutoChecker>();
+}
+
+bool ento::shouldRegisterPutenvWithAuto(const CheckerManager &) { return true; }
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp
index fdd03c75920d..ecfc7106560e 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp
@@ -13,7 +13,7 @@ using namespace ento;
void AnalysisManager::anchor() { }
-AnalysisManager::AnalysisManager(ASTContext &ASTCtx,
+AnalysisManager::AnalysisManager(ASTContext &ASTCtx, Preprocessor &PP,
const PathDiagnosticConsumers &PDC,
StoreManagerCreator storemgr,
ConstraintManagerCreator constraintmgr,
@@ -38,7 +38,7 @@ AnalysisManager::AnalysisManager(ASTContext &ASTCtx,
Options.ShouldElideConstructors,
/*addVirtualBaseBranches=*/true,
injector),
- Ctx(ASTCtx), LangOpts(ASTCtx.getLangOpts()),
+ Ctx(ASTCtx), PP(PP), LangOpts(ASTCtx.getLangOpts()),
PathConsumers(PDC), CreateStoreMgr(storemgr),
CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr),
options(Options) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
index 7cd48bf44374..73f057f09550 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -239,7 +239,7 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op,
if (Amt >= V1.getBitWidth())
return nullptr;
- if (!Ctx.getLangOpts().CPlusPlus2a) {
+ if (!Ctx.getLangOpts().CPlusPlus20) {
if (V1.isSigned() && V1.isNegative())
return nullptr;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
index 1864bcef9b87..72be4e81c83d 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -34,10 +34,12 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/CheckerRegistryData.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/ArrayRef.h"
@@ -51,6 +53,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Casting.h"
@@ -2105,6 +2108,53 @@ void BuiltinBug::anchor() {}
// Methods for BugReport and subclasses.
//===----------------------------------------------------------------------===//
+LLVM_ATTRIBUTE_USED static bool
+isDependency(const CheckerRegistryData &Registry, StringRef CheckerName) {
+ for (const std::pair<StringRef, StringRef> &Pair : Registry.Dependencies) {
+ if (Pair.second == CheckerName)
+ return true;
+ }
+ return false;
+}
+
+LLVM_ATTRIBUTE_USED static bool isHidden(const CheckerRegistryData &Registry,
+ StringRef CheckerName) {
+ for (const CheckerInfo &Checker : Registry.Checkers) {
+ if (Checker.FullName == CheckerName)
+ return Checker.IsHidden;
+ }
+ llvm_unreachable(
+ "Checker name not found in CheckerRegistry -- did you retrieve it "
+ "correctly from CheckerManager::getCurrentCheckerName?");
+}
+
+PathSensitiveBugReport::PathSensitiveBugReport(
+ const BugType &bt, StringRef shortDesc, StringRef desc,
+ const ExplodedNode *errorNode, PathDiagnosticLocation LocationToUnique,
+ const Decl *DeclToUnique)
+ : BugReport(Kind::PathSensitive, bt, shortDesc, desc), ErrorNode(errorNode),
+ ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() : SourceRange()),
+ UniqueingLocation(LocationToUnique), UniqueingDecl(DeclToUnique) {
+ assert(!isDependency(ErrorNode->getState()
+ ->getAnalysisManager()
+ .getCheckerManager()
+ ->getCheckerRegistryData(),
+ bt.getCheckerName()) &&
+ "Some checkers depend on this one! We don't allow dependency "
+ "checkers to emit warnings, because checkers should depend on "
+ "*modeling*, not *diagnostics*.");
+
+ assert(
+ (bt.getCheckerName().startswith("debug") ||
+ !isHidden(ErrorNode->getState()
+ ->getAnalysisManager()
+ .getCheckerManager()
+ ->getCheckerRegistryData(),
+ bt.getCheckerName())) &&
+ "Hidden checkers musn't emit diagnostics as they are by definition "
+ "non-user facing!");
+}
+
void PathSensitiveBugReport::addVisitor(
std::unique_ptr<BugReporterVisitor> visitor) {
if (!visitor)
@@ -2193,12 +2243,12 @@ static void insertToInterestingnessMap(
return;
case bugreporter::TrackingKind::Condition:
return;
- }
+ }
- llvm_unreachable(
- "BugReport::markInteresting currently can only handle 2 different "
- "tracking kinds! Please define what tracking kind should this entitiy"
- "have, if it was already marked as interesting with a different kind!");
+ llvm_unreachable(
+ "BugReport::markInteresting currently can only handle 2 different "
+ "tracking kinds! Please define what tracking kind should this entitiy"
+ "have, if it was already marked as interesting with a different kind!");
}
void PathSensitiveBugReport::markInteresting(SymbolRef sym,
@@ -2389,6 +2439,7 @@ ProgramStateManager &PathSensitiveBugReporter::getStateManager() const {
return Eng.getStateManager();
}
+BugReporter::BugReporter(BugReporterData &d) : D(d) {}
BugReporter::~BugReporter() {
// Make sure reports are flushed.
assert(StrBugTypes.empty() &&
@@ -2409,7 +2460,7 @@ void BugReporter::FlushReports() {
// EmitBasicReport.
// FIXME: There are leaks from checkers that assume that the BugTypes they
// create will be destroyed by the BugReporter.
- llvm::DeleteContainerSeconds(StrBugTypes);
+ StrBugTypes.clear();
}
//===----------------------------------------------------------------------===//
@@ -2781,7 +2832,7 @@ Optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport(
R->clearVisitors();
R->addVisitor(std::make_unique<FalsePositiveRefutationBRVisitor>());
- // We don't overrite the notes inserted by other visitors because the
+ // We don't overwrite the notes inserted by other visitors because the
// refutation manager does not add any new note to the path
generateVisitorsDiagnostics(R, BugPath->ErrorNode, BRC);
}
@@ -3262,8 +3313,8 @@ BugType *BugReporter::getBugTypeForName(CheckerNameRef CheckName,
SmallString<136> fullDesc;
llvm::raw_svector_ostream(fullDesc) << CheckName.getName() << ":" << name
<< ":" << category;
- BugType *&BT = StrBugTypes[fullDesc];
+ std::unique_ptr<BugType> &BT = StrBugTypes[fullDesc];
if (!BT)
- BT = new BugType(CheckName, name, category);
- return BT;
+ BT = std::make_unique<BugType>(CheckName, name, category);
+ return BT.get();
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 0525b5c41e34..ef4d38ff498f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -45,7 +45,6 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/Optional.h"
@@ -358,7 +357,7 @@ class NoStoreFuncVisitor final : public BugReporterVisitor {
public:
NoStoreFuncVisitor(const SubRegion *R, bugreporter::TrackingKind TKind)
- : RegionOfInterest(R), MmrMgr(*R->getMemRegionManager()),
+ : RegionOfInterest(R), MmrMgr(R->getMemRegionManager()),
SM(MmrMgr.getContext().getSourceManager()),
PP(MmrMgr.getContext().getPrintingPolicy()), TKind(TKind) {}
@@ -813,7 +812,7 @@ public:
const SourceManager &SMgr = BRC.getSourceManager();
if (auto Loc = matchAssignment(N)) {
if (isFunctionMacroExpansion(*Loc, SMgr)) {
- std::string MacroName = getMacroName(*Loc, BRC);
+ std::string MacroName = std::string(getMacroName(*Loc, BRC));
SourceLocation BugLoc = BugPoint->getStmt()->getBeginLoc();
if (!BugLoc.isMacroID() || getMacroName(BugLoc, BRC) != MacroName)
BR.markInvalid(getTag(), MacroName.c_str());
@@ -1735,10 +1734,9 @@ constructDebugPieceForTrackedCondition(const Expr *Cond,
!BRC.getAnalyzerOptions().ShouldTrackConditionsDebug)
return nullptr;
- std::string ConditionText = Lexer::getSourceText(
+ std::string ConditionText = std::string(Lexer::getSourceText(
CharSourceRange::getTokenRange(Cond->getSourceRange()),
- BRC.getSourceManager(),
- BRC.getASTContext().getLangOpts());
+ BRC.getSourceManager(), BRC.getASTContext().getLangOpts()));
return std::make_shared<PathDiagnosticEventPiece>(
PathDiagnosticLocation::createBegin(
@@ -2494,7 +2492,7 @@ PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(
Out << WillBeUsedForACondition;
// Convert 'field ...' to 'Field ...' if it is a MemberExpr.
- std::string Message = Out.str();
+ std::string Message = std::string(Out.str());
Message[0] = toupper(Message[0]);
// If we know the value create a pop-up note to the value part of 'BExpr'.
@@ -2821,7 +2819,7 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor(
BugReporterContext &BRC, const ExplodedNode *EndPathNode,
PathSensitiveBugReport &BR) {
// Collect new constraints
- VisitNode(EndPathNode, BRC, BR);
+ addConstraints(EndPathNode, /*OverwriteConstraintsOnExistingSyms=*/true);
// Create a refutation manager
llvm::SMTSolverRef RefutationSolver = llvm::CreateZ3Solver();
@@ -2832,30 +2830,30 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor(
const SymbolRef Sym = I.first;
auto RangeIt = I.second.begin();
- llvm::SMTExprRef Constraints = SMTConv::getRangeExpr(
+ llvm::SMTExprRef SMTConstraints = SMTConv::getRangeExpr(
RefutationSolver, Ctx, Sym, RangeIt->From(), RangeIt->To(),
/*InRange=*/true);
while ((++RangeIt) != I.second.end()) {
- Constraints = RefutationSolver->mkOr(
- Constraints, SMTConv::getRangeExpr(RefutationSolver, Ctx, Sym,
- RangeIt->From(), RangeIt->To(),
- /*InRange=*/true));
+ SMTConstraints = RefutationSolver->mkOr(
+ SMTConstraints, SMTConv::getRangeExpr(RefutationSolver, Ctx, Sym,
+ RangeIt->From(), RangeIt->To(),
+ /*InRange=*/true));
}
- RefutationSolver->addConstraint(Constraints);
+ RefutationSolver->addConstraint(SMTConstraints);
}
// And check for satisfiability
- Optional<bool> isSat = RefutationSolver->check();
- if (!isSat.hasValue())
+ Optional<bool> IsSAT = RefutationSolver->check();
+ if (!IsSAT.hasValue())
return;
- if (!isSat.getValue())
+ if (!IsSAT.getValue())
BR.markInvalid("Infeasible constraints", EndPathNode->getLocationContext());
}
-PathDiagnosticPieceRef FalsePositiveRefutationBRVisitor::VisitNode(
- const ExplodedNode *N, BugReporterContext &, PathSensitiveBugReport &) {
+void FalsePositiveRefutationBRVisitor::addConstraints(
+ const ExplodedNode *N, bool OverwriteConstraintsOnExistingSyms) {
// Collect new constraints
const ConstraintRangeTy &NewCs = N->getState()->get<ConstraintRange>();
ConstraintRangeTy::Factory &CF =
@@ -2865,10 +2863,19 @@ PathDiagnosticPieceRef FalsePositiveRefutationBRVisitor::VisitNode(
for (auto const &C : NewCs) {
const SymbolRef &Sym = C.first;
if (!Constraints.contains(Sym)) {
+ // This symbol is new, just add the constraint.
+ Constraints = CF.add(Constraints, Sym, C.second);
+ } else if (OverwriteConstraintsOnExistingSyms) {
+ // Overwrite the associated constraint of the Symbol.
+ Constraints = CF.remove(Constraints, Sym);
Constraints = CF.add(Constraints, Sym, C.second);
}
}
+}
+PathDiagnosticPieceRef FalsePositiveRefutationBRVisitor::VisitNode(
+ const ExplodedNode *N, BugReporterContext &, PathSensitiveBugReport &) {
+ addConstraints(N, /*OverwriteConstraintsOnExistingSyms=*/false);
return nullptr;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 168d6fe6ec48..78d13ddfb773 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -172,23 +172,9 @@ AnalysisDeclContext *CallEvent::getCalleeAnalysisDeclContext() const {
if (!D)
return nullptr;
- // TODO: For now we skip functions without definitions, even if we have
- // our own getDecl(), because it's hard to find out which re-declaration
- // is going to be used, and usually clients don't really care about this
- // situation because there's a loss of precision anyway because we cannot
- // inline the call.
- RuntimeDefinition RD = getRuntimeDefinition();
- if (!RD.getDecl())
- return nullptr;
-
AnalysisDeclContext *ADC =
LCtx->getAnalysisDeclContext()->getManager()->getContext(D);
- // TODO: For now we skip virtual functions, because this also rises
- // the problem of which decl to use, but now it's across different classes.
- if (RD.mayHaveOtherDefinitions() || RD.getDecl() != ADC->getDecl())
- return nullptr;
-
return ADC;
}
@@ -222,39 +208,17 @@ CallEvent::getCalleeStackFrame(unsigned BlockCount) const {
return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, BlockCount, Idx);
}
-const VarRegion *CallEvent::getParameterLocation(unsigned Index,
- unsigned BlockCount) const {
+const ParamVarRegion
+*CallEvent::getParameterLocation(unsigned Index, unsigned BlockCount) const {
const StackFrameContext *SFC = getCalleeStackFrame(BlockCount);
// We cannot construct a VarRegion without a stack frame.
if (!SFC)
return nullptr;
- // Retrieve parameters of the definition, which are different from
- // CallEvent's parameters() because getDecl() isn't necessarily
- // the definition. SFC contains the definition that would be used
- // during analysis.
- const Decl *D = SFC->getDecl();
-
- // TODO: Refactor into a virtual method of CallEvent, like parameters().
- const ParmVarDecl *PVD = nullptr;
- if (const auto *FD = dyn_cast<FunctionDecl>(D))
- PVD = FD->parameters()[Index];
- else if (const auto *BD = dyn_cast<BlockDecl>(D))
- PVD = BD->parameters()[Index];
- else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
- PVD = MD->parameters()[Index];
- else if (const auto *CD = dyn_cast<CXXConstructorDecl>(D))
- PVD = CD->parameters()[Index];
- assert(PVD && "Unexpected Decl kind!");
-
- const VarRegion *VR =
- State->getStateManager().getRegionManager().getVarRegion(PVD, SFC);
-
- // This sanity check would fail if our parameter declaration doesn't
- // correspond to the stack frame's function declaration.
- assert(VR->getStackFrame() == SFC);
-
- return VR;
+ const ParamVarRegion *PVR =
+ State->getStateManager().getRegionManager().getParamVarRegion(
+ getOriginExpr(), Index, SFC);
+ return PVR;
}
/// Returns true if a type is a pointer-to-const or reference-to-const
@@ -325,8 +289,9 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
if (getKind() != CE_CXXAllocator)
if (isArgumentConstructedDirectly(Idx))
if (auto AdjIdx = getAdjustedParameterIndex(Idx))
- if (const VarRegion *VR = getParameterLocation(*AdjIdx, BlockCount))
- ValuesToInvalidate.push_back(loc::MemRegionVal(VR));
+ if (const TypedValueRegion *TVR =
+ getParameterLocation(*AdjIdx, BlockCount))
+ ValuesToInvalidate.push_back(loc::MemRegionVal(TVR));
}
// Invalidate designated regions using the batch invalidation API.
@@ -450,8 +415,7 @@ void CallEvent::dump(raw_ostream &Out) const {
return;
}
- // FIXME: a string representation of the kind would be nice.
- Out << "Unknown call (type " << getKind() << ")";
+ Out << "Unknown call (type " << getKindAsString() << ")";
}
bool CallEvent::isCallStmt(const Stmt *S) {
@@ -515,8 +479,7 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
unsigned Idx = 0;
ArrayRef<ParmVarDecl*>::iterator I = parameters.begin(), E = parameters.end();
for (; I != E && Idx < NumArgs; ++I, ++Idx) {
- const ParmVarDecl *ParamDecl = *I;
- assert(ParamDecl && "Formal parameter has no decl?");
+ assert(*I && "Formal parameter has no decl?");
// TODO: Support allocator calls.
if (Call.getKind() != CE_CXXAllocator)
@@ -528,7 +491,8 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
// which makes getArgSVal() fail and return UnknownVal.
SVal ArgVal = Call.getArgSVal(Idx);
if (!ArgVal.isUnknown()) {
- Loc ParamLoc = SVB.makeLoc(MRMgr.getVarRegion(ParamDecl, CalleeCtx));
+ Loc ParamLoc = SVB.makeLoc(
+ MRMgr.getParamVarRegion(Call.getOriginExpr(), Idx, CalleeCtx));
Bindings.push_back(std::make_pair(ParamLoc, ArgVal));
}
}
@@ -536,6 +500,37 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
// FIXME: Variadic arguments are not handled at all right now.
}
+const ConstructionContext *CallEvent::getConstructionContext() const {
+ const StackFrameContext *StackFrame = getCalleeStackFrame(0);
+ if (!StackFrame)
+ return nullptr;
+
+ const CFGElement Element = StackFrame->getCallSiteCFGElement();
+ if (const auto Ctor = Element.getAs<CFGConstructor>()) {
+ return Ctor->getConstructionContext();
+ }
+
+ if (const auto RecCall = Element.getAs<CFGCXXRecordTypedCall>()) {
+ return RecCall->getConstructionContext();
+ }
+
+ return nullptr;
+}
+
+Optional<SVal>
+CallEvent::getReturnValueUnderConstruction() const {
+ const auto *CC = getConstructionContext();
+ if (!CC)
+ return None;
+
+ EvalCallOptions CallOpts;
+ ExprEngine &Engine = getState()->getStateManager().getOwningEngine();
+ SVal RetVal =
+ Engine.computeObjectUnderConstruction(getOriginExpr(), getState(),
+ getLocationContext(), CC, CallOpts);
+ return RetVal;
+}
+
ArrayRef<ParmVarDecl*> AnyFunctionCall::parameters() const {
const FunctionDecl *D = getDecl();
if (!D)
@@ -565,7 +560,7 @@ RuntimeDefinition AnyFunctionCall::getRuntimeDefinition() const {
return RuntimeDefinition(Decl);
}
- SubEngine &Engine = getState()->getStateManager().getOwningEngine();
+ ExprEngine &Engine = getState()->getStateManager().getOwningEngine();
AnalyzerOptions &Opts = Engine.getAnalysisManager().options;
// Try to get CTU definition only if CTUDir is provided.
@@ -889,24 +884,22 @@ void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
Params);
}
-SVal CXXConstructorCall::getCXXThisVal() const {
+SVal AnyCXXConstructorCall::getCXXThisVal() const {
if (Data)
return loc::MemRegionVal(static_cast<const MemRegion *>(Data));
return UnknownVal();
}
-void CXXConstructorCall::getExtraInvalidatedValues(ValueList &Values,
+void AnyCXXConstructorCall::getExtraInvalidatedValues(ValueList &Values,
RegionAndSymbolInvalidationTraits *ETraits) const {
- if (Data) {
- loc::MemRegionVal MV(static_cast<const MemRegion *>(Data));
- if (SymbolRef Sym = MV.getAsSymbol(true))
- ETraits->setTrait(Sym,
- RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
- Values.push_back(MV);
- }
+ SVal V = getCXXThisVal();
+ if (SymbolRef Sym = V.getAsSymbol(true))
+ ETraits->setTrait(Sym,
+ RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
+ Values.push_back(V);
}
-void CXXConstructorCall::getInitialStackFrameContents(
+void AnyCXXConstructorCall::getInitialStackFrameContents(
const StackFrameContext *CalleeCtx,
BindingsTy &Bindings) const {
AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings);
@@ -920,6 +913,14 @@ void CXXConstructorCall::getInitialStackFrameContents(
}
}
+const StackFrameContext *
+CXXInheritedConstructorCall::getInheritingStackFrame() const {
+ const StackFrameContext *SFC = getLocationContext()->getStackFrame();
+ while (isa<CXXInheritedCtorInitExpr>(SFC->getCallSite()))
+ SFC = SFC->getParent()->getStackFrame();
+ return SFC;
+}
+
SVal CXXDestructorCall::getCXXThisVal() const {
if (Data)
return loc::MemRegionVal(DtorDataTy::getFromOpaqueValue(Data).getPointer());
@@ -967,14 +968,6 @@ void ObjCMethodCall::getExtraInvalidatedValues(
Values.push_back(getReceiverSVal());
}
-SVal ObjCMethodCall::getSelfSVal() const {
- const LocationContext *LCtx = getLocationContext();
- const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
- if (!SelfDecl)
- return SVal();
- return getState()->getSVal(getState()->getRegion(SelfDecl, LCtx));
-}
-
SVal ObjCMethodCall::getReceiverSVal() const {
// FIXME: Is this the best way to handle class receivers?
if (!isInstanceMessage())
@@ -986,7 +979,7 @@ SVal ObjCMethodCall::getReceiverSVal() const {
// An instance message with no expression means we are sending to super.
// In this case the object reference is the same as 'self'.
assert(getOriginExpr()->getReceiverKind() == ObjCMessageExpr::SuperInstance);
- SVal SelfVal = getSelfSVal();
+ SVal SelfVal = getState()->getSelfSVal(getLocationContext());
assert(SelfVal.isValid() && "Calling super but not in ObjC method");
return SelfVal;
}
@@ -1000,8 +993,9 @@ bool ObjCMethodCall::isReceiverSelfOrSuper() const {
return false;
SVal RecVal = getSVal(getOriginExpr()->getInstanceReceiver());
+ SVal SelfVal = getState()->getSelfSVal(getLocationContext());
- return (RecVal == getSelfSVal());
+ return (RecVal == SelfVal);
}
SourceRange ObjCMethodCall::getSourceRange() const {
@@ -1168,23 +1162,77 @@ static const ObjCMethodDecl *findDefiningRedecl(const ObjCMethodDecl *MD) {
return MD;
}
-static bool isCallToSelfClass(const ObjCMessageExpr *ME) {
- const Expr* InstRec = ME->getInstanceReceiver();
- if (!InstRec)
- return false;
- const auto *InstRecIg = dyn_cast<DeclRefExpr>(InstRec->IgnoreParenImpCasts());
+struct PrivateMethodKey {
+ const ObjCInterfaceDecl *Interface;
+ Selector LookupSelector;
+ bool IsClassMethod;
+};
- // Check that receiver is called 'self'.
- if (!InstRecIg || !InstRecIg->getFoundDecl() ||
- !InstRecIg->getFoundDecl()->getName().equals("self"))
- return false;
+namespace llvm {
+template <> struct DenseMapInfo<PrivateMethodKey> {
+ using InterfaceInfo = DenseMapInfo<const ObjCInterfaceDecl *>;
+ using SelectorInfo = DenseMapInfo<Selector>;
- // Check that the method name is 'class'.
- if (ME->getSelector().getNumArgs() != 0 ||
- !ME->getSelector().getNameForSlot(0).equals("class"))
- return false;
+ static inline PrivateMethodKey getEmptyKey() {
+ return {InterfaceInfo::getEmptyKey(), SelectorInfo::getEmptyKey(), false};
+ }
- return true;
+ static inline PrivateMethodKey getTombstoneKey() {
+ return {InterfaceInfo::getTombstoneKey(), SelectorInfo::getTombstoneKey(),
+ true};
+ }
+
+ static unsigned getHashValue(const PrivateMethodKey &Key) {
+ return llvm::hash_combine(
+ llvm::hash_code(InterfaceInfo::getHashValue(Key.Interface)),
+ llvm::hash_code(SelectorInfo::getHashValue(Key.LookupSelector)),
+ Key.IsClassMethod);
+ }
+
+ static bool isEqual(const PrivateMethodKey &LHS,
+ const PrivateMethodKey &RHS) {
+ return InterfaceInfo::isEqual(LHS.Interface, RHS.Interface) &&
+ SelectorInfo::isEqual(LHS.LookupSelector, RHS.LookupSelector) &&
+ LHS.IsClassMethod == RHS.IsClassMethod;
+ }
+};
+} // end namespace llvm
+
+static const ObjCMethodDecl *
+lookupRuntimeDefinition(const ObjCInterfaceDecl *Interface,
+ Selector LookupSelector, bool InstanceMethod) {
+ // Repeatedly calling lookupPrivateMethod() is expensive, especially
+ // when in many cases it returns null. We cache the results so
+ // that repeated queries on the same ObjCIntefaceDecl and Selector
+ // don't incur the same cost. On some test cases, we can see the
+ // same query being issued thousands of times.
+ //
+ // NOTE: This cache is essentially a "global" variable, but it
+ // only gets lazily created when we get here. The value of the
+ // cache probably comes from it being global across ExprEngines,
+ // where the same queries may get issued. If we are worried about
+ // concurrency, or possibly loading/unloading ASTs, etc., we may
+ // need to revisit this someday. In terms of memory, this table
+ // stays around until clang quits, which also may be bad if we
+ // need to release memory.
+ using PrivateMethodCache =
+ llvm::DenseMap<PrivateMethodKey, Optional<const ObjCMethodDecl *>>;
+
+ static PrivateMethodCache PMC;
+ Optional<const ObjCMethodDecl *> &Val =
+ PMC[{Interface, LookupSelector, InstanceMethod}];
+
+ // Query lookupPrivateMethod() if the cache does not hit.
+ if (!Val.hasValue()) {
+ Val = Interface->lookupPrivateMethod(LookupSelector, InstanceMethod);
+
+ if (!*Val) {
+ // Query 'lookupMethod' as a backup.
+ Val = Interface->lookupMethod(LookupSelector, InstanceMethod);
+ }
+ }
+
+ return Val.getValue();
}
RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
@@ -1194,8 +1242,9 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
if (E->isInstanceMessage()) {
// Find the receiver type.
- const ObjCObjectPointerType *ReceiverT = nullptr;
+ const ObjCObjectType *ReceiverT = nullptr;
bool CanBeSubClassed = false;
+ bool LookingForInstanceMethod = true;
QualType SupersType = E->getSuperType();
const MemRegion *Receiver = nullptr;
@@ -1203,7 +1252,7 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
// The receiver is guaranteed to be 'super' in this case.
// Super always means the type of immediate predecessor to the method
// where the call occurs.
- ReceiverT = cast<ObjCObjectPointerType>(SupersType);
+ ReceiverT = cast<ObjCObjectPointerType>(SupersType)->getObjectType();
} else {
Receiver = getReceiverSVal().getAsRegion();
if (!Receiver)
@@ -1218,100 +1267,59 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
QualType DynType = DTI.getType();
CanBeSubClassed = DTI.canBeASubClass();
- ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType.getCanonicalType());
- if (ReceiverT && CanBeSubClassed)
- if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl())
- if (!canBeOverridenInSubclass(IDecl, Sel))
- CanBeSubClassed = false;
- }
+ const auto *ReceiverDynT =
+ dyn_cast<ObjCObjectPointerType>(DynType.getCanonicalType());
+
+ if (ReceiverDynT) {
+ ReceiverT = ReceiverDynT->getObjectType();
- // Handle special cases of '[self classMethod]' and
- // '[[self class] classMethod]', which are treated by the compiler as
- // instance (not class) messages. We will statically dispatch to those.
- if (auto *PT = dyn_cast_or_null<ObjCObjectPointerType>(ReceiverT)) {
- // For [self classMethod], return the compiler visible declaration.
- if (PT->getObjectType()->isObjCClass() &&
- Receiver == getSelfSVal().getAsRegion())
- return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl()));
-
- // Similarly, handle [[self class] classMethod].
- // TODO: We are currently doing a syntactic match for this pattern with is
- // limiting as the test cases in Analysis/inlining/InlineObjCClassMethod.m
- // shows. A better way would be to associate the meta type with the symbol
- // using the dynamic type info tracking and use it here. We can add a new
- // SVal for ObjC 'Class' values that know what interface declaration they
- // come from. Then 'self' in a class method would be filled in with
- // something meaningful in ObjCMethodCall::getReceiverSVal() and we could
- // do proper dynamic dispatch for class methods just like we do for
- // instance methods now.
- if (E->getInstanceReceiver())
- if (const auto *M = dyn_cast<ObjCMessageExpr>(E->getInstanceReceiver()))
- if (isCallToSelfClass(M))
+ // It can be actually class methods called with Class object as a
+ // receiver. This type of messages is treated by the compiler as
+ // instance (not class).
+ if (ReceiverT->isObjCClass()) {
+
+ SVal SelfVal = getState()->getSelfSVal(getLocationContext());
+ // For [self classMethod], return compiler visible declaration.
+ if (Receiver == SelfVal.getAsRegion()) {
return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl()));
+ }
+
+ // Otherwise, let's check if we know something about the type
+ // inside of this class object.
+ if (SymbolRef ReceiverSym = getReceiverSVal().getAsSymbol()) {
+ DynamicTypeInfo DTI =
+ getClassObjectDynamicTypeInfo(getState(), ReceiverSym);
+ if (DTI.isValid()) {
+ // Let's use this type for lookup.
+ ReceiverT =
+ cast<ObjCObjectType>(DTI.getType().getCanonicalType());
+
+ CanBeSubClassed = DTI.canBeASubClass();
+ // And it should be a class method instead.
+ LookingForInstanceMethod = false;
+ }
+ }
+ }
+
+ if (CanBeSubClassed)
+ if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterface())
+ // Even if `DynamicTypeInfo` told us that it can be
+ // not necessarily this type, but its descendants, we still want
+ // to check again if this selector can be actually overridden.
+ CanBeSubClassed = canBeOverridenInSubclass(IDecl, Sel);
+ }
}
// Lookup the instance method implementation.
if (ReceiverT)
- if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) {
- // Repeatedly calling lookupPrivateMethod() is expensive, especially
- // when in many cases it returns null. We cache the results so
- // that repeated queries on the same ObjCIntefaceDecl and Selector
- // don't incur the same cost. On some test cases, we can see the
- // same query being issued thousands of times.
- //
- // NOTE: This cache is essentially a "global" variable, but it
- // only gets lazily created when we get here. The value of the
- // cache probably comes from it being global across ExprEngines,
- // where the same queries may get issued. If we are worried about
- // concurrency, or possibly loading/unloading ASTs, etc., we may
- // need to revisit this someday. In terms of memory, this table
- // stays around until clang quits, which also may be bad if we
- // need to release memory.
- using PrivateMethodKey = std::pair<const ObjCInterfaceDecl *, Selector>;
- using PrivateMethodCache =
- llvm::DenseMap<PrivateMethodKey, Optional<const ObjCMethodDecl *>>;
-
- static PrivateMethodCache PMC;
- Optional<const ObjCMethodDecl *> &Val = PMC[std::make_pair(IDecl, Sel)];
-
- // Query lookupPrivateMethod() if the cache does not hit.
- if (!Val.hasValue()) {
- Val = IDecl->lookupPrivateMethod(Sel);
-
- // If the method is a property accessor, we should try to "inline" it
- // even if we don't actually have an implementation.
- if (!*Val)
- if (const ObjCMethodDecl *CompileTimeMD = E->getMethodDecl())
- if (CompileTimeMD->isPropertyAccessor()) {
- if (!CompileTimeMD->getSelfDecl() &&
- isa<ObjCCategoryDecl>(CompileTimeMD->getDeclContext())) {
- // If the method is an accessor in a category, and it doesn't
- // have a self declaration, first
- // try to find the method in a class extension. This
- // works around a bug in Sema where multiple accessors
- // are synthesized for properties in class
- // extensions that are redeclared in a category and the
- // the implicit parameters are not filled in for
- // the method on the category.
- // This ensures we find the accessor in the extension, which
- // has the implicit parameters filled in.
- auto *ID = CompileTimeMD->getClassInterface();
- for (auto *CatDecl : ID->visible_extensions()) {
- Val = CatDecl->getMethod(Sel,
- CompileTimeMD->isInstanceMethod());
- if (*Val)
- break;
- }
- }
- if (!*Val)
- Val = IDecl->lookupInstanceMethod(Sel);
- }
- }
+ if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterface()) {
+ const ObjCMethodDecl *MD =
+ lookupRuntimeDefinition(IDecl, Sel, LookingForInstanceMethod);
- const ObjCMethodDecl *MD = Val.getValue();
if (MD && !MD->hasBody())
MD = MD->getCanonicalDecl();
+
if (CanBeSubClassed)
return RuntimeDefinition(MD, Receiver);
else
@@ -1392,17 +1400,20 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
if (CallEventRef<> Out = getCall(CallSite, State, CallerCtx))
return Out;
- // All other cases are handled by getCall.
- assert(isa<CXXConstructExpr>(CallSite) &&
- "This is not an inlineable statement");
-
SValBuilder &SVB = State->getStateManager().getSValBuilder();
const auto *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl());
Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx);
SVal ThisVal = State->getSVal(ThisPtr);
- return getCXXConstructorCall(cast<CXXConstructExpr>(CallSite),
- ThisVal.getAsRegion(), State, CallerCtx);
+ if (const auto *CE = dyn_cast<CXXConstructExpr>(CallSite))
+ return getCXXConstructorCall(CE, ThisVal.getAsRegion(), State, CallerCtx);
+ else if (const auto *CIE = dyn_cast<CXXInheritedCtorInitExpr>(CallSite))
+ return getCXXInheritedConstructorCall(CIE, ThisVal.getAsRegion(), State,
+ CallerCtx);
+ else {
+ // All other cases are handled by getCall.
+ llvm_unreachable("This is not an inlineable statement");
+ }
}
// Fall back to the CFG. The only thing we haven't handled yet is
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
index 11693132de68..cae728815b41 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
@@ -13,6 +13,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
+#include "clang/Lex/Preprocessor.h"
namespace clang {
@@ -109,6 +110,43 @@ Nullability getNullabilityAnnotation(QualType Type) {
return Nullability::Unspecified;
}
+llvm::Optional<int> tryExpandAsInteger(StringRef Macro,
+ const Preprocessor &PP) {
+ const auto *MacroII = PP.getIdentifierInfo(Macro);
+ if (!MacroII)
+ return llvm::None;
+ const MacroInfo *MI = PP.getMacroInfo(MacroII);
+ if (!MI)
+ return llvm::None;
+
+ // Filter out parens.
+ std::vector<Token> FilteredTokens;
+ FilteredTokens.reserve(MI->tokens().size());
+ for (auto &T : MI->tokens())
+ if (!T.isOneOf(tok::l_paren, tok::r_paren))
+ FilteredTokens.push_back(T);
+
+ // Parse an integer at the end of the macro definition.
+ const Token &T = FilteredTokens.back();
+ // FIXME: EOF macro token coming from a PCH file on macOS while marked as
+ // literal, doesn't contain any literal data
+ if (!T.isLiteral() || !T.getLiteralData())
+ return llvm::None;
+ StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength());
+ llvm::APInt IntValue;
+ constexpr unsigned AutoSenseRadix = 0;
+ if (ValueStr.getAsInteger(AutoSenseRadix, IntValue))
+ return llvm::None;
+
+ // Parse an optional minus sign.
+ size_t Size = FilteredTokens.size();
+ if (Size >= 2) {
+ if (FilteredTokens[Size - 2].is(tok::minus))
+ IntValue = -IntValue;
+ }
+
+ return IntValue.getSExtValue();
+}
-} // end namespace ento
-} // end namespace clang
+} // namespace ento
+} // namespace clang
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
index a9361837cf68..86cecf6524f0 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -61,12 +61,12 @@ void CheckerManager::finishedCheckerRegistration() {
}
void CheckerManager::reportInvalidCheckerOptionValue(
- const CheckerBase *C, StringRef OptionName, StringRef ExpectedValueDesc) {
+ const CheckerBase *C, StringRef OptionName,
+ StringRef ExpectedValueDesc) const {
- Context.getDiagnostics()
- .Report(diag::err_analyzer_checker_option_invalid_input)
- << (llvm::Twine() + C->getTagDescription() + ":" + OptionName).str()
- << ExpectedValueDesc;
+ getDiagnostics().Report(diag::err_analyzer_checker_option_invalid_input)
+ << (llvm::Twine() + C->getTagDescription() + ":" + OptionName).str()
+ << ExpectedValueDesc;
}
//===----------------------------------------------------------------------===//
@@ -243,13 +243,13 @@ void CheckerManager::runCheckersForObjCMessage(ObjCMessageVisitKind visitKind,
const ObjCMethodCall &msg,
ExprEngine &Eng,
bool WasInlined) {
- auto &checkers = getObjCMessageCheckers(visitKind);
+ const auto &checkers = getObjCMessageCheckers(visitKind);
CheckObjCMessageContext C(visitKind, checkers, msg, Eng, WasInlined);
expandGraphWithCheckers(C, Dst, Src);
}
const std::vector<CheckerManager::CheckObjCMessageFunc> &
-CheckerManager::getObjCMessageCheckers(ObjCMessageVisitKind Kind) {
+CheckerManager::getObjCMessageCheckers(ObjCMessageVisitKind Kind) const {
switch (Kind) {
case ObjCMessageVisitKind::Pre:
return PreObjCMessageCheckers;
@@ -507,35 +507,38 @@ namespace {
using CheckersTy = std::vector<CheckerManager::CheckNewAllocatorFunc>;
const CheckersTy &Checkers;
- const CXXNewExpr *NE;
- SVal Target;
+ const CXXAllocatorCall &Call;
bool WasInlined;
ExprEngine &Eng;
- CheckNewAllocatorContext(const CheckersTy &Checkers, const CXXNewExpr *NE,
- SVal Target, bool WasInlined, ExprEngine &Eng)
- : Checkers(Checkers), NE(NE), Target(Target), WasInlined(WasInlined),
- Eng(Eng) {}
+ CheckNewAllocatorContext(const CheckersTy &Checkers,
+ const CXXAllocatorCall &Call, bool WasInlined,
+ ExprEngine &Eng)
+ : Checkers(Checkers), Call(Call), WasInlined(WasInlined), Eng(Eng) {}
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
void runChecker(CheckerManager::CheckNewAllocatorFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
- ProgramPoint L = PostAllocatorCall(NE, Pred->getLocationContext());
+ ProgramPoint L =
+ PostAllocatorCall(Call.getOriginExpr(), Pred->getLocationContext());
CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
- checkFn(NE, Target, C);
+ checkFn(cast<CXXAllocatorCall>(*Call.cloneWithState(Pred->getState())),
+ C);
}
};
} // namespace
-void CheckerManager::runCheckersForNewAllocator(
- const CXXNewExpr *NE, SVal Target, ExplodedNodeSet &Dst, ExplodedNode *Pred,
- ExprEngine &Eng, bool WasInlined) {
+void CheckerManager::runCheckersForNewAllocator(const CXXAllocatorCall &Call,
+ ExplodedNodeSet &Dst,
+ ExplodedNode *Pred,
+ ExprEngine &Eng,
+ bool WasInlined) {
ExplodedNodeSet Src;
Src.insert(Pred);
- CheckNewAllocatorContext C(NewAllocatorCheckers, NE, Target, WasInlined, Eng);
+ CheckNewAllocatorContext C(NewAllocatorCheckers, Call, WasInlined, Eng);
expandGraphWithCheckers(C, Dst, Src);
}
@@ -650,8 +653,9 @@ CheckerManager::runCheckersForEvalAssume(ProgramStateRef state,
void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
const CallEvent &Call,
- ExprEngine &Eng) {
- for (const auto Pred : Src) {
+ ExprEngine &Eng,
+ const EvalCallOptions &CallOpts) {
+ for (auto *const Pred : Src) {
bool anyEvaluated = false;
ExplodedNodeSet checkDst;
@@ -662,10 +666,8 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
// 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);
+ 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
@@ -687,7 +689,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
// If none of the checkers evaluated the call, ask ExprEngine to handle it.
if (!anyEvaluated) {
NodeBuilder B(Pred, Dst, Eng.getBuilderContext());
- Eng.defaultEvalCall(B, Pred, Call);
+ Eng.defaultEvalCall(B, Pred, Call, CallOpts);
}
}
}
@@ -902,8 +904,3 @@ CheckerManager::getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit) {
Checkers.push_back(Info.CheckFn);
return Checkers;
}
-
-CheckerManager::~CheckerManager() {
- for (const auto &CheckerDtor : CheckerDtors)
- CheckerDtor();
-}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp
new file mode 100644
index 000000000000..1b3e8b11549d
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp
@@ -0,0 +1,241 @@
+//===- CheckerRegistry.h - Maintains all available checkers -----*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/CheckerRegistryData.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "llvm/ADT/Twine.h"
+#include <map>
+
+using namespace clang;
+using namespace ento;
+
+//===----------------------------------------------------------------------===//
+// Methods of CmdLineOption, PackageInfo and CheckerInfo.
+//===----------------------------------------------------------------------===//
+
+LLVM_DUMP_METHOD void CmdLineOption::dump() const {
+ dumpToStream(llvm::errs());
+}
+
+LLVM_DUMP_METHOD void
+CmdLineOption::dumpToStream(llvm::raw_ostream &Out) const {
+ // The description can be just checked in Checkers.inc, the point here is to
+ // debug whether we succeeded in parsing it.
+ Out << OptionName << " (" << OptionType << ", "
+ << (IsHidden ? "hidden, " : "") << DevelopmentStatus << ") default: \""
+ << DefaultValStr;
+}
+
+static StringRef toString(StateFromCmdLine Kind) {
+ switch (Kind) {
+ case StateFromCmdLine::State_Disabled:
+ return "Disabled";
+ case StateFromCmdLine::State_Enabled:
+ return "Enabled";
+ case StateFromCmdLine::State_Unspecified:
+ return "Unspecified";
+ }
+ llvm_unreachable("Unhandled StateFromCmdLine enum");
+}
+
+LLVM_DUMP_METHOD void CheckerInfo::dump() const { dumpToStream(llvm::errs()); }
+
+LLVM_DUMP_METHOD void CheckerInfo::dumpToStream(llvm::raw_ostream &Out) const {
+ // The description can be just checked in Checkers.inc, the point here is to
+ // debug whether we succeeded in parsing it. Same with documentation uri.
+ Out << FullName << " (" << toString(State) << (IsHidden ? ", hidden" : "")
+ << ")\n";
+ Out << " Options:\n";
+ for (const CmdLineOption &Option : CmdLineOptions) {
+ Out << " ";
+ Option.dumpToStream(Out);
+ Out << '\n';
+ }
+ Out << " Dependencies:\n";
+ for (const CheckerInfo *Dependency : Dependencies) {
+ Out << " " << Dependency->FullName << '\n';
+ }
+ Out << " Weak dependencies:\n";
+ for (const CheckerInfo *Dependency : WeakDependencies) {
+ Out << " " << Dependency->FullName << '\n';
+ }
+}
+
+LLVM_DUMP_METHOD void PackageInfo::dump() const { dumpToStream(llvm::errs()); }
+
+LLVM_DUMP_METHOD void PackageInfo::dumpToStream(llvm::raw_ostream &Out) const {
+ Out << FullName << "\n";
+ Out << " Options:\n";
+ for (const CmdLineOption &Option : CmdLineOptions) {
+ Out << " ";
+ Option.dumpToStream(Out);
+ Out << '\n';
+ }
+}
+
+static constexpr char PackageSeparator = '.';
+
+static bool isInPackage(const CheckerInfo &Checker, StringRef PackageName) {
+ // Does the checker's full name have the package as a prefix?
+ if (!Checker.FullName.startswith(PackageName))
+ return false;
+
+ // Is the package actually just the name of a specific checker?
+ if (Checker.FullName.size() == PackageName.size())
+ return true;
+
+ // Is the checker in the package (or a subpackage)?
+ if (Checker.FullName[PackageName.size()] == PackageSeparator)
+ return true;
+
+ return false;
+}
+
+CheckerInfoListRange
+CheckerRegistryData::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) {
+ auto It = checker_registry::binaryFind(Checkers, CmdLineArg);
+
+ if (!isInPackage(*It, CmdLineArg))
+ return {Checkers.end(), Checkers.end()};
+
+ // See how large the package is.
+ // If the package doesn't exist, assume the option refers to a single
+ // checker.
+ size_t Size = 1;
+ llvm::StringMap<size_t>::const_iterator PackageSize =
+ PackageSizes.find(CmdLineArg);
+
+ if (PackageSize != PackageSizes.end())
+ Size = PackageSize->getValue();
+
+ return {It, It + Size};
+}
+//===----------------------------------------------------------------------===//
+// Printing functions.
+//===----------------------------------------------------------------------===//
+
+void CheckerRegistryData::printCheckerWithDescList(
+ const AnalyzerOptions &AnOpts, raw_ostream &Out,
+ size_t MaxNameChars) const {
+ // FIXME: Print available packages.
+
+ Out << "CHECKERS:\n";
+
+ // Find the maximum option length.
+ size_t OptionFieldWidth = 0;
+ for (const auto &Checker : Checkers) {
+ // Limit the amount of padding we are willing to give up for alignment.
+ // Package.Name Description [Hidden]
+ size_t NameLength = Checker.FullName.size();
+ if (NameLength <= MaxNameChars)
+ OptionFieldWidth = std::max(OptionFieldWidth, NameLength);
+ }
+
+ const size_t InitialPad = 2;
+
+ auto Print = [=](llvm::raw_ostream &Out, const CheckerInfo &Checker,
+ StringRef Description) {
+ AnalyzerOptions::printFormattedEntry(Out, {Checker.FullName, Description},
+ InitialPad, OptionFieldWidth);
+ Out << '\n';
+ };
+
+ for (const auto &Checker : Checkers) {
+ // The order of this if branches is significant, we wouldn't like to display
+ // developer checkers even in the alpha output. For example,
+ // alpha.cplusplus.IteratorModeling is a modeling checker, hence it's hidden
+ // by default, and users (even when the user is a developer of an alpha
+ // checker) shouldn't normally tinker with whether they should be enabled.
+
+ if (Checker.IsHidden) {
+ if (AnOpts.ShowCheckerHelpDeveloper)
+ Print(Out, Checker, Checker.Desc);
+ continue;
+ }
+
+ if (Checker.FullName.startswith("alpha")) {
+ if (AnOpts.ShowCheckerHelpAlpha)
+ Print(Out, Checker,
+ ("(Enable only for development!) " + Checker.Desc).str());
+ continue;
+ }
+
+ if (AnOpts.ShowCheckerHelp)
+ Print(Out, Checker, Checker.Desc);
+ }
+}
+
+void CheckerRegistryData::printEnabledCheckerList(raw_ostream &Out) const {
+ for (const auto *i : EnabledCheckers)
+ Out << i->FullName << '\n';
+}
+
+void CheckerRegistryData::printCheckerOptionList(const AnalyzerOptions &AnOpts,
+ raw_ostream &Out) const {
+ Out << "OVERVIEW: Clang Static Analyzer Checker and Package Option List\n\n";
+ Out << "USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>\n\n";
+ Out << " -analyzer-config OPTION1=VALUE, -analyzer-config "
+ "OPTION2=VALUE, ...\n\n";
+ Out << "OPTIONS:\n\n";
+
+ // It's usually ill-advised to use multimap, but clang will terminate after
+ // this function.
+ std::multimap<StringRef, const CmdLineOption &> OptionMap;
+
+ for (const CheckerInfo &Checker : Checkers) {
+ for (const CmdLineOption &Option : Checker.CmdLineOptions) {
+ OptionMap.insert({Checker.FullName, Option});
+ }
+ }
+
+ for (const PackageInfo &Package : Packages) {
+ for (const CmdLineOption &Option : Package.CmdLineOptions) {
+ OptionMap.insert({Package.FullName, Option});
+ }
+ }
+
+ auto Print = [](llvm::raw_ostream &Out, StringRef FullOption,
+ StringRef Desc) {
+ AnalyzerOptions::printFormattedEntry(Out, {FullOption, Desc},
+ /*InitialPad*/ 2,
+ /*EntryWidth*/ 50,
+ /*MinLineWidth*/ 90);
+ Out << "\n\n";
+ };
+ for (const std::pair<const StringRef, const CmdLineOption &> &Entry :
+ OptionMap) {
+ const CmdLineOption &Option = Entry.second;
+ std::string FullOption = (Entry.first + ":" + Option.OptionName).str();
+
+ std::string Desc =
+ ("(" + Option.OptionType + ") " + Option.Description + " (default: " +
+ (Option.DefaultValStr.empty() ? "\"\"" : Option.DefaultValStr) + ")")
+ .str();
+
+ // The list of these if branches is significant, we wouldn't like to
+ // display hidden alpha checker options for
+ // -analyzer-checker-option-help-alpha.
+
+ if (Option.IsHidden) {
+ if (AnOpts.ShowCheckerOptionDeveloperList)
+ Print(Out, FullOption, Desc);
+ continue;
+ }
+
+ if (Option.DevelopmentStatus == "alpha" ||
+ Entry.first.startswith("alpha")) {
+ if (AnOpts.ShowCheckerOptionAlphaList)
+ Print(Out, FullOption,
+ llvm::Twine("(Enable only for development!) " + Desc).str());
+ continue;
+ }
+
+ if (AnOpts.ShowCheckerOptionList)
+ Print(Out, FullOption, Desc);
+ }
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
index bdae3e605eff..a601370775b4 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
@@ -9,13 +9,18 @@
#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
// Common strings used for the "category" of many static analyzer issues.
-namespace clang { namespace ento { namespace categories {
+namespace clang {
+namespace ento {
+namespace categories {
-const char * const CoreFoundationObjectiveC = "Core Foundation/Objective-C";
-const char * const LogicError = "Logic error";
-const char * const MemoryRefCount =
- "Memory (Core Foundation/Objective-C/OSObject)";
-const char * const MemoryError = "Memory error";
-const char * const UnixAPI = "Unix API";
-const char * const CXXObjectLifecycle = "C++ object lifecycle";
-}}}
+const char *const CoreFoundationObjectiveC = "Core Foundation/Objective-C";
+const char *const LogicError = "Logic error";
+const char *const MemoryRefCount =
+ "Memory (Core Foundation/Objective-C/OSObject)";
+const char *const MemoryError = "Memory error";
+const char *const UnixAPI = "Unix API";
+const char *const CXXObjectLifecycle = "C++ object lifecycle";
+const char *const SecurityError = "Security error";
+} // namespace categories
+} // namespace ento
+} // namespace clang
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
index 94cf74de8293..70deb13a8e1a 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -23,8 +23,8 @@
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
@@ -52,8 +52,7 @@ STATISTIC(NumPathsExplored,
// Core analysis engine.
//===----------------------------------------------------------------------===//
-static std::unique_ptr<WorkList> generateWorkList(AnalyzerOptions &Opts,
- SubEngine &subengine) {
+static std::unique_ptr<WorkList> generateWorkList(AnalyzerOptions &Opts) {
switch (Opts.getExplorationStrategy()) {
case ExplorationStrategyKind::DFS:
return WorkList::makeDFS();
@@ -71,9 +70,9 @@ static std::unique_ptr<WorkList> generateWorkList(AnalyzerOptions &Opts,
llvm_unreachable("Unknown AnalyzerOptions::ExplorationStrategyKind");
}
-CoreEngine::CoreEngine(SubEngine &subengine, FunctionSummariesTy *FS,
+CoreEngine::CoreEngine(ExprEngine &exprengine, FunctionSummariesTy *FS,
AnalyzerOptions &Opts)
- : SubEng(subengine), WList(generateWorkList(Opts, subengine)),
+ : ExprEng(exprengine), WList(generateWorkList(Opts)),
BCounterFactory(G.getAllocator()), FunctionSummaries(FS) {}
/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps.
@@ -104,7 +103,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
WList->setBlockCounter(BCounterFactory.GetEmptyCounter());
if (!InitState)
- InitState = SubEng.getInitialState(L);
+ InitState = ExprEng.getInitialState(L);
bool IsNew;
ExplodedNode *Node = G.getNode(StartLoc, InitState, false, &IsNew);
@@ -113,7 +112,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
NodeBuilderContext BuilderCtx(*this, StartLoc.getDst(), Node);
ExplodedNodeSet DstBegin;
- SubEng.processBeginOfFunction(BuilderCtx, Node, DstBegin, StartLoc);
+ ExprEng.processBeginOfFunction(BuilderCtx, Node, DstBegin, StartLoc);
enqueue(DstBegin);
}
@@ -147,7 +146,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
dispatchWorkItem(Node, Node->getLocation(), WU);
}
- SubEng.processEndWorklist();
+ ExprEng.processEndWorklist();
return WList->hasWork();
}
@@ -172,7 +171,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
break;
case ProgramPoint::CallExitBeginKind:
- SubEng.processCallExit(Pred);
+ ExprEng.processCallExit(Pred);
break;
case ProgramPoint::EpsilonKind: {
@@ -221,7 +220,7 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
if (L.getSrc()->getTerminator().isVirtualBaseBranch() &&
L.getDst() == *L.getSrc()->succ_begin()) {
ProgramPoint P = L.withTag(getNoteTags().makeNoteTag(
- [](BugReporterContext &, BugReport &) -> std::string {
+ [](BugReporterContext &, PathSensitiveBugReport &) -> std::string {
// TODO: Just call out the name of the most derived class
// when we know it.
return "Virtual base initialization skipped because "
@@ -253,17 +252,17 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
}
// Process the final state transition.
- SubEng.processEndOfFunction(BuilderCtx, Pred, RS);
+ ExprEng.processEndOfFunction(BuilderCtx, Pred, RS);
// This path is done. Don't enqueue any more nodes.
return;
}
- // Call into the SubEngine to process entering the CFGBlock.
+ // Call into the ExprEngine to process entering the CFGBlock.
ExplodedNodeSet dstNodes;
BlockEntrance BE(Blk, Pred->getLocationContext());
NodeBuilderWithSinks nodeBuilder(Pred, dstNodes, BuilderCtx, BE);
- SubEng.processCFGBlockEntrance(L, nodeBuilder, Pred);
+ ExprEng.processCFGBlockEntrance(L, nodeBuilder, Pred);
// Auto-generate a node.
if (!nodeBuilder.hasGeneratedNodes()) {
@@ -287,7 +286,7 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance &L,
// Process the entrance of the block.
if (Optional<CFGElement> E = L.getFirstElement()) {
NodeBuilderContext Ctx(*this, L.getBlock(), Pred);
- SubEng.processCFGElement(*E, Pred, 0, &Ctx);
+ ExprEng.processCFGElement(*E, Pred, 0, &Ctx);
}
else
HandleBlockExit(L.getBlock(), Pred);
@@ -367,7 +366,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
builder(Pred, B, cast<IndirectGotoStmt>(Term)->getTarget(),
*(B->succ_begin()), this);
- SubEng.processIndirectGoto(builder);
+ ExprEng.processIndirectGoto(builder);
return;
}
@@ -378,7 +377,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
// 'element' variable to a value.
// (2) in a terminator, which represents the branch.
//
- // For (1), subengines will bind a value (i.e., 0 or 1) indicating
+ // For (1), ExprEngine will bind a value (i.e., 0 or 1) indicating
// whether or not collection contains any more elements. We cannot
// just test to see if the element is nil because a container can
// contain nil elements.
@@ -389,7 +388,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
SwitchNodeBuilder builder(Pred, B, cast<SwitchStmt>(Term)->getCond(),
this);
- SubEng.processSwitch(builder);
+ ExprEng.processSwitch(builder);
return;
}
@@ -418,7 +417,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
void CoreEngine::HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred) {
NodeBuilderContext BuilderCtx(*this, CE.getEntry(), Pred);
- SubEng.processCallEnter(BuilderCtx, CE, Pred);
+ ExprEng.processCallEnter(BuilderCtx, CE, Pred);
}
void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
@@ -426,7 +425,7 @@ void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
assert(B->succ_size() == 2);
NodeBuilderContext Ctx(*this, B, Pred);
ExplodedNodeSet Dst;
- SubEng.processBranch(Cond, Ctx, Pred, Dst, *(B->succ_begin()),
+ ExprEng.processBranch(Cond, Ctx, Pred, Dst, *(B->succ_begin()),
*(B->succ_begin() + 1));
// Enqueue the new frontier onto the worklist.
enqueue(Dst);
@@ -438,7 +437,7 @@ void CoreEngine::HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
assert(B->succ_size() == 2);
NodeBuilderContext Ctx(*this, B, Pred);
ExplodedNodeSet Dst;
- SubEng.processCleanupTemporaryBranch(BTE, Ctx, Pred, Dst, *(B->succ_begin()),
+ ExprEng.processCleanupTemporaryBranch(BTE, Ctx, Pred, Dst, *(B->succ_begin()),
*(B->succ_begin() + 1));
// Enqueue the new frontier onto the worklist.
enqueue(Dst);
@@ -449,7 +448,7 @@ void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
assert(B->succ_size() == 2);
NodeBuilderContext Ctx(*this, B, Pred);
ExplodedNodeSet Dst;
- SubEng.processStaticInitializer(DS, Ctx, Pred, Dst,
+ ExprEng.processStaticInitializer(DS, Ctx, Pred, Dst,
*(B->succ_begin()), *(B->succ_begin()+1));
// Enqueue the new frontier onto the worklist.
enqueue(Dst);
@@ -464,7 +463,7 @@ void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
HandleBlockExit(B, Pred);
else {
NodeBuilderContext Ctx(*this, B, Pred);
- SubEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx, &Ctx);
+ ExprEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx, &Ctx);
}
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicSize.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicSize.cpp
new file mode 100644
index 000000000000..8b2172db445c
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicSize.cpp
@@ -0,0 +1,71 @@
+//===- DynamicSize.cpp - Dynamic size related APIs --------------*- 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 APIs that track and query dynamic size information.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
+#include "clang/AST/Expr.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+
+namespace clang {
+namespace ento {
+
+DefinedOrUnknownSVal getDynamicSize(ProgramStateRef State, const MemRegion *MR,
+ SValBuilder &SVB) {
+ return MR->getMemRegionManager().getStaticSize(MR, SVB);
+}
+
+DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State,
+ const MemRegion *MR,
+ SValBuilder &SVB,
+ QualType ElementTy) {
+ MemRegionManager &MemMgr = MR->getMemRegionManager();
+ ASTContext &Ctx = MemMgr.getContext();
+
+ DefinedOrUnknownSVal Size = getDynamicSize(State, MR, SVB);
+ SVal ElementSizeV = SVB.makeIntVal(
+ Ctx.getTypeSizeInChars(ElementTy).getQuantity(), SVB.getArrayIndexType());
+
+ SVal DivisionV =
+ SVB.evalBinOp(State, BO_Div, Size, ElementSizeV, SVB.getArrayIndexType());
+
+ return DivisionV.castAs<DefinedOrUnknownSVal>();
+}
+
+SVal getDynamicSizeWithOffset(ProgramStateRef State, const SVal &BufV) {
+ SValBuilder &SvalBuilder = State->getStateManager().getSValBuilder();
+ const MemRegion *MRegion = BufV.getAsRegion();
+ if (!MRegion)
+ return UnknownVal();
+ RegionOffset Offset = MRegion->getAsOffset();
+ if (Offset.hasSymbolicOffset())
+ return UnknownVal();
+ const MemRegion *BaseRegion = MRegion->getBaseRegion();
+ if (!BaseRegion)
+ return UnknownVal();
+
+ NonLoc OffsetInBytes = SvalBuilder.makeArrayIndex(
+ Offset.getOffset() /
+ MRegion->getMemRegionManager().getContext().getCharWidth());
+ DefinedOrUnknownSVal ExtentInBytes =
+ getDynamicSize(State, BaseRegion, SvalBuilder);
+
+ return SvalBuilder.evalBinOp(State, BinaryOperator::Opcode::BO_Sub,
+ ExtentInBytes, OffsetInBytes,
+ SvalBuilder.getArrayIndexType());
+}
+
+} // namespace ento
+} // namespace clang
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicType.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicType.cpp
index a78e0e05e903..e9b64fd79614 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicType.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicType.cpp
@@ -34,6 +34,10 @@ REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(CastSet, clang::ento::DynamicCastInfo)
REGISTER_MAP_WITH_PROGRAMSTATE(DynamicCastMap, const clang::ento::MemRegion *,
CastSet)
+// A map from Class object symbols to the most likely pointed-to type.
+REGISTER_MAP_WITH_PROGRAMSTATE(DynamicClassObjectMap, clang::ento::SymbolRef,
+ clang::ento::DynamicTypeInfo)
+
namespace clang {
namespace ento {
@@ -76,6 +80,12 @@ const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State,
return nullptr;
}
+DynamicTypeInfo getClassObjectDynamicTypeInfo(ProgramStateRef State,
+ SymbolRef Sym) {
+ const DynamicTypeInfo *DTI = State->get<DynamicClassObjectMap>(Sym);
+ return DTI ? *DTI : DynamicTypeInfo{};
+}
+
ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR,
DynamicTypeInfo NewTy) {
State = State->set<DynamicTypeMap>(MR->StripCasts(), NewTy);
@@ -118,111 +128,165 @@ ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State,
return State;
}
+ProgramStateRef setClassObjectDynamicTypeInfo(ProgramStateRef State,
+ SymbolRef Sym,
+ DynamicTypeInfo NewTy) {
+ State = State->set<DynamicClassObjectMap>(Sym, NewTy);
+ return State;
+}
+
+ProgramStateRef setClassObjectDynamicTypeInfo(ProgramStateRef State,
+ SymbolRef Sym, QualType NewTy,
+ bool CanBeSubClassed) {
+ return setClassObjectDynamicTypeInfo(State, Sym,
+ DynamicTypeInfo(NewTy, CanBeSubClassed));
+}
+
+static bool isLive(SymbolReaper &SR, const MemRegion *MR) {
+ return SR.isLiveRegion(MR);
+}
+
+static bool isLive(SymbolReaper &SR, SymbolRef Sym) { return SR.isLive(Sym); }
+
template <typename MapTy>
-ProgramStateRef removeDead(ProgramStateRef State, const MapTy &Map,
- SymbolReaper &SR) {
+static ProgramStateRef removeDeadImpl(ProgramStateRef State, SymbolReaper &SR) {
+ const auto &Map = State->get<MapTy>();
+
for (const auto &Elem : Map)
- if (!SR.isLiveRegion(Elem.first))
- State = State->remove<DynamicCastMap>(Elem.first);
+ if (!isLive(SR, Elem.first))
+ State = State->remove<MapTy>(Elem.first);
return State;
}
ProgramStateRef removeDeadTypes(ProgramStateRef State, SymbolReaper &SR) {
- return removeDead(State, State->get<DynamicTypeMap>(), SR);
+ return removeDeadImpl<DynamicTypeMap>(State, SR);
}
ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR) {
- return removeDead(State, State->get<DynamicCastMap>(), SR);
+ return removeDeadImpl<DynamicCastMap>(State, SR);
}
-static void printDynamicTypesJson(raw_ostream &Out, ProgramStateRef State,
- const char *NL, unsigned int Space,
- bool IsDot) {
- Indent(Out, Space, IsDot) << "\"dynamic_types\": ";
+ProgramStateRef removeDeadClassObjectTypes(ProgramStateRef State,
+ SymbolReaper &SR) {
+ return removeDeadImpl<DynamicClassObjectMap>(State, SR);
+}
- const DynamicTypeMapTy &Map = State->get<DynamicTypeMap>();
- if (Map.isEmpty()) {
- Out << "null," << NL;
- return;
- }
+//===----------------------------------------------------------------------===//
+// Implementation of the 'printer-to-JSON' function
+//===----------------------------------------------------------------------===//
- ++Space;
- Out << '[' << NL;
- for (DynamicTypeMapTy::iterator I = Map.begin(); I != Map.end(); ++I) {
- const MemRegion *MR = I->first;
- const DynamicTypeInfo &DTI = I->second;
- Indent(Out, Space, IsDot)
- << "{ \"region\": \"" << MR << "\", \"dyn_type\": ";
- if (!DTI.isValid()) {
- Out << "null";
- } else {
- Out << '\"' << DTI.getType()->getPointeeType().getAsString()
- << "\", \"sub_classable\": "
- << (DTI.canBeASubClass() ? "true" : "false");
- }
- Out << " }";
-
- if (std::next(I) != Map.end())
- Out << ',';
- Out << NL;
+static raw_ostream &printJson(const MemRegion *Region, raw_ostream &Out,
+ const char *NL, unsigned int Space, bool IsDot) {
+ return Out << "\"region\": \"" << Region << "\"";
+}
+
+static raw_ostream &printJson(const SymExpr *Symbol, raw_ostream &Out,
+ const char *NL, unsigned int Space, bool IsDot) {
+ return Out << "\"symbol\": \"" << Symbol << "\"";
+}
+
+static raw_ostream &printJson(const DynamicTypeInfo &DTI, raw_ostream &Out,
+ const char *NL, unsigned int Space, bool IsDot) {
+ Out << "\"dyn_type\": ";
+ if (!DTI.isValid()) {
+ Out << "null";
+ } else {
+ QualType ToPrint = DTI.getType();
+ if (ToPrint->isAnyPointerType())
+ ToPrint = ToPrint->getPointeeType();
+
+ Out << '\"' << ToPrint.getAsString() << "\", \"sub_classable\": "
+ << (DTI.canBeASubClass() ? "true" : "false");
}
+ return Out;
+}
- --Space;
- Indent(Out, Space, IsDot) << "]," << NL;
+static raw_ostream &printJson(const DynamicCastInfo &DCI, raw_ostream &Out,
+ const char *NL, unsigned int Space, bool IsDot) {
+ return Out << "\"from\": \"" << DCI.from().getAsString() << "\", \"to\": \""
+ << DCI.to().getAsString() << "\", \"kind\": \""
+ << (DCI.succeeds() ? "success" : "fail") << "\"";
}
-static void printDynamicCastsJson(raw_ostream &Out, ProgramStateRef State,
- const char *NL, unsigned int Space,
- bool IsDot) {
- Indent(Out, Space, IsDot) << "\"dynamic_casts\": ";
+template <class T, class U>
+static raw_ostream &printJson(const std::pair<T, U> &Pair, raw_ostream &Out,
+ const char *NL, unsigned int Space, bool IsDot) {
+ printJson(Pair.first, Out, NL, Space, IsDot) << ", ";
+ return printJson(Pair.second, Out, NL, Space, IsDot);
+}
- const DynamicCastMapTy &Map = State->get<DynamicCastMap>();
- if (Map.isEmpty()) {
- Out << "null," << NL;
- return;
+template <class ContainerTy>
+static raw_ostream &printJsonContainer(const ContainerTy &Container,
+ raw_ostream &Out, const char *NL,
+ unsigned int Space, bool IsDot) {
+ if (Container.isEmpty()) {
+ return Out << "null";
}
++Space;
Out << '[' << NL;
- for (DynamicCastMapTy::iterator I = Map.begin(); I != Map.end(); ++I) {
- const MemRegion *MR = I->first;
- const CastSet &Set = I->second;
-
- Indent(Out, Space, IsDot) << "{ \"region\": \"" << MR << "\", \"casts\": ";
- if (Set.isEmpty()) {
- Out << "null ";
- } else {
- ++Space;
- Out << '[' << NL;
- for (CastSet::iterator SI = Set.begin(); SI != Set.end(); ++SI) {
- Indent(Out, Space, IsDot)
- << "{ \"from\": \"" << SI->from().getAsString() << "\", \"to\": \""
- << SI->to().getAsString() << "\", \"kind\": \""
- << (SI->succeeds() ? "success" : "fail") << "\" }";
-
- if (std::next(SI) != Set.end())
- Out << ',';
- Out << NL;
- }
- --Space;
- Indent(Out, Space, IsDot) << ']';
- }
- Out << '}';
-
- if (std::next(I) != Map.end())
+ for (auto I = Container.begin(); I != Container.end(); ++I) {
+ const auto &Element = *I;
+
+ Indent(Out, Space, IsDot) << "{ ";
+ printJson(Element, Out, NL, Space, IsDot) << " }";
+
+ if (std::next(I) != Container.end())
Out << ',';
Out << NL;
}
--Space;
- Indent(Out, Space, IsDot) << "]," << NL;
+ return Indent(Out, Space, IsDot) << "]";
+}
+
+static raw_ostream &printJson(const CastSet &Set, raw_ostream &Out,
+ const char *NL, unsigned int Space, bool IsDot) {
+ Out << "\"casts\": ";
+ return printJsonContainer(Set, Out, NL, Space, IsDot);
+}
+
+template <class MapTy>
+static void printJsonImpl(raw_ostream &Out, ProgramStateRef State,
+ const char *Name, const char *NL, unsigned int Space,
+ bool IsDot, bool PrintEvenIfEmpty = true) {
+ const auto &Map = State->get<MapTy>();
+ if (Map.isEmpty() && !PrintEvenIfEmpty)
+ return;
+
+ Indent(Out, Space, IsDot) << "\"" << Name << "\": ";
+ printJsonContainer(Map, Out, NL, Space, IsDot) << "," << NL;
+}
+
+static void printDynamicTypesJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, unsigned int Space,
+ bool IsDot) {
+ printJsonImpl<DynamicTypeMap>(Out, State, "dynamic_types", NL, Space, IsDot);
+}
+
+static void printDynamicCastsJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, unsigned int Space,
+ bool IsDot) {
+ printJsonImpl<DynamicCastMap>(Out, State, "dynamic_casts", NL, Space, IsDot);
+}
+
+static void printClassObjectDynamicTypesJson(raw_ostream &Out,
+ ProgramStateRef State,
+ const char *NL, unsigned int Space,
+ bool IsDot) {
+ // Let's print Class object type information only if we have something
+ // meaningful to print.
+ printJsonImpl<DynamicClassObjectMap>(Out, State, "class_object_types", NL,
+ Space, IsDot,
+ /*PrintEvenIfEmpty=*/false);
}
void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State,
const char *NL, unsigned int Space, bool IsDot) {
printDynamicTypesJson(Out, State, NL, Space, IsDot);
printDynamicCastsJson(Out, State, NL, Space, IsDot);
+ printClassObjectDynamicTypesJson(Out, State, NL, Space, IsDot);
}
} // namespace ento
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp
index 1ccf4c6104a6..9e6d79bb7dcc 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp
@@ -183,12 +183,18 @@ EnvironmentManager::removeDeadBindings(Environment Env,
F.getTreeFactory());
// Iterate over the block-expr bindings.
- for (Environment::iterator I = Env.begin(), E = Env.end();
- I != E; ++I) {
+ for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) {
const EnvironmentEntry &BlkExpr = I.getKey();
const SVal &X = I.getData();
- if (SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext())) {
+ const bool IsBlkExprLive =
+ SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext());
+
+ assert((isa<Expr>(BlkExpr.getStmt()) || !IsBlkExprLive) &&
+ "Only Exprs can be live, LivenessAnalysis argues about the liveness "
+ "of *values*!");
+
+ if (IsBlkExprLive) {
// Copy the binding to the new map.
EBMapRef = EBMapRef.add(BlkExpr, X);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
index c4838492271c..635495e9bf60 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
@@ -50,9 +50,8 @@ ExplodedGraph::~ExplodedGraph() = default;
bool ExplodedGraph::isInterestingLValueExpr(const Expr *Ex) {
if (!Ex->isLValue())
return false;
- return isa<DeclRefExpr>(Ex) ||
- isa<MemberExpr>(Ex) ||
- isa<ObjCIvarRefExpr>(Ex);
+ return isa<DeclRefExpr>(Ex) || isa<MemberExpr>(Ex) ||
+ isa<ObjCIvarRefExpr>(Ex) || isa<ArraySubscriptExpr>(Ex);
}
bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index b542cf2c0303..265dcd134213 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1210,9 +1210,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
switch (S->getStmtClass()) {
// C++, OpenMP and ARC stuff we don't support yet.
- case Expr::ObjCIndirectCopyRestoreExprClass:
case Stmt::CXXDependentScopeMemberExprClass:
- case Stmt::CXXInheritedCtorInitExprClass:
case Stmt::CXXTryStmtClass:
case Stmt::CXXTypeidExprClass:
case Stmt::CXXUuidofExprClass:
@@ -1226,6 +1224,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::UnresolvedLookupExprClass:
case Stmt::UnresolvedMemberExprClass:
case Stmt::TypoExprClass:
+ case Stmt::RecoveryExprClass:
case Stmt::CXXNoexceptExprClass:
case Stmt::PackExpansionExprClass:
case Stmt::SubstNonTypeTemplateParmPackExprClass:
@@ -1258,6 +1257,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPTaskwaitDirectiveClass:
case Stmt::OMPTaskgroupDirectiveClass:
case Stmt::OMPFlushDirectiveClass:
+ case Stmt::OMPDepobjDirectiveClass:
+ case Stmt::OMPScanDirectiveClass:
case Stmt::OMPOrderedDirectiveClass:
case Stmt::OMPAtomicDirectiveClass:
case Stmt::OMPTargetDirectiveClass:
@@ -1411,6 +1412,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::SubstNonTypeTemplateParmExprClass:
case Stmt::CXXNullPtrLiteralExprClass:
case Stmt::OMPArraySectionExprClass:
+ case Stmt::OMPArrayShapingExprClass:
+ case Stmt::OMPIteratorExprClass:
case Stmt::TypeTraitExprClass: {
Bldr.takeNodes(Pred);
ExplodedNodeSet preVisit;
@@ -1511,6 +1514,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
+ case Stmt::MatrixSubscriptExprClass:
+ llvm_unreachable("Support for MatrixSubscriptExpr is not implemented.");
+ break;
+
case Stmt::GCCAsmStmtClass:
Bldr.takeNodes(Pred);
VisitGCCAsmStmt(cast<GCCAsmStmt>(S), Pred, Dst);
@@ -1618,6 +1625,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
+ case Stmt::CXXInheritedCtorInitExprClass:
+ Bldr.takeNodes(Pred);
+ VisitCXXInheritedCtorInitExpr(cast<CXXInheritedCtorInitExpr>(S), Pred,
+ Dst);
+ Bldr.addNodes(Dst);
+ break;
+
case Stmt::CXXNewExprClass: {
Bldr.takeNodes(Pred);
@@ -1638,8 +1652,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
ExplodedNodeSet PreVisit;
const auto *CDE = cast<CXXDeleteExpr>(S);
getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
+ ExplodedNodeSet PostVisit;
+ getCheckerManager().runCheckersForPostStmt(PostVisit, PreVisit, S, *this);
- for (const auto i : PreVisit)
+ for (const auto i : PostVisit)
VisitCXXDeleteExpr(CDE, i, Dst);
Bldr.addNodes(Dst);
@@ -1705,7 +1721,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::CXXConstCastExprClass:
case Stmt::CXXFunctionalCastExprClass:
case Stmt::BuiltinBitCastExprClass:
- case Stmt::ObjCBridgedCastExprClass: {
+ case Stmt::ObjCBridgedCastExprClass:
+ case Stmt::CXXAddrspaceCastExprClass: {
Bldr.takeNodes(Pred);
const auto *C = cast<CastExpr>(S);
ExplodedNodeSet dstExpr;
@@ -1852,6 +1869,21 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
}
+
+ case Expr::ObjCIndirectCopyRestoreExprClass: {
+ // ObjCIndirectCopyRestoreExpr implies passing a temporary for
+ // correctness of lifetime management. Due to limited analysis
+ // of ARC, this is implemented as direct arg passing.
+ Bldr.takeNodes(Pred);
+ ProgramStateRef state = Pred->getState();
+ const auto *OIE = cast<ObjCIndirectCopyRestoreExpr>(S);
+ const Expr *E = OIE->getSubExpr();
+ SVal V = state->getSVal(E, Pred->getLocationContext());
+ Bldr.generateNode(S, Pred,
+ state->BindExpr(S, Pred->getLocationContext(), V));
+ Bldr.addNodes(Dst);
+ break;
+ }
}
}
@@ -3161,11 +3193,13 @@ std::string ExprEngine::DumpGraph(bool trim, StringRef Filename) {
return DumpGraph(Src, Filename);
} else {
return llvm::WriteGraph(&G, "ExprEngine", /*ShortNames=*/false,
- /*Title=*/"Exploded Graph", /*Filename=*/Filename);
+ /*Title=*/"Exploded Graph",
+ /*Filename=*/std::string(Filename));
}
-#endif
+#else
llvm::errs() << "Warning: dumping graph requires assertions" << "\n";
return "";
+#endif
}
std::string ExprEngine::DumpGraph(ArrayRef<const ExplodedNode*> Nodes,
@@ -3179,7 +3213,7 @@ std::string ExprEngine::DumpGraph(ArrayRef<const ExplodedNode*> Nodes,
return llvm::WriteGraph(TrimmedG.get(), "TrimmedExprEngine",
/*ShortNames=*/false,
/*Title=*/"Trimmed Exploded Graph",
- /*Filename=*/Filename);
+ /*Filename=*/std::string(Filename));
}
#endif
llvm::errs() << "Warning: dumping graph requires assertions" << "\n";
@@ -3190,3 +3224,5 @@ void *ProgramStateTrait<ReplayWithoutInlining>::GDMIndex() {
static int index = 0;
return &index;
}
+
+void ExprEngine::anchor() { }
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index b17f26aa9c53..c5e38cc7423d 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -218,7 +218,7 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
auto CE = BD->capture_end();
for (; I != E; ++I) {
const VarRegion *capturedR = I.getCapturedRegion();
- const VarRegion *originalR = I.getOriginalRegion();
+ const TypedValueRegion *originalR = I.getOriginalRegion();
// If the capture had a copy expression, use the result of evaluating
// that expression, otherwise use the original value.
@@ -573,6 +573,18 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL,
void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ if (isa<TypedefNameDecl>(*DS->decl_begin())) {
+ // C99 6.7.7 "Any array size expressions associated with variable length
+ // array declarators are evaluated each time the declaration of the typedef
+ // name is reached in the order of execution."
+ // The checkers should know about typedef to be able to handle VLA size
+ // expressions.
+ ExplodedNodeSet DstPre;
+ getCheckerManager().runCheckersForPreStmt(DstPre, Pred, DS, *this);
+ getCheckerManager().runCheckersForPostStmt(Dst, DstPre, DS, *this);
+ return;
+ }
+
// Assumption: The CFG has one DeclStmt per Decl.
const VarDecl *VD = dyn_cast_or_null<VarDecl>(*DS->decl_begin());
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index b816aab7c18f..38a680eb04c0 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -109,15 +109,14 @@ SVal ExprEngine::makeZeroElementRegion(ProgramStateRef State, SVal LValue,
return LValue;
}
-std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
+SVal ExprEngine::computeObjectUnderConstruction(
const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
const ConstructionContext *CC, EvalCallOptions &CallOpts) {
SValBuilder &SVB = getSValBuilder();
MemRegionManager &MRMgr = SVB.getRegionManager();
ASTContext &ACtx = SVB.getContext();
- // See if we're constructing an existing region by looking at the
- // current construction context.
+ // Compute the target region by exploring the construction context.
if (CC) {
switch (CC->getKind()) {
case ConstructionContext::CXX17ElidedCopyVariableKind:
@@ -125,13 +124,9 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
const auto *DSCC = cast<VariableConstructionContext>(CC);
const auto *DS = DSCC->getDeclStmt();
const auto *Var = cast<VarDecl>(DS->getSingleDecl());
- SVal LValue = State->getLValue(Var, LCtx);
QualType Ty = Var->getType();
- LValue =
- makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor);
- State =
- addObjectUnderConstruction(State, DSCC->getDeclStmt(), LCtx, LValue);
- return std::make_pair(State, LValue);
+ return makeZeroElementRegion(State, State->getLValue(Var, LCtx), Ty,
+ CallOpts.IsArrayCtorOrDtor);
}
case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind:
case ConstructionContext::SimpleConstructorInitializerKind: {
@@ -139,8 +134,7 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
const auto *Init = ICC->getCXXCtorInitializer();
assert(Init->isAnyMemberInitializer());
const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
- Loc ThisPtr =
- SVB.getCXXThis(CurCtor, LCtx->getStackFrame());
+ Loc ThisPtr = SVB.getCXXThis(CurCtor, LCtx->getStackFrame());
SVal ThisVal = State->getSVal(ThisPtr);
const ValueDecl *Field;
@@ -154,10 +148,8 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
}
QualType Ty = Field->getType();
- FieldVal = makeZeroElementRegion(State, FieldVal, Ty,
- CallOpts.IsArrayCtorOrDtor);
- State = addObjectUnderConstruction(State, Init, LCtx, FieldVal);
- return std::make_pair(State, FieldVal);
+ return makeZeroElementRegion(State, FieldVal, Ty,
+ CallOpts.IsArrayCtorOrDtor);
}
case ConstructionContext::NewAllocatedObjectKind: {
if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator) {
@@ -170,11 +162,10 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
// TODO: In fact, we need to call the constructor for every
// allocated element, not just the first one!
CallOpts.IsArrayCtorOrDtor = true;
- return std::make_pair(
- State, loc::MemRegionVal(getStoreManager().GetElementZeroRegion(
- MR, NE->getType()->getPointeeType())));
+ return loc::MemRegionVal(getStoreManager().GetElementZeroRegion(
+ MR, NE->getType()->getPointeeType()));
}
- return std::make_pair(State, V);
+ return V;
}
// TODO: Detect when the allocator returns a null pointer.
// Constructor shall not be called in this case.
@@ -202,7 +193,7 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
CallerLCtx = CallerLCtx->getParent();
assert(!isa<BlockInvocationContext>(CallerLCtx));
}
- return prepareForObjectConstruction(
+ return computeObjectUnderConstruction(
cast<Expr>(SFC->getCallSite()), State, CallerLCtx,
RTC->getConstructionContext(), CallOpts);
} else {
@@ -223,64 +214,46 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
assert(RetE && "Void returns should not have a construction context");
QualType ReturnTy = RetE->getType();
QualType RegionTy = ACtx.getPointerType(ReturnTy);
- SVal V = SVB.conjureSymbolVal(&TopLevelSymRegionTag, RetE, SFC,
- RegionTy, currBldrCtx->blockCount());
- return std::make_pair(State, V);
+ return SVB.conjureSymbolVal(&TopLevelSymRegionTag, RetE, SFC, RegionTy,
+ currBldrCtx->blockCount());
}
llvm_unreachable("Unhandled return value construction context!");
}
case ConstructionContext::ElidedTemporaryObjectKind: {
assert(AMgr.getAnalyzerOptions().ShouldElideConstructors);
const auto *TCC = cast<ElidedTemporaryObjectConstructionContext>(CC);
- const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();
- const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();
- const CXXConstructExpr *CE = TCC->getConstructorAfterElision();
// Support pre-C++17 copy elision. We'll have the elidable copy
// constructor in the AST and in the CFG, but we'll skip it
// and construct directly into the final object. This call
// also sets the CallOpts flags for us.
- SVal V;
// If the elided copy/move constructor is not supported, there's still
// benefit in trying to model the non-elided constructor.
// Stash our state before trying to elide, as it'll get overwritten.
ProgramStateRef PreElideState = State;
EvalCallOptions PreElideCallOpts = CallOpts;
- std::tie(State, V) = prepareForObjectConstruction(
- CE, State, LCtx, TCC->getConstructionContextAfterElision(), CallOpts);
+ SVal V = computeObjectUnderConstruction(
+ TCC->getConstructorAfterElision(), State, LCtx,
+ TCC->getConstructionContextAfterElision(), CallOpts);
// FIXME: This definition of "copy elision has not failed" is unreliable.
// It doesn't indicate that the constructor will actually be inlined
- // later; it is still up to evalCall() to decide.
- if (!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) {
- // Remember that we've elided the constructor.
- State = addObjectUnderConstruction(State, CE, LCtx, V);
-
- // Remember that we've elided the destructor.
- if (BTE)
- State = elideDestructor(State, BTE, LCtx);
-
- // Instead of materialization, shamelessly return
- // the final object destination.
- if (MTE)
- State = addObjectUnderConstruction(State, MTE, LCtx, V);
-
- return std::make_pair(State, V);
- } else {
- // Copy elision failed. Revert the changes and proceed as if we have
- // a simple temporary.
- State = PreElideState;
- CallOpts = PreElideCallOpts;
- }
+ // later; this is still up to evalCall() to decide.
+ if (!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion)
+ return V;
+
+ // Copy elision failed. Revert the changes and proceed as if we have
+ // a simple temporary.
+ CallOpts = PreElideCallOpts;
+ CallOpts.IsElidableCtorThatHasNotBeenElided = true;
LLVM_FALLTHROUGH;
}
case ConstructionContext::SimpleTemporaryObjectKind: {
const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
- const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();
const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();
- SVal V = UnknownVal();
+ CallOpts.IsTemporaryCtorOrDtor = true;
if (MTE) {
if (const ValueDecl *VD = MTE->getExtendingDecl()) {
assert(MTE->getStorageDuration() != SD_FullExpression);
@@ -296,20 +269,10 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
if (MTE->getStorageDuration() == SD_Static ||
MTE->getStorageDuration() == SD_Thread)
- V = loc::MemRegionVal(MRMgr.getCXXStaticTempObjectRegion(E));
+ return loc::MemRegionVal(MRMgr.getCXXStaticTempObjectRegion(E));
}
- if (V.isUnknown())
- V = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
-
- if (BTE)
- State = addObjectUnderConstruction(State, BTE, LCtx, V);
-
- if (MTE)
- State = addObjectUnderConstruction(State, MTE, LCtx, V);
-
- CallOpts.IsTemporaryCtorOrDtor = true;
- return std::make_pair(State, V);
+ return loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
}
case ConstructionContext::ArgumentKind: {
// Arguments are technically temporaries.
@@ -318,10 +281,8 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
const auto *ACC = cast<ArgumentConstructionContext>(CC);
const Expr *E = ACC->getCallLikeExpr();
unsigned Idx = ACC->getIndex();
- const CXXBindTemporaryExpr *BTE = ACC->getCXXBindTemporaryExpr();
CallEventManager &CEMgr = getStateManager().getCallEventManager();
- SVal V = UnknownVal();
auto getArgLoc = [&](CallEventRef<> Caller) -> Optional<SVal> {
const LocationContext *FutureSFC =
Caller->getCalleeStackFrame(currBldrCtx->blockCount());
@@ -342,76 +303,171 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
// Operator arguments do not correspond to operator parameters
// because this-argument is implemented as a normal argument in
// operator call expressions but not in operator declarations.
- const VarRegion *VR = Caller->getParameterLocation(
+ const TypedValueRegion *TVR = Caller->getParameterLocation(
*Caller->getAdjustedParameterIndex(Idx), currBldrCtx->blockCount());
- if (!VR)
+ if (!TVR)
return None;
- return loc::MemRegionVal(VR);
+ return loc::MemRegionVal(TVR);
};
if (const auto *CE = dyn_cast<CallExpr>(E)) {
CallEventRef<> Caller = CEMgr.getSimpleCall(CE, State, LCtx);
- if (auto OptV = getArgLoc(Caller))
- V = *OptV;
+ if (Optional<SVal> V = getArgLoc(Caller))
+ return *V;
else
break;
- State = addObjectUnderConstruction(State, {CE, Idx}, LCtx, V);
} else if (const auto *CCE = dyn_cast<CXXConstructExpr>(E)) {
// Don't bother figuring out the target region for the future
// constructor because we won't need it.
CallEventRef<> Caller =
CEMgr.getCXXConstructorCall(CCE, /*Target=*/nullptr, State, LCtx);
- if (auto OptV = getArgLoc(Caller))
- V = *OptV;
+ if (Optional<SVal> V = getArgLoc(Caller))
+ return *V;
else
break;
- State = addObjectUnderConstruction(State, {CCE, Idx}, LCtx, V);
} else if (const auto *ME = dyn_cast<ObjCMessageExpr>(E)) {
CallEventRef<> Caller = CEMgr.getObjCMethodCall(ME, State, LCtx);
- if (auto OptV = getArgLoc(Caller))
- V = *OptV;
+ if (Optional<SVal> V = getArgLoc(Caller))
+ return *V;
else
break;
- State = addObjectUnderConstruction(State, {ME, Idx}, LCtx, V);
+ }
+ }
+ } // switch (CC->getKind())
+ }
+
+ // If we couldn't find an existing region to construct into, assume we're
+ // constructing a temporary. Notify the caller of our failure.
+ CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
+ return loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
+}
+
+ProgramStateRef ExprEngine::updateObjectsUnderConstruction(
+ SVal V, const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
+ const ConstructionContext *CC, const EvalCallOptions &CallOpts) {
+ if (CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) {
+ // Sounds like we failed to find the target region and therefore
+ // copy elision failed. There's nothing we can do about it here.
+ return State;
+ }
+
+ // See if we're constructing an existing region by looking at the
+ // current construction context.
+ assert(CC && "Computed target region without construction context?");
+ switch (CC->getKind()) {
+ case ConstructionContext::CXX17ElidedCopyVariableKind:
+ case ConstructionContext::SimpleVariableKind: {
+ const auto *DSCC = cast<VariableConstructionContext>(CC);
+ return addObjectUnderConstruction(State, DSCC->getDeclStmt(), LCtx, V);
+ }
+ case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind:
+ case ConstructionContext::SimpleConstructorInitializerKind: {
+ const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
+ return addObjectUnderConstruction(State, ICC->getCXXCtorInitializer(),
+ LCtx, V);
+ }
+ case ConstructionContext::NewAllocatedObjectKind: {
+ return State;
+ }
+ case ConstructionContext::SimpleReturnedValueKind:
+ case ConstructionContext::CXX17ElidedCopyReturnedValueKind: {
+ const StackFrameContext *SFC = LCtx->getStackFrame();
+ const LocationContext *CallerLCtx = SFC->getParent();
+ if (!CallerLCtx) {
+ // No extra work is necessary in top frame.
+ return State;
}
- assert(!V.isUnknown());
+ auto RTC = (*SFC->getCallSiteBlock())[SFC->getIndex()]
+ .getAs<CFGCXXRecordTypedCall>();
+ assert(RTC && "Could not have had a target region without it");
+ if (isa<BlockInvocationContext>(CallerLCtx)) {
+ // Unwrap block invocation contexts. They're mostly part of
+ // the current stack frame.
+ CallerLCtx = CallerLCtx->getParent();
+ assert(!isa<BlockInvocationContext>(CallerLCtx));
+ }
- if (BTE)
+ return updateObjectsUnderConstruction(V,
+ cast<Expr>(SFC->getCallSite()), State, CallerLCtx,
+ RTC->getConstructionContext(), CallOpts);
+ }
+ case ConstructionContext::ElidedTemporaryObjectKind: {
+ assert(AMgr.getAnalyzerOptions().ShouldElideConstructors);
+ if (!CallOpts.IsElidableCtorThatHasNotBeenElided) {
+ const auto *TCC = cast<ElidedTemporaryObjectConstructionContext>(CC);
+ State = updateObjectsUnderConstruction(
+ V, TCC->getConstructorAfterElision(), State, LCtx,
+ TCC->getConstructionContextAfterElision(), CallOpts);
+
+ // Remember that we've elided the constructor.
+ State = addObjectUnderConstruction(
+ State, TCC->getConstructorAfterElision(), LCtx, V);
+
+ // Remember that we've elided the destructor.
+ if (const auto *BTE = TCC->getCXXBindTemporaryExpr())
+ State = elideDestructor(State, BTE, LCtx);
+
+ // Instead of materialization, shamelessly return
+ // the final object destination.
+ if (const auto *MTE = TCC->getMaterializedTemporaryExpr())
+ State = addObjectUnderConstruction(State, MTE, LCtx, V);
+
+ return State;
+ }
+ // If we decided not to elide the constructor, proceed as if
+ // it's a simple temporary.
+ LLVM_FALLTHROUGH;
+ }
+ case ConstructionContext::SimpleTemporaryObjectKind: {
+ const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
+ if (const auto *BTE = TCC->getCXXBindTemporaryExpr())
State = addObjectUnderConstruction(State, BTE, LCtx, V);
- return std::make_pair(State, V);
+ if (const auto *MTE = TCC->getMaterializedTemporaryExpr())
+ State = addObjectUnderConstruction(State, MTE, LCtx, V);
+
+ return State;
}
+ case ConstructionContext::ArgumentKind: {
+ const auto *ACC = cast<ArgumentConstructionContext>(CC);
+ if (const auto *BTE = ACC->getCXXBindTemporaryExpr())
+ State = addObjectUnderConstruction(State, BTE, LCtx, V);
+
+ return addObjectUnderConstruction(
+ State, {ACC->getCallLikeExpr(), ACC->getIndex()}, LCtx, V);
}
}
- // If we couldn't find an existing region to construct into, assume we're
- // constructing a temporary. Notify the caller of our failure.
- CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
- return std::make_pair(
- State, loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx)));
+ llvm_unreachable("Unhandled construction context!");
}
-void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
- ExplodedNode *Pred,
- ExplodedNodeSet &destNodes) {
+void ExprEngine::handleConstructor(const Expr *E,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &destNodes) {
+ const auto *CE = dyn_cast<CXXConstructExpr>(E);
+ const auto *CIE = dyn_cast<CXXInheritedCtorInitExpr>(E);
+ assert(CE || CIE);
+
const LocationContext *LCtx = Pred->getLocationContext();
ProgramStateRef State = Pred->getState();
SVal Target = UnknownVal();
- if (Optional<SVal> ElidedTarget =
- getObjectUnderConstruction(State, CE, LCtx)) {
- // We've previously modeled an elidable constructor by pretending that it in
- // fact constructs into the correct target. This constructor can therefore
- // be skipped.
- Target = *ElidedTarget;
- StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx);
- State = finishObjectConstruction(State, CE, LCtx);
- if (auto L = Target.getAs<Loc>())
- State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType()));
- Bldr.generateNode(CE, Pred, State);
- return;
+ if (CE) {
+ if (Optional<SVal> ElidedTarget =
+ getObjectUnderConstruction(State, CE, LCtx)) {
+ // We've previously modeled an elidable constructor by pretending that it
+ // in fact constructs into the correct target. This constructor can
+ // therefore be skipped.
+ Target = *ElidedTarget;
+ StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx);
+ State = finishObjectConstruction(State, CE, LCtx);
+ if (auto L = Target.getAs<Loc>())
+ State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType()));
+ Bldr.generateNode(CE, Pred, State);
+ return;
+ }
}
// FIXME: Handle arrays, which run the same constructor for every element.
@@ -423,10 +479,16 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
assert(C || getCurrentCFGElement().getAs<CFGStmt>());
const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr;
- switch (CE->getConstructionKind()) {
+ const CXXConstructExpr::ConstructionKind CK =
+ CE ? CE->getConstructionKind() : CIE->getConstructionKind();
+ switch (CK) {
case CXXConstructExpr::CK_Complete: {
+ // Inherited constructors are always base class constructors.
+ assert(CE && !CIE && "A complete constructor is inherited?!");
+
+ // The target region is found from construction context.
std::tie(State, Target) =
- prepareForObjectConstruction(CE, State, LCtx, CC, CallOpts);
+ handleConstructionContext(CE, State, LCtx, CC, CallOpts);
break;
}
case CXXConstructExpr::CK_VirtualBase: {
@@ -455,9 +517,9 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
// FIXME: Instead of relying on the ParentMap, we should have the
// trigger-statement (InitListExpr in this case) passed down from CFG or
// otherwise always available during construction.
- if (dyn_cast_or_null<InitListExpr>(LCtx->getParentMap().getParent(CE))) {
+ if (dyn_cast_or_null<InitListExpr>(LCtx->getParentMap().getParent(E))) {
MemRegionManager &MRMgr = getSValBuilder().getRegionManager();
- Target = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(CE, LCtx));
+ Target = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
break;
}
@@ -468,14 +530,13 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
LCtx->getStackFrame());
SVal ThisVal = State->getSVal(ThisPtr);
- if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) {
+ if (CK == CXXConstructExpr::CK_Delegating) {
Target = ThisVal;
} else {
// Cast to the base type.
- bool IsVirtual =
- (CE->getConstructionKind() == CXXConstructExpr::CK_VirtualBase);
- SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, CE->getType(),
- IsVirtual);
+ bool IsVirtual = (CK == CXXConstructExpr::CK_VirtualBase);
+ SVal BaseVal =
+ getStoreManager().evalDerivedToBase(ThisVal, E->getType(), IsVirtual);
Target = BaseVal;
}
break;
@@ -487,23 +548,27 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
"Prepare for object construction");
ExplodedNodeSet DstPrepare;
StmtNodeBuilder BldrPrepare(Pred, DstPrepare, *currBldrCtx);
- BldrPrepare.generateNode(CE, Pred, State, &T, ProgramPoint::PreStmtKind);
+ BldrPrepare.generateNode(E, Pred, State, &T, ProgramPoint::PreStmtKind);
assert(DstPrepare.size() <= 1);
if (DstPrepare.size() == 0)
return;
Pred = *BldrPrepare.begin();
}
+ const MemRegion *TargetRegion = Target.getAsRegion();
CallEventManager &CEMgr = getStateManager().getCallEventManager();
- CallEventRef<CXXConstructorCall> Call =
- CEMgr.getCXXConstructorCall(CE, Target.getAsRegion(), State, LCtx);
+ CallEventRef<> Call =
+ CIE ? (CallEventRef<>)CEMgr.getCXXInheritedConstructorCall(
+ CIE, TargetRegion, State, LCtx)
+ : (CallEventRef<>)CEMgr.getCXXConstructorCall(
+ CE, TargetRegion, State, LCtx);
ExplodedNodeSet DstPreVisit;
- getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this);
+ getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, E, *this);
- // FIXME: Is it possible and/or useful to do this before PreStmt?
ExplodedNodeSet PreInitialized;
- {
+ if (CE) {
+ // FIXME: Is it possible and/or useful to do this before PreStmt?
StmtNodeBuilder Bldr(DstPreVisit, PreInitialized, *currBldrCtx);
for (ExplodedNodeSet::iterator I = DstPreVisit.begin(),
E = DstPreVisit.end();
@@ -528,6 +593,8 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
Bldr.generateNode(CE, *I, State, /*tag=*/nullptr,
ProgramPoint::PreStmtKind);
}
+ } else {
+ PreInitialized = DstPreVisit;
}
ExplodedNodeSet DstPreCall;
@@ -537,7 +604,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
ExplodedNodeSet DstEvaluated;
StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx);
- if (CE->getConstructor()->isTrivial() &&
+ if (CE && CE->getConstructor()->isTrivial() &&
CE->getConstructor()->isCopyOrMoveConstructor() &&
!CallOpts.IsArrayCtorOrDtor) {
// FIXME: Handle other kinds of trivial constructors as well.
@@ -548,7 +615,8 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
} else {
for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
I != E; ++I)
- defaultEvalCall(Bldr, *I, *Call, CallOpts);
+ getCheckerManager().runCheckersForEvalCall(DstEvaluated, *I, *Call, *this,
+ CallOpts);
}
// If the CFG was constructed without elements for temporary destructors
@@ -560,9 +628,10 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
// paths when no-return temporary destructors are used for assertions.
const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext();
if (!ADC->getCFGBuildOptions().AddTemporaryDtors) {
- const MemRegion *Target = Call->getCXXThisVal().getAsRegion();
- if (Target && isa<CXXTempObjectRegion>(Target) &&
- Call->getDecl()->getParent()->isAnyDestructorNoReturn()) {
+ if (llvm::isa_and_nonnull<CXXTempObjectRegion>(TargetRegion) &&
+ cast<CXXConstructorDecl>(Call->getDecl())
+ ->getParent()
+ ->isAnyDestructorNoReturn()) {
// If we've inlined the constructor, then DstEvaluated would be empty.
// In this case we still want a sink, which could be implemented
@@ -575,7 +644,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
"We should not have inlined this constructor!");
for (ExplodedNode *N : DstEvaluated) {
- Bldr.generateSink(CE, N, N->getState());
+ Bldr.generateSink(E, N, N->getState());
}
// There is no need to run the PostCall and PostStmt checker
@@ -586,7 +655,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
}
ExplodedNodeSet DstPostArgumentCleanup;
- for (auto I : DstEvaluated)
+ for (ExplodedNode *I : DstEvaluated)
finishArgumentConstruction(DstPostArgumentCleanup, I, *Call);
// If there were other constructors called for object-type arguments
@@ -595,7 +664,19 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
getCheckerManager().runCheckersForPostCall(DstPostCall,
DstPostArgumentCleanup,
*Call, *this);
- getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this);
+ getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, E, *this);
+}
+
+void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ handleConstructor(CE, Pred, Dst);
+}
+
+void ExprEngine::VisitCXXInheritedCtorInitExpr(
+ const CXXInheritedCtorInitExpr *CE, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ handleConstructor(CE, Pred, Dst);
}
void ExprEngine::VisitCXXDestructor(QualType ObjectType,
@@ -683,7 +764,7 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
ExplodedNodeSet DstPostCall;
StmtNodeBuilder CallBldr(DstPreCall, DstPostCall, *currBldrCtx);
- for (auto I : DstPreCall) {
+ for (ExplodedNode *I : DstPreCall) {
// FIXME: Provide evalCall for checkers?
defaultEvalCall(CallBldr, I, *Call);
}
@@ -693,7 +774,7 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
// CXXNewExpr gets processed.
ExplodedNodeSet DstPostValue;
StmtNodeBuilder ValueBldr(DstPostCall, DstPostValue, *currBldrCtx);
- for (auto I : DstPostCall) {
+ for (ExplodedNode *I : DstPostCall) {
// FIXME: Because CNE serves as the "call site" for the allocator (due to
// lack of a better expression in the AST), the conjured return value symbol
// is going to be of the same type (C++ object pointer type). Technically
@@ -727,10 +808,8 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
ExplodedNodeSet DstPostPostCallCallback;
getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback,
DstPostValue, *Call, *this);
- for (auto I : DstPostPostCallCallback) {
- getCheckerManager().runCheckersForNewAllocator(
- CNE, *getObjectUnderConstruction(I->getState(), CNE, LCtx), Dst, I,
- *this);
+ for (ExplodedNode *I : DstPostPostCallCallback) {
+ getCheckerManager().runCheckersForNewAllocator(*Call, Dst, I, *this);
}
}
@@ -846,13 +925,18 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE,
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
- StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
- ProgramStateRef state = Pred->getState();
- Bldr.generateNode(CDE, Pred, state);
+
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<CXXDeallocatorCall> Call = CEMgr.getCXXDeallocatorCall(
+ CDE, Pred->getState(), Pred->getLocationContext());
+
+ ExplodedNodeSet DstPreCall;
+ getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, *Call, *this);
+
+ getCheckerManager().runCheckersForPostCall(Dst, DstPreCall, *Call, *this);
}
-void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS,
- ExplodedNode *Pred,
+void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
const VarDecl *VD = CS->getExceptionDecl();
if (!VD) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 01a371e664b2..52ba17d59ae0 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -10,17 +10,19 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/AST/Decl.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "PrettyStackTraceLocationContext.h"
#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/ConstructionContext.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/Statistic.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
#include "llvm/Support/SaveAndRestore.h"
using namespace clang;
@@ -324,17 +326,14 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState);
ExplodedNodeSet DstPostCall;
- if (const CXXNewExpr *CNE = dyn_cast_or_null<CXXNewExpr>(CE)) {
+ if (llvm::isa_and_nonnull<CXXNewExpr>(CE)) {
ExplodedNodeSet DstPostPostCallCallback;
getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback,
CEENode, *UpdatedCall, *this,
/*wasInlined=*/true);
- for (auto I : DstPostPostCallCallback) {
+ for (ExplodedNode *I : DstPostPostCallCallback) {
getCheckerManager().runCheckersForNewAllocator(
- CNE,
- *getObjectUnderConstruction(I->getState(), CNE,
- calleeCtx->getParent()),
- DstPostCall, I, *this,
+ cast<CXXAllocatorCall>(*UpdatedCall), DstPostCall, I, *this,
/*wasInlined=*/true);
}
} else {
@@ -585,12 +584,12 @@ void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
// defaultEvalCall if all of them fail.
ExplodedNodeSet dstCallEvaluated;
getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, dstPreVisit,
- Call, *this);
+ Call, *this, EvalCallOptions());
// If there were other constructors called for object-type arguments
// of this call, clean them up.
ExplodedNodeSet dstArgumentCleanup;
- for (auto I : dstCallEvaluated)
+ for (ExplodedNode *I : dstCallEvaluated)
finishArgumentConstruction(dstArgumentCleanup, I, Call);
ExplodedNodeSet dstPostCall;
@@ -604,7 +603,7 @@ void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
// Run pointerEscape callback with the newly conjured symbols.
SmallVector<std::pair<SVal, SVal>, 8> Escaped;
- for (auto I : dstPostCall) {
+ for (ExplodedNode *I : dstPostCall) {
NodeBuilder B(I, Dst, *currBldrCtx);
ProgramStateRef State = I->getState();
Escaped.clear();
@@ -668,8 +667,8 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
assert(RTC->getStmt() == Call.getOriginExpr());
EvalCallOptions CallOpts; // FIXME: We won't really need those.
std::tie(State, Target) =
- prepareForObjectConstruction(Call.getOriginExpr(), State, LCtx,
- RTC->getConstructionContext(), CallOpts);
+ handleConstructionContext(Call.getOriginExpr(), State, LCtx,
+ RTC->getConstructionContext(), CallOpts);
const MemRegion *TargetR = Target.getAsRegion();
assert(TargetR);
// Invalidate the region so that it didn't look uninitialized. If this is
@@ -718,7 +717,7 @@ void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr,
ExprEngine::CallInlinePolicy
ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
AnalyzerOptions &Opts,
- const ExprEngine::EvalCallOptions &CallOpts) {
+ const EvalCallOptions &CallOpts) {
const LocationContext *CurLC = Pred->getLocationContext();
const StackFrameContext *CallerSFC = CurLC->getStackFrame();
switch (Call.getKind()) {
@@ -742,7 +741,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
const ConstructionContext *CC = CCE ? CCE->getConstructionContext()
: nullptr;
- if (CC && isa<NewAllocatedObjectConstructionContext>(CC) &&
+ if (llvm::isa_and_nonnull<NewAllocatedObjectConstructionContext>(CC) &&
!Opts.MayInlineCXXAllocator)
return CIP_DisallowedOnce;
@@ -789,6 +788,11 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
break;
}
+ case CE_CXXInheritedConstructor: {
+ // This doesn't really increase the cost of inlining ever, because
+ // the stack frame of the inherited constructor is trivial.
+ return CIP_Allowed;
+ }
case CE_CXXDestructor: {
if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors))
return CIP_DisallowedAlways;
@@ -814,6 +818,8 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
return CIP_DisallowedOnce;
break;
}
+ case CE_CXXDeallocator:
+ LLVM_FALLTHROUGH;
case CE_CXXAllocator:
if (Opts.MayInlineCXXAllocator)
break;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
index 002b6070ddcd..bc7c41d039c4 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -66,11 +66,9 @@ class HTMLDiagnostics : public PathDiagnosticConsumer {
const bool SupportsCrossFileDiagnostics;
public:
- HTMLDiagnostics(AnalyzerOptions &AnalyzerOpts,
- const std::string& prefix,
- const Preprocessor &pp,
- bool supportsMultipleFiles)
- : Directory(prefix), PP(pp), AnalyzerOpts(AnalyzerOpts),
+ HTMLDiagnostics(AnalyzerOptions &AnalyzerOpts, const std::string &OutputDir,
+ const Preprocessor &pp, bool supportsMultipleFiles)
+ : Directory(OutputDir), PP(pp), AnalyzerOpts(AnalyzerOpts),
SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
~HTMLDiagnostics() override { FlushDiagnostics(nullptr); }
@@ -136,16 +134,45 @@ private:
void ento::createHTMLDiagnosticConsumer(
AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
- const std::string &prefix, const Preprocessor &PP,
- const cross_tu::CrossTranslationUnitContext &) {
- C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP, true));
+ const std::string &OutputDir, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+
+ // FIXME: HTML is currently our default output type, but if the output
+ // directory isn't specified, it acts like if it was in the minimal text
+ // output mode. This doesn't make much sense, we should have the minimal text
+ // as our default. In the case of backward compatibility concerns, this could
+ // be preserved with -analyzer-config-compatibility-mode=true.
+ createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputDir, PP, CTU);
+
+ // TODO: Emit an error here.
+ if (OutputDir.empty())
+ return;
+
+ C.push_back(new HTMLDiagnostics(AnalyzerOpts, OutputDir, PP, true));
}
void ento::createHTMLSingleFileDiagnosticConsumer(
AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ const std::string &OutputDir, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+
+ // TODO: Emit an error here.
+ if (OutputDir.empty())
+ return;
+
+ C.push_back(new HTMLDiagnostics(AnalyzerOpts, OutputDir, PP, false));
+ createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputDir, PP, CTU);
+}
+
+void ento::createPlistHTMLDiagnosticConsumer(
+ AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
const std::string &prefix, const Preprocessor &PP,
- const cross_tu::CrossTranslationUnitContext &) {
- C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP, false));
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+ createHTMLDiagnosticConsumer(
+ AnalyzerOpts, C, std::string(llvm::sys::path::parent_path(prefix)), PP,
+ CTU);
+ createPlistMultiFileDiagnosticConsumer(AnalyzerOpts, C, prefix, PP, CTU);
+ createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, prefix, PP, CTU);
}
//===----------------------------------------------------------------------===//
@@ -1043,8 +1070,13 @@ StringRef HTMLDiagnostics::generateKeyboardNavigationJavascript() {
<script type='text/javascript'>
var digitMatcher = new RegExp("[0-9]+");
+var querySelectorAllArray = function(selector) {
+ return Array.prototype.slice.call(
+ document.querySelectorAll(selector));
+}
+
document.addEventListener("DOMContentLoaded", function() {
- document.querySelectorAll(".PathNav > a").forEach(
+ querySelectorAllArray(".PathNav > a").forEach(
function(currentValue, currentIndex) {
var hrefValue = currentValue.getAttribute("href");
currentValue.onclick = function() {
@@ -1064,7 +1096,7 @@ var findNum = function() {
};
var scrollTo = function(el) {
- document.querySelectorAll(".selected").forEach(function(s) {
+ querySelectorAllArray(".selected").forEach(function(s) {
s.classList.remove("selected");
});
el.classList.add("selected");
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
index 1a09a521f116..dc268e562237 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
@@ -130,10 +130,10 @@ static internal::Matcher<Stmt> hasSuspiciousStmt(StringRef NodeName) {
// Escaping and not known mutation of the loop counter is handled
// by exclusion of assigning and address-of operators and
// pass-by-ref function calls on the loop counter from the body.
- changeIntBoundNode(equalsBoundNode(NodeName)),
- callByRef(equalsBoundNode(NodeName)),
- getAddrTo(equalsBoundNode(NodeName)),
- assignedToRef(equalsBoundNode(NodeName)))));
+ changeIntBoundNode(equalsBoundNode(std::string(NodeName))),
+ callByRef(equalsBoundNode(std::string(NodeName))),
+ getAddrTo(equalsBoundNode(std::string(NodeName))),
+ assignedToRef(equalsBoundNode(std::string(NodeName))))));
}
static internal::Matcher<Stmt> forLoopMatcher() {
@@ -164,6 +164,11 @@ static bool isPossiblyEscaped(const VarDecl *VD, ExplodedNode *N) {
if (VD->hasGlobalStorage())
return true;
+ const bool isParm = isa<ParmVarDecl>(VD);
+ // Reference parameters are assumed as escaped variables.
+ if (isParm && VD->getType()->isReferenceType())
+ return true;
+
while (!N->pred_empty()) {
// FIXME: getStmtForDiagnostics() does nasty things in order to provide
// a valid statement for body farms, do we need this behavior here?
@@ -193,6 +198,11 @@ static bool isPossiblyEscaped(const VarDecl *VD, ExplodedNode *N) {
N = N->getFirstPred();
}
+
+ // Parameter declaration will not be found.
+ if (isParm)
+ return false;
+
llvm_unreachable("Reached root without finding the declaration of VD");
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
index 9a7b1a24b819..47e34dd84b9a 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
@@ -67,8 +67,10 @@ ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState,
}
// References should not be invalidated.
- auto Matches = match(findAll(stmt(hasDescendant(varDecl(hasType(referenceType())).bind(MatchRef)))),
- *LCtx->getDecl()->getBody(), ASTCtx);
+ auto Matches = match(
+ findAll(stmt(hasDescendant(
+ varDecl(hasType(hasCanonicalType(referenceType()))).bind(MatchRef)))),
+ *LCtx->getDecl()->getBody(), ASTCtx);
for (BoundNodes Match : Matches) {
const VarDecl *VD = Match.getNodeAs<VarDecl>(MatchRef);
assert(VD);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
index a10d7e69ad7e..455adf53ac99 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -142,7 +142,7 @@ bool SubRegion::isSubRegionOf(const MemRegion* R) const {
return false;
}
-MemRegionManager* SubRegion::getMemRegionManager() const {
+MemRegionManager &SubRegion::getMemRegionManager() const {
const SubRegion* r = this;
do {
const MemRegion *superRegion = r->getSuperRegion();
@@ -159,62 +159,10 @@ const StackFrameContext *VarRegion::getStackFrame() const {
return SSR ? SSR->getStackFrame() : nullptr;
}
-//===----------------------------------------------------------------------===//
-// Region extents.
-//===----------------------------------------------------------------------===//
-
-DefinedOrUnknownSVal TypedValueRegion::getExtent(SValBuilder &svalBuilder) const {
- ASTContext &Ctx = svalBuilder.getContext();
- QualType T = getDesugaredValueType(Ctx);
-
- if (isa<VariableArrayType>(T))
- return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this));
- if (T->isIncompleteType())
- return UnknownVal();
-
- CharUnits size = Ctx.getTypeSizeInChars(T);
- QualType sizeTy = svalBuilder.getArrayIndexType();
- return svalBuilder.makeIntVal(size.getQuantity(), sizeTy);
-}
-
-DefinedOrUnknownSVal FieldRegion::getExtent(SValBuilder &svalBuilder) const {
- // Force callers to deal with bitfields explicitly.
- if (getDecl()->isBitField())
- return UnknownVal();
-
- DefinedOrUnknownSVal Extent = DeclRegion::getExtent(svalBuilder);
-
- // A zero-length array at the end of a struct often stands for dynamically-
- // allocated extra memory.
- if (Extent.isZeroConstant()) {
- QualType T = getDesugaredValueType(svalBuilder.getContext());
-
- if (isa<ConstantArrayType>(T))
- return UnknownVal();
- }
-
- return Extent;
-}
-
-DefinedOrUnknownSVal AllocaRegion::getExtent(SValBuilder &svalBuilder) const {
- return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this));
-}
-
-DefinedOrUnknownSVal SymbolicRegion::getExtent(SValBuilder &svalBuilder) const {
- return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this));
-}
-
-DefinedOrUnknownSVal StringRegion::getExtent(SValBuilder &svalBuilder) const {
- return svalBuilder.makeIntVal(getStringLiteral()->getByteLength()+1,
- svalBuilder.getArrayIndexType());
-}
-
ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const SubRegion *sReg)
- : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {}
+ : DeclRegion(sReg, ObjCIvarRegionKind), IVD(ivd) {}
-const ObjCIvarDecl *ObjCIvarRegion::getDecl() const {
- return cast<ObjCIvarDecl>(D);
-}
+const ObjCIvarDecl *ObjCIvarRegion::getDecl() const { return IVD; }
QualType ObjCIvarRegion::getValueType() const {
return getDecl()->getType();
@@ -228,6 +176,33 @@ QualType CXXDerivedObjectRegion::getValueType() const {
return QualType(getDecl()->getTypeForDecl(), 0);
}
+QualType ParamVarRegion::getValueType() const {
+ assert(getDecl() &&
+ "`ParamVarRegion` support functions without `Decl` not implemented"
+ " yet.");
+ return getDecl()->getType();
+}
+
+const ParmVarDecl *ParamVarRegion::getDecl() const {
+ const Decl *D = getStackFrame()->getDecl();
+
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ assert(Index < FD->param_size());
+ return FD->parameters()[Index];
+ } else if (const auto *BD = dyn_cast<BlockDecl>(D)) {
+ assert(Index < BD->param_size());
+ return BD->parameters()[Index];
+ } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
+ assert(Index < MD->param_size());
+ return MD->parameters()[Index];
+ } else if (const auto *CD = dyn_cast<CXXConstructorDecl>(D)) {
+ assert(Index < CD->param_size());
+ return CD->parameters()[Index];
+ } else {
+ llvm_unreachable("Unexpected Decl kind!");
+ }
+}
+
//===----------------------------------------------------------------------===//
// FoldingSet profiling.
//===----------------------------------------------------------------------===//
@@ -299,25 +274,44 @@ void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const {
CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion);
}
+void FieldRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ProfileRegion(ID, getDecl(), superRegion);
+}
+
void ObjCIvarRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
const ObjCIvarDecl *ivd,
const MemRegion* superRegion) {
- DeclRegion::ProfileRegion(ID, ivd, superRegion, ObjCIvarRegionKind);
+ ID.AddInteger(static_cast<unsigned>(ObjCIvarRegionKind));
+ ID.AddPointer(ivd);
+ ID.AddPointer(superRegion);
}
-void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D,
- const MemRegion* superRegion, Kind k) {
- ID.AddInteger(static_cast<unsigned>(k));
- ID.AddPointer(D);
+void ObjCIvarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ProfileRegion(ID, getDecl(), superRegion);
+}
+
+void NonParamVarRegion::ProfileRegion(llvm::FoldingSetNodeID &ID,
+ const VarDecl *VD,
+ const MemRegion *superRegion) {
+ ID.AddInteger(static_cast<unsigned>(NonParamVarRegionKind));
+ ID.AddPointer(VD);
ID.AddPointer(superRegion);
}
-void DeclRegion::Profile(llvm::FoldingSetNodeID& ID) const {
- DeclRegion::ProfileRegion(ID, D, superRegion, getKind());
+void NonParamVarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ProfileRegion(ID, getDecl(), superRegion);
}
-void VarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
- VarRegion::ProfileRegion(ID, getDecl(), superRegion);
+void ParamVarRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, const Expr *OE,
+ unsigned Idx, const MemRegion *SReg) {
+ ID.AddInteger(static_cast<unsigned>(ParamVarRegionKind));
+ ID.AddPointer(OE);
+ ID.AddInteger(Idx);
+ ID.AddPointer(SReg);
+}
+
+void ParamVarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ProfileRegion(ID, getOriginExpr(), getIndex(), superRegion);
}
void SymbolicRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, SymbolRef sym,
@@ -529,12 +523,11 @@ void SymbolicRegion::dumpToStream(raw_ostream &os) const {
os << "SymRegion{" << sym << '}';
}
-void VarRegion::dumpToStream(raw_ostream &os) const {
- const auto *VD = cast<VarDecl>(D);
+void NonParamVarRegion::dumpToStream(raw_ostream &os) const {
if (const IdentifierInfo *ID = VD->getIdentifier())
os << ID->getName();
else
- os << "VarRegion{D" << VD->getID() << '}';
+ os << "NonParamVarRegion{D" << VD->getID() << '}';
}
LLVM_DUMP_METHOD void RegionRawOffset::dump() const {
@@ -581,6 +574,18 @@ void StackLocalsSpaceRegion::dumpToStream(raw_ostream &os) const {
os << "StackLocalsSpaceRegion";
}
+void ParamVarRegion::dumpToStream(raw_ostream &os) const {
+ const ParmVarDecl *PVD = getDecl();
+ assert(PVD &&
+ "`ParamVarRegion` support functions without `Decl` not implemented"
+ " yet.");
+ if (const IdentifierInfo *ID = PVD->getIdentifier()) {
+ os << ID->getName();
+ } else {
+ os << "ParamVarRegion{P" << PVD->getID() << '}';
+ }
+}
+
bool MemRegion::canPrintPretty() const {
return canPrintPrettyAsExpr();
}
@@ -600,11 +605,18 @@ void MemRegion::printPrettyAsExpr(raw_ostream &) const {
llvm_unreachable("This region cannot be printed pretty.");
}
-bool VarRegion::canPrintPrettyAsExpr() const {
- return true;
+bool NonParamVarRegion::canPrintPrettyAsExpr() const { return true; }
+
+void NonParamVarRegion::printPrettyAsExpr(raw_ostream &os) const {
+ os << getDecl()->getName();
}
-void VarRegion::printPrettyAsExpr(raw_ostream &os) const {
+bool ParamVarRegion::canPrintPrettyAsExpr() const { return true; }
+
+void ParamVarRegion::printPrettyAsExpr(raw_ostream &os) const {
+ assert(getDecl() &&
+ "`ParamVarRegion` support functions without `Decl` not implemented"
+ " yet.");
os << getDecl()->getName();
}
@@ -717,11 +729,79 @@ SourceRange MemRegion::sourceRange() const {
// MemRegionManager methods.
//===----------------------------------------------------------------------===//
+static DefinedOrUnknownSVal getTypeSize(QualType Ty, ASTContext &Ctx,
+ SValBuilder &SVB) {
+ CharUnits Size = Ctx.getTypeSizeInChars(Ty);
+ QualType SizeTy = SVB.getArrayIndexType();
+ return SVB.makeIntVal(Size.getQuantity(), SizeTy);
+}
+
+DefinedOrUnknownSVal MemRegionManager::getStaticSize(const MemRegion *MR,
+ SValBuilder &SVB) const {
+ const auto *SR = cast<SubRegion>(MR);
+ SymbolManager &SymMgr = SVB.getSymbolManager();
+
+ switch (SR->getKind()) {
+ case MemRegion::AllocaRegionKind:
+ case MemRegion::SymbolicRegionKind:
+ return nonloc::SymbolVal(SymMgr.getExtentSymbol(SR));
+ case MemRegion::StringRegionKind:
+ return SVB.makeIntVal(
+ cast<StringRegion>(SR)->getStringLiteral()->getByteLength() + 1,
+ SVB.getArrayIndexType());
+ case MemRegion::CompoundLiteralRegionKind:
+ case MemRegion::CXXBaseObjectRegionKind:
+ case MemRegion::CXXDerivedObjectRegionKind:
+ case MemRegion::CXXTempObjectRegionKind:
+ case MemRegion::CXXThisRegionKind:
+ case MemRegion::ObjCIvarRegionKind:
+ case MemRegion::NonParamVarRegionKind:
+ case MemRegion::ParamVarRegionKind:
+ case MemRegion::ElementRegionKind:
+ case MemRegion::ObjCStringRegionKind: {
+ QualType Ty = cast<TypedValueRegion>(SR)->getDesugaredValueType(Ctx);
+ if (isa<VariableArrayType>(Ty))
+ return nonloc::SymbolVal(SymMgr.getExtentSymbol(SR));
+
+ if (Ty->isIncompleteType())
+ return UnknownVal();
+
+ return getTypeSize(Ty, Ctx, SVB);
+ }
+ case MemRegion::FieldRegionKind: {
+ // Force callers to deal with bitfields explicitly.
+ if (cast<FieldRegion>(SR)->getDecl()->isBitField())
+ return UnknownVal();
+
+ QualType Ty = cast<TypedValueRegion>(SR)->getDesugaredValueType(Ctx);
+ DefinedOrUnknownSVal Size = getTypeSize(Ty, Ctx, SVB);
+
+ // A zero-length array at the end of a struct often stands for dynamically
+ // allocated extra memory.
+ if (Size.isZeroConstant()) {
+ if (isa<ConstantArrayType>(Ty))
+ return UnknownVal();
+ }
+
+ return Size;
+ }
+ // FIXME: The following are being used in 'SimpleSValBuilder' and in
+ // 'ArrayBoundChecker::checkLocation' because there is no symbol to
+ // represent the regions more appropriately.
+ case MemRegion::BlockDataRegionKind:
+ case MemRegion::BlockCodeRegionKind:
+ case MemRegion::FunctionCodeRegionKind:
+ return nonloc::SymbolVal(SymMgr.getExtentSymbol(SR));
+ default:
+ llvm_unreachable("Unhandled region");
+ }
+}
+
template <typename REG>
const REG *MemRegionManager::LazyAllocate(REG*& region) {
if (!region) {
region = A.Allocate<REG>();
- new (region) REG(this);
+ new (region) REG(*this);
}
return region;
@@ -746,7 +826,7 @@ MemRegionManager::getStackLocalsRegion(const StackFrameContext *STC) {
return R;
R = A.Allocate<StackLocalsSpaceRegion>();
- new (R) StackLocalsSpaceRegion(this, STC);
+ new (R) StackLocalsSpaceRegion(*this, STC);
return R;
}
@@ -759,7 +839,7 @@ MemRegionManager::getStackArgumentsRegion(const StackFrameContext *STC) {
return R;
R = A.Allocate<StackArgumentsSpaceRegion>();
- new (R) StackArgumentsSpaceRegion(this, STC);
+ new (R) StackArgumentsSpaceRegion(*this, STC);
return R;
}
@@ -781,7 +861,7 @@ const GlobalsSpaceRegion
return R;
R = A.Allocate<StaticGlobalSpaceRegion>();
- new (R) StaticGlobalSpaceRegion(this, CR);
+ new (R) StaticGlobalSpaceRegion(*this, CR);
return R;
}
@@ -825,15 +905,16 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
return SFC;
}
if (const auto *BC = dyn_cast<BlockInvocationContext>(LC)) {
- const auto *BR =
- static_cast<const BlockDataRegion *>(BC->getContextData());
+ const auto *BR = static_cast<const BlockDataRegion *>(BC->getData());
// FIXME: This can be made more efficient.
for (BlockDataRegion::referenced_vars_iterator
I = BR->referenced_vars_begin(),
E = BR->referenced_vars_end(); I != E; ++I) {
- const VarRegion *VR = I.getOriginalRegion();
- if (VR->getDecl() == VD)
- return cast<VarRegion>(I.getCapturedRegion());
+ const TypedValueRegion *OrigR = I.getOriginalRegion();
+ if (const auto *VR = dyn_cast<VarRegion>(OrigR)) {
+ if (VR->getDecl() == VD)
+ return cast<VarRegion>(I.getCapturedRegion());
+ }
}
}
@@ -842,15 +923,37 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
return (const StackFrameContext *)nullptr;
}
-const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
+const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
const LocationContext *LC) {
+ const auto *PVD = dyn_cast<ParmVarDecl>(D);
+ if (PVD) {
+ unsigned Index = PVD->getFunctionScopeIndex();
+ const StackFrameContext *SFC = LC->getStackFrame();
+ const Stmt *CallSite = SFC->getCallSite();
+ if (CallSite) {
+ const Decl *D = SFC->getDecl();
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ if (Index < FD->param_size() && FD->parameters()[Index] == PVD)
+ return getSubRegion<ParamVarRegion>(cast<Expr>(CallSite), Index,
+ getStackArgumentsRegion(SFC));
+ } else if (const auto *BD = dyn_cast<BlockDecl>(D)) {
+ if (Index < BD->param_size() && BD->parameters()[Index] == PVD)
+ return getSubRegion<ParamVarRegion>(cast<Expr>(CallSite), Index,
+ getStackArgumentsRegion(SFC));
+ } else {
+ return getSubRegion<ParamVarRegion>(cast<Expr>(CallSite), Index,
+ getStackArgumentsRegion(SFC));
+ }
+ }
+ }
+
D = D->getCanonicalDecl();
const MemRegion *sReg = nullptr;
if (D->hasGlobalStorage() && !D->isStaticLocal()) {
// First handle the globals defined in system headers.
- if (C.getSourceManager().isInSystemHeader(D->getLocation())) {
+ if (Ctx.getSourceManager().isInSystemHeader(D->getLocation())) {
// Whitelist the system globals which often DO GET modified, assume the
// rest are immutable.
if (D->getName().find("errno") != StringRef::npos)
@@ -914,7 +1017,7 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
T = getContext().getBlockPointerType(T);
const BlockCodeRegion *BTR =
- getBlockCodeRegion(BD, C.getCanonicalType(T),
+ getBlockCodeRegion(BD, Ctx.getCanonicalType(T),
STC->getAnalysisDeclContext());
sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind,
BTR);
@@ -926,13 +1029,23 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
}
}
- return getSubRegion<VarRegion>(D, sReg);
+ return getSubRegion<NonParamVarRegion>(D, sReg);
}
-const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
- const MemRegion *superR) {
+const NonParamVarRegion *
+MemRegionManager::getNonParamVarRegion(const VarDecl *D,
+ const MemRegion *superR) {
D = D->getCanonicalDecl();
- return getSubRegion<VarRegion>(D, superR);
+ return getSubRegion<NonParamVarRegion>(D, superR);
+}
+
+const ParamVarRegion *
+MemRegionManager::getParamVarRegion(const Expr *OriginExpr, unsigned Index,
+ const LocationContext *LC) {
+ const StackFrameContext *SFC = LC->getStackFrame();
+ assert(SFC);
+ return getSubRegion<ParamVarRegion>(OriginExpr, Index,
+ getStackArgumentsRegion(SFC));
}
const BlockDataRegion *
@@ -1325,7 +1438,8 @@ static RegionOffset calculateOffset(const MemRegion *R) {
case MemRegion::CXXThisRegionKind:
case MemRegion::StringRegionKind:
case MemRegion::ObjCStringRegionKind:
- case MemRegion::VarRegionKind:
+ case MemRegion::NonParamVarRegionKind:
+ case MemRegion::ParamVarRegionKind:
case MemRegion::CXXTempObjectRegionKind:
// Usual base regions.
goto Finish;
@@ -1476,12 +1590,12 @@ RegionOffset MemRegion::getAsOffset() const {
std::pair<const VarRegion *, const VarRegion *>
BlockDataRegion::getCaptureRegions(const VarDecl *VD) {
- MemRegionManager &MemMgr = *getMemRegionManager();
+ MemRegionManager &MemMgr = getMemRegionManager();
const VarRegion *VR = nullptr;
const VarRegion *OriginalVR = nullptr;
if (!VD->hasAttr<BlocksAttr>() && VD->hasLocalStorage()) {
- VR = MemMgr.getVarRegion(VD, this);
+ VR = MemMgr.getNonParamVarRegion(VD, this);
OriginalVR = MemMgr.getVarRegion(VD, LC);
}
else {
@@ -1490,7 +1604,7 @@ BlockDataRegion::getCaptureRegions(const VarDecl *VD) {
OriginalVR = VR;
}
else {
- VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion());
+ VR = MemMgr.getNonParamVarRegion(VD, MemMgr.getUnknownRegion());
OriginalVR = MemMgr.getVarRegion(VD, LC);
}
}
@@ -1511,7 +1625,7 @@ void BlockDataRegion::LazyInitializeReferencedVars() {
return;
}
- MemRegionManager &MemMgr = *getMemRegionManager();
+ MemRegionManager &MemMgr = getMemRegionManager();
llvm::BumpPtrAllocator &A = MemMgr.getAllocator();
BumpVectorContext BC(A);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
index 3a3942a8301b..ed62778623a8 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -45,8 +45,8 @@ namespace {
AnalyzerOptions &AnOpts;
const bool SupportsCrossFileDiagnostics;
public:
- PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, const std::string &prefix,
- const Preprocessor &PP,
+ PlistDiagnostics(AnalyzerOptions &AnalyzerOpts,
+ const std::string &OutputFile, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU,
bool supportsMultipleFiles);
@@ -582,19 +582,32 @@ PlistDiagnostics::PlistDiagnostics(
void ento::createPlistDiagnosticConsumer(
AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
- const std::string &s, const Preprocessor &PP,
+ const std::string &OutputFile, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
- C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, CTU,
+
+ // TODO: Emit an error here.
+ if (OutputFile.empty())
+ return;
+
+ C.push_back(new PlistDiagnostics(AnalyzerOpts, OutputFile, PP, CTU,
/*supportsMultipleFiles*/ false));
+ createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputFile, PP, CTU);
}
void ento::createPlistMultiFileDiagnosticConsumer(
AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
- const std::string &s, const Preprocessor &PP,
+ const std::string &OutputFile, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
- C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, CTU,
+
+ // TODO: Emit an error here.
+ if (OutputFile.empty())
+ return;
+
+ C.push_back(new PlistDiagnostics(AnalyzerOpts, OutputFile, PP, CTU,
/*supportsMultipleFiles*/ true));
+ createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputFile, PP, CTU);
}
+
void PlistDiagnostics::FlushDiagnosticsImpl(
std::vector<const PathDiagnostic *> &Diags,
FilesMade *filesMade) {
@@ -939,7 +952,7 @@ getExpandedMacro(SourceLocation MacroLoc, const Preprocessor &PP,
std::string MacroName = getMacroNameAndPrintExpansion(
Printer, MacroLoc, *PPToUse, MacroArgMap{}, AlreadyProcessedTokens);
- return { MacroName, OS.str() };
+ return {MacroName, std::string(OS.str())};
}
static std::string getMacroNameAndPrintExpansion(
@@ -960,9 +973,8 @@ static std::string getMacroNameAndPrintExpansion(
// in this case we don't get the full expansion text in the Plist file. See
// the test file where "value" is expanded to "garbage_" instead of
// "garbage_value".
- if (AlreadyProcessedTokens.find(IDInfo) != AlreadyProcessedTokens.end())
+ if (!AlreadyProcessedTokens.insert(IDInfo).second)
return Info.Name;
- AlreadyProcessedTokens.insert(IDInfo);
if (!Info.MI)
return Info.Name;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
index 14006f79fd0f..006a4006b7fc 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -16,8 +16,8 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
@@ -76,12 +76,12 @@ ProgramStateManager::ProgramStateManager(ASTContext &Ctx,
StoreManagerCreator CreateSMgr,
ConstraintManagerCreator CreateCMgr,
llvm::BumpPtrAllocator &alloc,
- SubEngine *SubEng)
- : Eng(SubEng), EnvMgr(alloc), GDMFactory(alloc),
+ ExprEngine *ExprEng)
+ : Eng(ExprEng), EnvMgr(alloc), GDMFactory(alloc),
svalBuilder(createSimpleSValBuilder(alloc, Ctx, *this)),
CallEventMgr(new CallEventManager(alloc)), Alloc(alloc) {
StoreMgr = (*CreateSMgr)(*this);
- ConstraintMgr = (*CreateCMgr)(*this, SubEng);
+ ConstraintMgr = (*CreateCMgr)(*this, ExprEng);
}
@@ -189,7 +189,7 @@ ProgramState::invalidateRegionsImpl(ValueList Values,
RegionAndSymbolInvalidationTraits *ITraits,
const CallEvent *Call) const {
ProgramStateManager &Mgr = getStateManager();
- SubEngine &Eng = Mgr.getOwningEngine();
+ ExprEngine &Eng = Mgr.getOwningEngine();
InvalidatedSymbols InvalidatedSyms;
if (!IS)
@@ -240,6 +240,13 @@ ProgramState::enterStackFrame(const CallEvent &Call,
return makeWithStore(NewStore);
}
+SVal ProgramState::getSelfSVal(const LocationContext *LCtx) const {
+ const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
+ if (!SelfDecl)
+ return SVal();
+ return getSVal(getRegion(SelfDecl, LCtx));
+}
+
SVal ProgramState::getSValAsScalarOrLoc(const MemRegion *R) const {
// We only want to do fetches from regions that we can actually bind
// values. For example, SymbolicRegions of type 'id<...>' cannot
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index 9752a0e22832..cb6f61e86ae3 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -16,6 +16,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableSet.h"
#include "llvm/Support/raw_ostream.h"
@@ -23,10 +24,89 @@
using namespace clang;
using namespace ento;
+// This class can be extended with other tables which will help to reason
+// about ranges more precisely.
+class OperatorRelationsTable {
+ static_assert(BO_LT < BO_GT && BO_GT < BO_LE && BO_LE < BO_GE &&
+ BO_GE < BO_EQ && BO_EQ < BO_NE,
+ "This class relies on operators order. Rework it otherwise.");
+
+public:
+ enum TriStateKind {
+ False = 0,
+ True,
+ Unknown,
+ };
+
+private:
+ // CmpOpTable holds states which represent the corresponding range for
+ // branching an exploded graph. We can reason about the branch if there is
+ // a previously known fact of the existence of a comparison expression with
+ // operands used in the current expression.
+ // E.g. assuming (x < y) is true that means (x != y) is surely true.
+ // if (x previous_operation y) // < | != | >
+ // if (x operation y) // != | > | <
+ // tristate // True | Unknown | False
+ //
+ // CmpOpTable represents next:
+ // __|< |> |<=|>=|==|!=|UnknownX2|
+ // < |1 |0 |* |0 |0 |* |1 |
+ // > |0 |1 |0 |* |0 |* |1 |
+ // <=|1 |0 |1 |* |1 |* |0 |
+ // >=|0 |1 |* |1 |1 |* |0 |
+ // ==|0 |0 |* |* |1 |0 |1 |
+ // !=|1 |1 |* |* |0 |1 |0 |
+ //
+ // Columns stands for a previous operator.
+ // Rows stands for a current operator.
+ // Each row has exactly two `Unknown` cases.
+ // UnknownX2 means that both `Unknown` previous operators are met in code,
+ // and there is a special column for that, for example:
+ // if (x >= y)
+ // if (x != y)
+ // if (x <= y)
+ // False only
+ static constexpr size_t CmpOpCount = BO_NE - BO_LT + 1;
+ const TriStateKind CmpOpTable[CmpOpCount][CmpOpCount + 1] = {
+ // < > <= >= == != UnknownX2
+ {True, False, Unknown, False, False, Unknown, True}, // <
+ {False, True, False, Unknown, False, Unknown, True}, // >
+ {True, False, True, Unknown, True, Unknown, False}, // <=
+ {False, True, Unknown, True, True, Unknown, False}, // >=
+ {False, False, Unknown, Unknown, True, False, True}, // ==
+ {True, True, Unknown, Unknown, False, True, False}, // !=
+ };
+
+ static size_t getIndexFromOp(BinaryOperatorKind OP) {
+ return static_cast<size_t>(OP - BO_LT);
+ }
+
+public:
+ constexpr size_t getCmpOpCount() const { return CmpOpCount; }
+
+ static BinaryOperatorKind getOpFromIndex(size_t Index) {
+ return static_cast<BinaryOperatorKind>(Index + BO_LT);
+ }
+
+ TriStateKind getCmpOpState(BinaryOperatorKind CurrentOP,
+ BinaryOperatorKind QueriedOP) const {
+ return CmpOpTable[getIndexFromOp(CurrentOP)][getIndexFromOp(QueriedOP)];
+ }
+
+ TriStateKind getCmpOpStateForUnknownX2(BinaryOperatorKind CurrentOP) const {
+ return CmpOpTable[getIndexFromOp(CurrentOP)][CmpOpCount];
+ }
+};
+//===----------------------------------------------------------------------===//
+// RangeSet implementation
+//===----------------------------------------------------------------------===//
+
void RangeSet::IntersectInRange(BasicValueFactory &BV, Factory &F,
- const llvm::APSInt &Lower, const llvm::APSInt &Upper,
- PrimRangeSet &newRanges, PrimRangeSet::iterator &i,
- PrimRangeSet::iterator &e) const {
+ const llvm::APSInt &Lower,
+ const llvm::APSInt &Upper,
+ PrimRangeSet &newRanges,
+ PrimRangeSet::iterator &i,
+ PrimRangeSet::iterator &e) const {
// There are six cases for each range R in the set:
// 1. R is entirely before the intersection range.
// 2. R is entirely after the intersection range.
@@ -62,10 +142,27 @@ void RangeSet::IntersectInRange(BasicValueFactory &BV, Factory &F,
const llvm::APSInt &RangeSet::getMinValue() const {
assert(!isEmpty());
- return ranges.begin()->From();
+ return begin()->From();
+}
+
+const llvm::APSInt &RangeSet::getMaxValue() const {
+ assert(!isEmpty());
+ // NOTE: It's a shame that we can't implement 'getMaxValue' without scanning
+ // the whole tree to get to the last element.
+ // llvm::ImmutableSet should support decrement for 'end' iterators
+ // or reverse order iteration.
+ auto It = begin();
+ for (auto End = end(); std::next(It) != End; ++It) {
+ }
+ return It->To();
}
bool RangeSet::pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const {
+ if (isEmpty()) {
+ // This range is already infeasible.
+ return false;
+ }
+
// This function has nine cases, the cartesian product of range-testing
// both the upper and lower bounds against the symbol's type.
// Each case requires a different pinning operation.
@@ -155,11 +252,11 @@ bool RangeSet::pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const {
// or, alternatively, /removing/ all integers between Upper and Lower.
RangeSet RangeSet::Intersect(BasicValueFactory &BV, Factory &F,
llvm::APSInt Lower, llvm::APSInt Upper) const {
- if (!pin(Lower, Upper))
- return F.getEmptySet();
-
PrimRangeSet newRanges = F.getEmptySet();
+ if (isEmpty() || !pin(Lower, Upper))
+ return newRanges;
+
PrimRangeSet::iterator i = begin(), e = end();
if (Lower <= Upper)
IntersectInRange(BV, F, Lower, Upper, newRanges, i, e);
@@ -190,33 +287,78 @@ RangeSet RangeSet::Intersect(BasicValueFactory &BV, Factory &F,
return newRanges;
}
-// Turn all [A, B] ranges to [-B, -A]. Ranges [MIN, B] are turned to range set
-// [MIN, MIN] U [-B, MAX], when MIN and MAX are the minimal and the maximal
-// signed values of the type.
+// Turn all [A, B] ranges to [-B, -A], when "-" is a C-like unary minus
+// operation under the values of the type.
+//
+// We also handle MIN because applying unary minus to MIN does not change it.
+// Example 1:
+// char x = -128; // -128 is a MIN value in a range of 'char'
+// char y = -x; // y: -128
+// Example 2:
+// unsigned char x = 0; // 0 is a MIN value in a range of 'unsigned char'
+// unsigned char y = -x; // y: 0
+//
+// And it makes us to separate the range
+// like [MIN, N] to [MIN, MIN] U [-N,MAX].
+// For instance, whole range is {-128..127} and subrange is [-128,-126],
+// thus [-128,-127,-126,.....] negates to [-128,.....,126,127].
+//
+// Negate restores disrupted ranges on bounds,
+// e.g. [MIN, B] => [MIN, MIN] U [-B, MAX] => [MIN, B].
RangeSet RangeSet::Negate(BasicValueFactory &BV, Factory &F) const {
PrimRangeSet newRanges = F.getEmptySet();
- for (iterator i = begin(), e = end(); i != e; ++i) {
- const llvm::APSInt &from = i->From(), &to = i->To();
- const llvm::APSInt &newTo = (from.isMinSignedValue() ?
- BV.getMaxValue(from) :
- BV.getValue(- from));
- if (to.isMaxSignedValue() && !newRanges.isEmpty() &&
- newRanges.begin()->From().isMinSignedValue()) {
- assert(newRanges.begin()->To().isMinSignedValue() &&
- "Ranges should not overlap");
- assert(!from.isMinSignedValue() && "Ranges should not overlap");
- const llvm::APSInt &newFrom = newRanges.begin()->From();
- newRanges =
- F.add(F.remove(newRanges, *newRanges.begin()), Range(newFrom, newTo));
- } else if (!to.isMinSignedValue()) {
- const llvm::APSInt &newFrom = BV.getValue(- to);
- newRanges = F.add(newRanges, Range(newFrom, newTo));
- }
- if (from.isMinSignedValue()) {
- newRanges = F.add(newRanges, Range(BV.getMinValue(from),
- BV.getMinValue(from)));
+ if (isEmpty())
+ return newRanges;
+
+ const llvm::APSInt sampleValue = getMinValue();
+ const llvm::APSInt &MIN = BV.getMinValue(sampleValue);
+ const llvm::APSInt &MAX = BV.getMaxValue(sampleValue);
+
+ // Handle a special case for MIN value.
+ iterator i = begin();
+ const llvm::APSInt &from = i->From();
+ const llvm::APSInt &to = i->To();
+ if (from == MIN) {
+ // If [from, to] are [MIN, MAX], then just return the same [MIN, MAX].
+ if (to == MAX) {
+ newRanges = ranges;
+ } else {
+ // Add separate range for the lowest value.
+ newRanges = F.add(newRanges, Range(MIN, MIN));
+ // Skip adding the second range in case when [from, to] are [MIN, MIN].
+ if (to != MIN) {
+ newRanges = F.add(newRanges, Range(BV.getValue(-to), MAX));
+ }
}
+ // Skip the first range in the loop.
+ ++i;
+ }
+
+ // Negate all other ranges.
+ for (iterator e = end(); i != e; ++i) {
+ // Negate int values.
+ const llvm::APSInt &newFrom = BV.getValue(-i->To());
+ const llvm::APSInt &newTo = BV.getValue(-i->From());
+ // Add a negated range.
+ newRanges = F.add(newRanges, Range(newFrom, newTo));
+ }
+
+ if (newRanges.isSingleton())
+ return newRanges;
+
+ // Try to find and unite next ranges:
+ // [MIN, MIN] & [MIN + 1, N] => [MIN, N].
+ iterator iter1 = newRanges.begin();
+ iterator iter2 = std::next(iter1);
+
+ if (iter1->To() == MIN && (iter2->From() - 1) == MIN) {
+ const llvm::APSInt &to = iter2->To();
+ // remove adjacent ranges
+ newRanges = F.remove(newRanges, *iter1);
+ newRanges = F.remove(newRanges, *newRanges.begin());
+ // add united range
+ newRanges = F.add(newRanges, Range(MIN, to));
}
return newRanges;
@@ -238,10 +380,534 @@ void RangeSet::print(raw_ostream &os) const {
}
namespace {
+
+/// A little component aggregating all of the reasoning we have about
+/// the ranges of symbolic expressions.
+///
+/// Even when we don't know the exact values of the operands, we still
+/// can get a pretty good estimate of the result's range.
+class SymbolicRangeInferrer
+ : public SymExprVisitor<SymbolicRangeInferrer, RangeSet> {
+public:
+ static RangeSet inferRange(BasicValueFactory &BV, RangeSet::Factory &F,
+ ProgramStateRef State, SymbolRef Sym) {
+ SymbolicRangeInferrer Inferrer(BV, F, State);
+ return Inferrer.infer(Sym);
+ }
+
+ RangeSet VisitSymExpr(SymbolRef Sym) {
+ // If we got to this function, the actual type of the symbolic
+ // expression is not supported for advanced inference.
+ // In this case, we simply backoff to the default "let's simply
+ // infer the range from the expression's type".
+ return infer(Sym->getType());
+ }
+
+ RangeSet VisitSymIntExpr(const SymIntExpr *Sym) {
+ return VisitBinaryOperator(Sym);
+ }
+
+ RangeSet VisitIntSymExpr(const IntSymExpr *Sym) {
+ return VisitBinaryOperator(Sym);
+ }
+
+ RangeSet VisitSymSymExpr(const SymSymExpr *Sym) {
+ return VisitBinaryOperator(Sym);
+ }
+
+private:
+ SymbolicRangeInferrer(BasicValueFactory &BV, RangeSet::Factory &F,
+ ProgramStateRef S)
+ : ValueFactory(BV), RangeFactory(F), State(S) {}
+
+ /// Infer range information from the given integer constant.
+ ///
+ /// It's not a real "inference", but is here for operating with
+ /// sub-expressions in a more polymorphic manner.
+ RangeSet inferAs(const llvm::APSInt &Val, QualType) {
+ return {RangeFactory, Val};
+ }
+
+ /// Infer range information from symbol in the context of the given type.
+ RangeSet inferAs(SymbolRef Sym, QualType DestType) {
+ QualType ActualType = Sym->getType();
+ // Check that we can reason about the symbol at all.
+ if (ActualType->isIntegralOrEnumerationType() ||
+ Loc::isLocType(ActualType)) {
+ return infer(Sym);
+ }
+ // Otherwise, let's simply infer from the destination type.
+ // We couldn't figure out nothing else about that expression.
+ return infer(DestType);
+ }
+
+ RangeSet infer(SymbolRef Sym) {
+ const RangeSet *AssociatedRange = State->get<ConstraintRange>(Sym);
+
+ // If Sym is a difference of symbols A - B, then maybe we have range set
+ // stored for B - A.
+ const RangeSet *RangeAssociatedWithNegatedSym =
+ getRangeForMinusSymbol(State, Sym);
+
+ // If we have range set stored for both A - B and B - A then calculate the
+ // effective range set by intersecting the range set for A - B and the
+ // negated range set of B - A.
+ if (AssociatedRange && RangeAssociatedWithNegatedSym)
+ return AssociatedRange->Intersect(
+ ValueFactory, RangeFactory,
+ RangeAssociatedWithNegatedSym->Negate(ValueFactory, RangeFactory));
+
+ if (AssociatedRange)
+ return *AssociatedRange;
+
+ if (RangeAssociatedWithNegatedSym)
+ return RangeAssociatedWithNegatedSym->Negate(ValueFactory, RangeFactory);
+
+ // If Sym is a comparison expression (except <=>),
+ // find any other comparisons with the same operands.
+ // See function description.
+ const RangeSet CmpRangeSet = getRangeForComparisonSymbol(State, Sym);
+ if (!CmpRangeSet.isEmpty())
+ return CmpRangeSet;
+
+ return Visit(Sym);
+ }
+
+ /// Infer range information solely from the type.
+ RangeSet infer(QualType T) {
+ // Lazily generate a new RangeSet representing all possible values for the
+ // given symbol type.
+ RangeSet Result(RangeFactory, ValueFactory.getMinValue(T),
+ ValueFactory.getMaxValue(T));
+
+ // References are known to be non-zero.
+ if (T->isReferenceType())
+ return assumeNonZero(Result, T);
+
+ return Result;
+ }
+
+ template <class BinarySymExprTy>
+ RangeSet VisitBinaryOperator(const BinarySymExprTy *Sym) {
+ // TODO #1: VisitBinaryOperator implementation might not make a good
+ // use of the inferred ranges. In this case, we might be calculating
+ // everything for nothing. This being said, we should introduce some
+ // sort of laziness mechanism here.
+ //
+ // TODO #2: We didn't go into the nested expressions before, so it
+ // might cause us spending much more time doing the inference.
+ // This can be a problem for deeply nested expressions that are
+ // involved in conditions and get tested continuously. We definitely
+ // need to address this issue and introduce some sort of caching
+ // in here.
+ QualType ResultType = Sym->getType();
+ return VisitBinaryOperator(inferAs(Sym->getLHS(), ResultType),
+ Sym->getOpcode(),
+ inferAs(Sym->getRHS(), ResultType), ResultType);
+ }
+
+ RangeSet VisitBinaryOperator(RangeSet LHS, BinaryOperator::Opcode Op,
+ RangeSet RHS, QualType T) {
+ switch (Op) {
+ case BO_Or:
+ return VisitBinaryOperator<BO_Or>(LHS, RHS, T);
+ case BO_And:
+ return VisitBinaryOperator<BO_And>(LHS, RHS, T);
+ case BO_Rem:
+ return VisitBinaryOperator<BO_Rem>(LHS, RHS, T);
+ default:
+ return infer(T);
+ }
+ }
+
+ //===----------------------------------------------------------------------===//
+ // Ranges and operators
+ //===----------------------------------------------------------------------===//
+
+ /// Return a rough approximation of the given range set.
+ ///
+ /// For the range set:
+ /// { [x_0, y_0], [x_1, y_1], ... , [x_N, y_N] }
+ /// it will return the range [x_0, y_N].
+ static Range fillGaps(RangeSet Origin) {
+ assert(!Origin.isEmpty());
+ return {Origin.getMinValue(), Origin.getMaxValue()};
+ }
+
+ /// Try to convert given range into the given type.
+ ///
+ /// It will return llvm::None only when the trivial conversion is possible.
+ llvm::Optional<Range> convert(const Range &Origin, APSIntType To) {
+ if (To.testInRange(Origin.From(), false) != APSIntType::RTR_Within ||
+ To.testInRange(Origin.To(), false) != APSIntType::RTR_Within) {
+ return llvm::None;
+ }
+ return Range(ValueFactory.Convert(To, Origin.From()),
+ ValueFactory.Convert(To, Origin.To()));
+ }
+
+ template <BinaryOperator::Opcode Op>
+ RangeSet VisitBinaryOperator(RangeSet LHS, RangeSet RHS, QualType T) {
+ // We should propagate information about unfeasbility of one of the
+ // operands to the resulting range.
+ if (LHS.isEmpty() || RHS.isEmpty()) {
+ return RangeFactory.getEmptySet();
+ }
+
+ Range CoarseLHS = fillGaps(LHS);
+ Range CoarseRHS = fillGaps(RHS);
+
+ APSIntType ResultType = ValueFactory.getAPSIntType(T);
+
+ // We need to convert ranges to the resulting type, so we can compare values
+ // and combine them in a meaningful (in terms of the given operation) way.
+ auto ConvertedCoarseLHS = convert(CoarseLHS, ResultType);
+ auto ConvertedCoarseRHS = convert(CoarseRHS, ResultType);
+
+ // It is hard to reason about ranges when conversion changes
+ // borders of the ranges.
+ if (!ConvertedCoarseLHS || !ConvertedCoarseRHS) {
+ return infer(T);
+ }
+
+ return VisitBinaryOperator<Op>(*ConvertedCoarseLHS, *ConvertedCoarseRHS, T);
+ }
+
+ template <BinaryOperator::Opcode Op>
+ RangeSet VisitBinaryOperator(Range LHS, Range RHS, QualType T) {
+ return infer(T);
+ }
+
+ /// Return a symmetrical range for the given range and type.
+ ///
+ /// If T is signed, return the smallest range [-x..x] that covers the original
+ /// range, or [-min(T), max(T)] if the aforementioned symmetric range doesn't
+ /// exist due to original range covering min(T)).
+ ///
+ /// If T is unsigned, return the smallest range [0..x] that covers the
+ /// original range.
+ Range getSymmetricalRange(Range Origin, QualType T) {
+ APSIntType RangeType = ValueFactory.getAPSIntType(T);
+
+ if (RangeType.isUnsigned()) {
+ return Range(ValueFactory.getMinValue(RangeType), Origin.To());
+ }
+
+ if (Origin.From().isMinSignedValue()) {
+ // If mini is a minimal signed value, absolute value of it is greater
+ // than the maximal signed value. In order to avoid these
+ // complications, we simply return the whole range.
+ return {ValueFactory.getMinValue(RangeType),
+ ValueFactory.getMaxValue(RangeType)};
+ }
+
+ // At this point, we are sure that the type is signed and we can safely
+ // use unary - operator.
+ //
+ // While calculating absolute maximum, we can use the following formula
+ // because of these reasons:
+ // * If From >= 0 then To >= From and To >= -From.
+ // AbsMax == To == max(To, -From)
+ // * If To <= 0 then -From >= -To and -From >= From.
+ // AbsMax == -From == max(-From, To)
+ // * Otherwise, From <= 0, To >= 0, and
+ // AbsMax == max(abs(From), abs(To))
+ llvm::APSInt AbsMax = std::max(-Origin.From(), Origin.To());
+
+ // Intersection is guaranteed to be non-empty.
+ return {ValueFactory.getValue(-AbsMax), ValueFactory.getValue(AbsMax)};
+ }
+
+ /// Return a range set subtracting zero from \p Domain.
+ RangeSet assumeNonZero(RangeSet Domain, QualType T) {
+ APSIntType IntType = ValueFactory.getAPSIntType(T);
+ return Domain.Intersect(ValueFactory, RangeFactory,
+ ++IntType.getZeroValue(), --IntType.getZeroValue());
+ }
+
+ // FIXME: Once SValBuilder supports unary minus, we should use SValBuilder to
+ // obtain the negated symbolic expression instead of constructing the
+ // symbol manually. This will allow us to support finding ranges of not
+ // only negated SymSymExpr-type expressions, but also of other, simpler
+ // expressions which we currently do not know how to negate.
+ const RangeSet *getRangeForMinusSymbol(ProgramStateRef State, SymbolRef Sym) {
+ if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) {
+ if (SSE->getOpcode() == BO_Sub) {
+ QualType T = Sym->getType();
+ SymbolManager &SymMgr = State->getSymbolManager();
+ SymbolRef negSym =
+ SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), T);
+
+ if (const RangeSet *negV = State->get<ConstraintRange>(negSym)) {
+ // Unsigned range set cannot be negated, unless it is [0, 0].
+ if (T->isUnsignedIntegerOrEnumerationType() ||
+ T->isSignedIntegerOrEnumerationType())
+ return negV;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ // Returns ranges only for binary comparison operators (except <=>)
+ // when left and right operands are symbolic values.
+ // Finds any other comparisons with the same operands.
+ // Then do logical calculations and refuse impossible branches.
+ // E.g. (x < y) and (x > y) at the same time are impossible.
+ // E.g. (x >= y) and (x != y) at the same time makes (x > y) true only.
+ // E.g. (x == y) and (y == x) are just reversed but the same.
+ // It covers all possible combinations (see CmpOpTable description).
+ // Note that `x` and `y` can also stand for subexpressions,
+ // not only for actual symbols.
+ RangeSet getRangeForComparisonSymbol(ProgramStateRef State, SymbolRef Sym) {
+ const RangeSet EmptyRangeSet = RangeFactory.getEmptySet();
+
+ auto SSE = dyn_cast<SymSymExpr>(Sym);
+ if (!SSE)
+ return EmptyRangeSet;
+
+ BinaryOperatorKind CurrentOP = SSE->getOpcode();
+
+ // We currently do not support <=> (C++20).
+ if (!BinaryOperator::isComparisonOp(CurrentOP) || (CurrentOP == BO_Cmp))
+ return EmptyRangeSet;
+
+ static const OperatorRelationsTable CmpOpTable{};
+
+ const SymExpr *LHS = SSE->getLHS();
+ const SymExpr *RHS = SSE->getRHS();
+ QualType T = SSE->getType();
+
+ SymbolManager &SymMgr = State->getSymbolManager();
+ const llvm::APSInt &Zero = ValueFactory.getValue(0, T);
+ const llvm::APSInt &One = ValueFactory.getValue(1, T);
+ const RangeSet TrueRangeSet(RangeFactory, One, One);
+ const RangeSet FalseRangeSet(RangeFactory, Zero, Zero);
+
+ int UnknownStates = 0;
+
+ // Loop goes through all of the columns exept the last one ('UnknownX2').
+ // We treat `UnknownX2` column separately at the end of the loop body.
+ for (size_t i = 0; i < CmpOpTable.getCmpOpCount(); ++i) {
+
+ // Let's find an expression e.g. (x < y).
+ BinaryOperatorKind QueriedOP = OperatorRelationsTable::getOpFromIndex(i);
+ const SymSymExpr *SymSym = SymMgr.getSymSymExpr(LHS, QueriedOP, RHS, T);
+ const RangeSet *QueriedRangeSet = State->get<ConstraintRange>(SymSym);
+
+ // If ranges were not previously found,
+ // try to find a reversed expression (y > x).
+ if (!QueriedRangeSet) {
+ const BinaryOperatorKind ROP =
+ BinaryOperator::reverseComparisonOp(QueriedOP);
+ SymSym = SymMgr.getSymSymExpr(RHS, ROP, LHS, T);
+ QueriedRangeSet = State->get<ConstraintRange>(SymSym);
+ }
+
+ if (!QueriedRangeSet || QueriedRangeSet->isEmpty())
+ continue;
+
+ const llvm::APSInt *ConcreteValue = QueriedRangeSet->getConcreteValue();
+ const bool isInFalseBranch =
+ ConcreteValue ? (*ConcreteValue == 0) : false;
+
+ // If it is a false branch, we shall be guided by opposite operator,
+ // because the table is made assuming we are in the true branch.
+ // E.g. when (x <= y) is false, then (x > y) is true.
+ if (isInFalseBranch)
+ QueriedOP = BinaryOperator::negateComparisonOp(QueriedOP);
+
+ OperatorRelationsTable::TriStateKind BranchState =
+ CmpOpTable.getCmpOpState(CurrentOP, QueriedOP);
+
+ if (BranchState == OperatorRelationsTable::Unknown) {
+ if (++UnknownStates == 2)
+ // If we met both Unknown states.
+ // if (x <= y) // assume true
+ // if (x != y) // assume true
+ // if (x < y) // would be also true
+ // Get a state from `UnknownX2` column.
+ BranchState = CmpOpTable.getCmpOpStateForUnknownX2(CurrentOP);
+ else
+ continue;
+ }
+
+ return (BranchState == OperatorRelationsTable::True) ? TrueRangeSet
+ : FalseRangeSet;
+ }
+
+ return EmptyRangeSet;
+ }
+
+ BasicValueFactory &ValueFactory;
+ RangeSet::Factory &RangeFactory;
+ ProgramStateRef State;
+};
+
+template <>
+RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Or>(Range LHS, Range RHS,
+ QualType T) {
+ APSIntType ResultType = ValueFactory.getAPSIntType(T);
+ llvm::APSInt Zero = ResultType.getZeroValue();
+
+ bool IsLHSPositiveOrZero = LHS.From() >= Zero;
+ bool IsRHSPositiveOrZero = RHS.From() >= Zero;
+
+ bool IsLHSNegative = LHS.To() < Zero;
+ bool IsRHSNegative = RHS.To() < Zero;
+
+ // Check if both ranges have the same sign.
+ if ((IsLHSPositiveOrZero && IsRHSPositiveOrZero) ||
+ (IsLHSNegative && IsRHSNegative)) {
+ // The result is definitely greater or equal than any of the operands.
+ const llvm::APSInt &Min = std::max(LHS.From(), RHS.From());
+
+ // We estimate maximal value for positives as the maximal value for the
+ // given type. For negatives, we estimate it with -1 (e.g. 0x11111111).
+ //
+ // TODO: We basically, limit the resulting range from below, but don't do
+ // anything with the upper bound.
+ //
+ // For positive operands, it can be done as follows: for the upper
+ // bound of LHS and RHS we calculate the most significant bit set.
+ // Let's call it the N-th bit. Then we can estimate the maximal
+ // number to be 2^(N+1)-1, i.e. the number with all the bits up to
+ // the N-th bit set.
+ const llvm::APSInt &Max = IsLHSNegative
+ ? ValueFactory.getValue(--Zero)
+ : ValueFactory.getMaxValue(ResultType);
+
+ return {RangeFactory, ValueFactory.getValue(Min), Max};
+ }
+
+ // Otherwise, let's check if at least one of the operands is negative.
+ if (IsLHSNegative || IsRHSNegative) {
+ // This means that the result is definitely negative as well.
+ return {RangeFactory, ValueFactory.getMinValue(ResultType),
+ ValueFactory.getValue(--Zero)};
+ }
+
+ RangeSet DefaultRange = infer(T);
+
+ // It is pretty hard to reason about operands with different signs
+ // (and especially with possibly different signs). We simply check if it
+ // can be zero. In order to conclude that the result could not be zero,
+ // at least one of the operands should be definitely not zero itself.
+ if (!LHS.Includes(Zero) || !RHS.Includes(Zero)) {
+ return assumeNonZero(DefaultRange, T);
+ }
+
+ // Nothing much else to do here.
+ return DefaultRange;
+}
+
+template <>
+RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_And>(Range LHS,
+ Range RHS,
+ QualType T) {
+ APSIntType ResultType = ValueFactory.getAPSIntType(T);
+ llvm::APSInt Zero = ResultType.getZeroValue();
+
+ bool IsLHSPositiveOrZero = LHS.From() >= Zero;
+ bool IsRHSPositiveOrZero = RHS.From() >= Zero;
+
+ bool IsLHSNegative = LHS.To() < Zero;
+ bool IsRHSNegative = RHS.To() < Zero;
+
+ // Check if both ranges have the same sign.
+ if ((IsLHSPositiveOrZero && IsRHSPositiveOrZero) ||
+ (IsLHSNegative && IsRHSNegative)) {
+ // The result is definitely less or equal than any of the operands.
+ const llvm::APSInt &Max = std::min(LHS.To(), RHS.To());
+
+ // We conservatively estimate lower bound to be the smallest positive
+ // or negative value corresponding to the sign of the operands.
+ const llvm::APSInt &Min = IsLHSNegative
+ ? ValueFactory.getMinValue(ResultType)
+ : ValueFactory.getValue(Zero);
+
+ return {RangeFactory, Min, Max};
+ }
+
+ // Otherwise, let's check if at least one of the operands is positive.
+ if (IsLHSPositiveOrZero || IsRHSPositiveOrZero) {
+ // This makes result definitely positive.
+ //
+ // We can also reason about a maximal value by finding the maximal
+ // value of the positive operand.
+ const llvm::APSInt &Max = IsLHSPositiveOrZero ? LHS.To() : RHS.To();
+
+ // The minimal value on the other hand is much harder to reason about.
+ // The only thing we know for sure is that the result is positive.
+ return {RangeFactory, ValueFactory.getValue(Zero),
+ ValueFactory.getValue(Max)};
+ }
+
+ // Nothing much else to do here.
+ return infer(T);
+}
+
+template <>
+RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Rem>(Range LHS,
+ Range RHS,
+ QualType T) {
+ llvm::APSInt Zero = ValueFactory.getAPSIntType(T).getZeroValue();
+
+ Range ConservativeRange = getSymmetricalRange(RHS, T);
+
+ llvm::APSInt Max = ConservativeRange.To();
+ llvm::APSInt Min = ConservativeRange.From();
+
+ if (Max == Zero) {
+ // It's an undefined behaviour to divide by 0 and it seems like we know
+ // for sure that RHS is 0. Let's say that the resulting range is
+ // simply infeasible for that matter.
+ return RangeFactory.getEmptySet();
+ }
+
+ // At this point, our conservative range is closed. The result, however,
+ // couldn't be greater than the RHS' maximal absolute value. Because of
+ // this reason, we turn the range into open (or half-open in case of
+ // unsigned integers).
+ //
+ // While we operate on integer values, an open interval (a, b) can be easily
+ // represented by the closed interval [a + 1, b - 1]. And this is exactly
+ // what we do next.
+ //
+ // If we are dealing with unsigned case, we shouldn't move the lower bound.
+ if (Min.isSigned()) {
+ ++Min;
+ }
+ --Max;
+
+ bool IsLHSPositiveOrZero = LHS.From() >= Zero;
+ bool IsRHSPositiveOrZero = RHS.From() >= Zero;
+
+ // Remainder operator results with negative operands is implementation
+ // defined. Positive cases are much easier to reason about though.
+ if (IsLHSPositiveOrZero && IsRHSPositiveOrZero) {
+ // If maximal value of LHS is less than maximal value of RHS,
+ // the result won't get greater than LHS.To().
+ Max = std::min(LHS.To(), Max);
+ // We want to check if it is a situation similar to the following:
+ //
+ // <------------|---[ LHS ]--------[ RHS ]----->
+ // -INF 0 +INF
+ //
+ // In this situation, we can conclude that (LHS / RHS) == 0 and
+ // (LHS % RHS) == LHS.
+ Min = LHS.To() < RHS.From() ? LHS.From() : Zero;
+ }
+
+ // Nevertheless, the symmetrical range for RHS is a conservative estimate
+ // for any sign of either LHS, or RHS.
+ return {RangeFactory, ValueFactory.getValue(Min), ValueFactory.getValue(Max)};
+}
+
class RangeConstraintManager : public RangedConstraintManager {
public:
- RangeConstraintManager(SubEngine *SE, SValBuilder &SVB)
- : RangedConstraintManager(SE, SVB) {}
+ RangeConstraintManager(ExprEngine *EE, SValBuilder &SVB)
+ : RangedConstraintManager(EE, SVB) {}
//===------------------------------------------------------------------===//
// Implementation for interface from ConstraintManager.
@@ -305,8 +971,6 @@ private:
RangeSet::Factory F;
RangeSet getRange(ProgramStateRef State, SymbolRef Sym);
- const RangeSet* getRangeForMinusSymbol(ProgramStateRef State,
- SymbolRef Sym);
RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
@@ -323,13 +987,13 @@ private:
RangeSet getSymGERange(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment);
-
};
} // end anonymous namespace
std::unique_ptr<ConstraintManager>
-ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) {
+ento::CreateRangeConstraintManager(ProgramStateManager &StMgr,
+ ExprEngine *Eng) {
return std::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder());
}
@@ -429,113 +1093,9 @@ RangeConstraintManager::removeDeadBindings(ProgramStateRef State,
return Changed ? State->set<ConstraintRange>(CR) : State;
}
-/// Return a range set subtracting zero from \p Domain.
-static RangeSet assumeNonZero(
- BasicValueFactory &BV,
- RangeSet::Factory &F,
- SymbolRef Sym,
- RangeSet Domain) {
- APSIntType IntType = BV.getAPSIntType(Sym->getType());
- return Domain.Intersect(BV, F, ++IntType.getZeroValue(),
- --IntType.getZeroValue());
-}
-
-/// Apply implicit constraints for bitwise OR- and AND-.
-/// For unsigned types, bitwise OR with a constant always returns
-/// a value greater-or-equal than the constant, and bitwise AND
-/// returns a value less-or-equal then the constant.
-///
-/// Pattern matches the expression \p Sym against those rule,
-/// and applies the required constraints.
-/// \p Input Previously established expression range set
-static RangeSet applyBitwiseConstraints(
- BasicValueFactory &BV,
- RangeSet::Factory &F,
- RangeSet Input,
- const SymIntExpr* SIE) {
- QualType T = SIE->getType();
- bool IsUnsigned = T->isUnsignedIntegerType();
- const llvm::APSInt &RHS = SIE->getRHS();
- const llvm::APSInt &Zero = BV.getAPSIntType(T).getZeroValue();
- BinaryOperator::Opcode Operator = SIE->getOpcode();
-
- // For unsigned types, the output of bitwise-or is bigger-or-equal than RHS.
- if (Operator == BO_Or && IsUnsigned)
- return Input.Intersect(BV, F, RHS, BV.getMaxValue(T));
-
- // Bitwise-or with a non-zero constant is always non-zero.
- if (Operator == BO_Or && RHS != Zero)
- return assumeNonZero(BV, F, SIE, Input);
-
- // For unsigned types, or positive RHS,
- // bitwise-and output is always smaller-or-equal than RHS (assuming two's
- // complement representation of signed types).
- if (Operator == BO_And && (IsUnsigned || RHS >= Zero))
- return Input.Intersect(BV, F, BV.getMinValue(T), RHS);
-
- return Input;
-}
-
RangeSet RangeConstraintManager::getRange(ProgramStateRef State,
SymbolRef Sym) {
- ConstraintRangeTy::data_type *V = State->get<ConstraintRange>(Sym);
-
- // If Sym is a difference of symbols A - B, then maybe we have range set
- // stored for B - A.
- BasicValueFactory &BV = getBasicVals();
- const RangeSet *R = getRangeForMinusSymbol(State, Sym);
-
- // If we have range set stored for both A - B and B - A then calculate the
- // effective range set by intersecting the range set for A - B and the
- // negated range set of B - A.
- if (V && R)
- return V->Intersect(BV, F, R->Negate(BV, F));
- if (V)
- return *V;
- if (R)
- return R->Negate(BV, F);
-
- // Lazily generate a new RangeSet representing all possible values for the
- // given symbol type.
- QualType T = Sym->getType();
-
- RangeSet Result(F, BV.getMinValue(T), BV.getMaxValue(T));
-
- // References are known to be non-zero.
- if (T->isReferenceType())
- return assumeNonZero(BV, F, Sym, Result);
-
- // Known constraints on ranges of bitwise expressions.
- if (const SymIntExpr* SIE = dyn_cast<SymIntExpr>(Sym))
- return applyBitwiseConstraints(BV, F, Result, SIE);
-
- return Result;
-}
-
-// FIXME: Once SValBuilder supports unary minus, we should use SValBuilder to
-// obtain the negated symbolic expression instead of constructing the
-// symbol manually. This will allow us to support finding ranges of not
-// only negated SymSymExpr-type expressions, but also of other, simpler
-// expressions which we currently do not know how to negate.
-const RangeSet*
-RangeConstraintManager::getRangeForMinusSymbol(ProgramStateRef State,
- SymbolRef Sym) {
- if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) {
- if (SSE->getOpcode() == BO_Sub) {
- QualType T = Sym->getType();
- SymbolManager &SymMgr = State->getSymbolManager();
- SymbolRef negSym = SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub,
- SSE->getLHS(), T);
- if (const RangeSet *negV = State->get<ConstraintRange>(negSym)) {
- // Unsigned range set cannot be negated, unless it is [0, 0].
- if ((negV->getConcreteValue() &&
- (*negV->getConcreteValue() == 0)) ||
- T->isSignedIntegerOrEnumerationType())
- return negV;
- }
- }
- }
- return nullptr;
+ return SymbolicRangeInferrer::inferRange(getBasicVals(), F, State, Sym);
}
//===------------------------------------------------------------------------===
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 4797f564a837..57fde32bc01d 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -23,10 +23,11 @@
#include "clang/Basic/TargetInfo.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/raw_ostream.h"
@@ -381,7 +382,7 @@ public:
: StoreManager(mgr), Features(f),
RBFactory(mgr.getAllocator()), CBFactory(mgr.getAllocator()),
SmallStructLimit(0) {
- SubEngine &Eng = StateMgr.getOwningEngine();
+ ExprEngine &Eng = StateMgr.getOwningEngine();
AnalyzerOptions &Options = Eng.getAnalysisManager().options;
SmallStructLimit = Options.RegionStoreSmallStructLimit;
}
@@ -622,15 +623,6 @@ public: // Part of public interface to class.
SymbolReaper& SymReaper) override;
//===------------------------------------------------------------------===//
- // Region "extents".
- //===------------------------------------------------------------------===//
-
- // FIXME: This method will soon be eliminated; see the note in Store.h.
- DefinedOrUnknownSVal getSizeInElements(ProgramStateRef state,
- const MemRegion* R,
- QualType EleTy) override;
-
- //===------------------------------------------------------------------===//
// Utility methods.
//===------------------------------------------------------------------===//
@@ -876,7 +868,7 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings,
// Find the length (in bits) of the region being invalidated.
uint64_t Length = UINT64_MAX;
- SVal Extent = Top->getExtent(SVB);
+ SVal Extent = Top->getMemRegionManager().getStaticSize(Top, SVB);
if (Optional<nonloc::ConcreteInt> ExtentCI =
Extent.getAs<nonloc::ConcreteInt>()) {
const llvm::APSInt &ExtentInt = ExtentCI->getValue();
@@ -1387,37 +1379,6 @@ RegionStoreManager::invalidateRegions(Store store,
}
//===----------------------------------------------------------------------===//
-// Extents for regions.
-//===----------------------------------------------------------------------===//
-
-DefinedOrUnknownSVal
-RegionStoreManager::getSizeInElements(ProgramStateRef state,
- const MemRegion *R,
- QualType EleTy) {
- SVal Size = cast<SubRegion>(R)->getExtent(svalBuilder);
- const llvm::APSInt *SizeInt = svalBuilder.getKnownValue(state, Size);
- if (!SizeInt)
- return UnknownVal();
-
- CharUnits RegionSize = CharUnits::fromQuantity(SizeInt->getSExtValue());
-
- if (Ctx.getAsVariableArrayType(EleTy)) {
- // FIXME: We need to track extra state to properly record the size
- // of VLAs. Returning UnknownVal here, however, is a stop-gap so that
- // we don't have a divide-by-zero below.
- return UnknownVal();
- }
-
- CharUnits EleSize = Ctx.getTypeSizeInChars(EleTy);
-
- // If a variable is reinterpreted as a type that doesn't fit into a larger
- // type evenly, round it down.
- // This is a signed value, since it's used in arithmetic with signed indices.
- return svalBuilder.makeIntVal(RegionSize / EleSize,
- svalBuilder.getArrayIndexType());
-}
-
-//===----------------------------------------------------------------------===//
// Location and region casting.
//===----------------------------------------------------------------------===//
@@ -1667,10 +1628,6 @@ RegionStoreManager::findLazyBinding(RegionBindingsConstRef B,
SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
const ElementRegion* R) {
- // We do not currently model bindings of the CompoundLiteralregion.
- if (isa<CompoundLiteralRegion>(R->getBaseRegion()))
- return UnknownVal();
-
// Check if the region has a binding.
if (const Optional<SVal> &V = B.getDirectBinding(R))
return *V;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
index 6ad12ca0a688..7395622a659c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
@@ -13,6 +13,6 @@ using namespace clang;
using namespace ento;
std::unique_ptr<ConstraintManager>
-ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) {
+ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, ExprEngine *Eng) {
return std::make_unique<SMTConstraintManager>(Eng, StMgr.getSValBuilder());
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
index 3a5841137e1a..c00a2c8ba8a2 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -24,12 +24,12 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/APSInt.h"
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
index 12332aaf936f..8c2e85601576 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Analysis/PathDiagnostic.h"
+#include "clang/Basic/FileManager.h"
#include "clang/Basic/Version.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
@@ -49,8 +50,14 @@ public:
void ento::createSarifDiagnosticConsumer(
AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
const std::string &Output, const Preprocessor &PP,
- const cross_tu::CrossTranslationUnitContext &) {
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+
+ // TODO: Emit an error here.
+ if (Output.empty())
+ return;
+
C.push_back(new SarifDiagnostics(AnalyzerOpts, Output, PP.getLangOpts()));
+ createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, Output, PP, CTU);
}
static StringRef getFileName(const FileEntry &FE) {
@@ -106,7 +113,7 @@ static std::string fileNameToURI(StringRef Filename) {
}
});
- return Ret.str().str();
+ return std::string(Ret);
}
static json::Object createArtifactLocation(const FileEntry &FE) {
@@ -322,7 +329,7 @@ static json::Object createRule(const PathDiagnostic &Diag) {
{"name", CheckName},
{"id", CheckName}};
- std::string RuleURI = getRuleHelpURIStr(CheckName);
+ std::string RuleURI = std::string(getRuleHelpURIStr(CheckName));
if (!RuleURI.empty())
Ret["helpUri"] = RuleURI;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
index 85f60231a276..3709106ad44c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
@@ -44,8 +44,8 @@ ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State,
ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State,
NonLoc Cond, bool Assumption) {
State = assumeAux(State, Cond, Assumption);
- if (NotifyAssumeClients && SU)
- return SU->processAssume(State, Cond, Assumption);
+ if (NotifyAssumeClients && EE)
+ return EE->processAssume(State, Cond, Assumption);
return State;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index 84c52f53ca5e..2e269f6a596e 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -13,8 +13,8 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
using namespace clang;
@@ -652,6 +652,11 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
if (LHSValue == 0)
return evalCastFromNonLoc(lhs, resultTy);
return makeSymExprValNN(op, InputLHS, InputRHS, resultTy);
+ case BO_Rem:
+ // 0 % x == 0
+ if (LHSValue == 0)
+ return makeZeroVal(resultTy);
+ LLVM_FALLTHROUGH;
default:
return makeSymExprValNN(op, InputLHS, InputRHS, resultTy);
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Store.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Store.cpp
index b33129c88cea..ea617bbeeba1 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Store.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Store.cpp
@@ -134,7 +134,8 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
case MemRegion::FieldRegionKind:
case MemRegion::ObjCIvarRegionKind:
case MemRegion::ObjCStringRegionKind:
- case MemRegion::VarRegionKind:
+ case MemRegion::NonParamVarRegionKind:
+ case MemRegion::ParamVarRegionKind:
case MemRegion::CXXTempObjectRegionKind:
case MemRegion::CXXBaseObjectRegionKind:
case MemRegion::CXXDerivedObjectRegionKind:
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SubEngine.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SubEngine.cpp
deleted file mode 100644
index d7ddd9cf4610..000000000000
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SubEngine.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-//== SubEngine.cpp - Interface of the subengine of CoreEngine ------*- 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
-
-using namespace clang::ento;
-
-void SubEngine::anchor() { }
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
index 675209f6fd7e..6ca7aec9caec 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -34,45 +34,27 @@ using namespace ento;
void SymExpr::anchor() {}
-LLVM_DUMP_METHOD void SymExpr::dump() const {
- dumpToStream(llvm::errs());
-}
+LLVM_DUMP_METHOD void SymExpr::dump() const { dumpToStream(llvm::errs()); }
-void SymIntExpr::dumpToStream(raw_ostream &os) const {
- os << '(';
- getLHS()->dumpToStream(os);
- os << ") "
- << BinaryOperator::getOpcodeStr(getOpcode()) << ' ';
- if (getRHS().isUnsigned())
- os << getRHS().getZExtValue();
- else
- os << getRHS().getSExtValue();
- if (getRHS().isUnsigned())
- os << 'U';
+void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS, const SymExpr *Sym) {
+ OS << '(';
+ Sym->dumpToStream(OS);
+ OS << ')';
}
-void IntSymExpr::dumpToStream(raw_ostream &os) const {
- if (getLHS().isUnsigned())
- os << getLHS().getZExtValue();
+void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS,
+ const llvm::APSInt &Value) {
+ if (Value.isUnsigned())
+ OS << Value.getZExtValue();
else
- os << getLHS().getSExtValue();
- if (getLHS().isUnsigned())
- os << 'U';
- os << ' '
- << BinaryOperator::getOpcodeStr(getOpcode())
- << " (";
- getRHS()->dumpToStream(os);
- os << ')';
+ OS << Value.getSExtValue();
+ if (Value.isUnsigned())
+ OS << 'U';
}
-void SymSymExpr::dumpToStream(raw_ostream &os) const {
- os << '(';
- getLHS()->dumpToStream(os);
- os << ") "
- << BinaryOperator::getOpcodeStr(getOpcode())
- << " (";
- getRHS()->dumpToStream(os);
- os << ')';
+void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS,
+ BinaryOperator::Opcode Op) {
+ OS << ' ' << BinaryOperator::getOpcodeStr(Op) << ' ';
}
void SymbolCast::dumpToStream(raw_ostream &os) const {
@@ -329,7 +311,7 @@ QualType SymbolDerived::getType() const {
}
QualType SymbolExtent::getType() const {
- ASTContext &Ctx = R->getMemRegionManager()->getContext();
+ ASTContext &Ctx = R->getMemRegionManager().getContext();
return Ctx.getSizeType();
}
@@ -341,10 +323,6 @@ QualType SymbolRegionValue::getType() const {
return R->getValueType();
}
-SymbolManager::~SymbolManager() {
- llvm::DeleteContainerSeconds(SymbolDependencies);
-}
-
bool SymbolManager::canSymbolicate(QualType T) {
T = T.getCanonicalType();
@@ -362,13 +340,9 @@ bool SymbolManager::canSymbolicate(QualType T) {
void SymbolManager::addSymbolDependency(const SymbolRef Primary,
const SymbolRef Dependent) {
- SymbolDependTy::iterator I = SymbolDependencies.find(Primary);
- SymbolRefSmallVectorTy *dependencies = nullptr;
- if (I == SymbolDependencies.end()) {
- dependencies = new SymbolRefSmallVectorTy();
- SymbolDependencies[Primary] = dependencies;
- } else {
- dependencies = I->second;
+ auto &dependencies = SymbolDependencies[Primary];
+ if (!dependencies) {
+ dependencies = std::make_unique<SymbolRefSmallVectorTy>();
}
dependencies->push_back(Dependent);
}
@@ -378,7 +352,7 @@ const SymbolRefSmallVectorTy *SymbolManager::getDependentSymbols(
SymbolDependTy::const_iterator I = SymbolDependencies.find(Primary);
if (I == SymbolDependencies.end())
return nullptr;
- return I->second;
+ return I->second.get();
}
void SymbolReaper::markDependentsLive(SymbolRef sym) {
@@ -542,6 +516,11 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{
if (!Loc)
return true;
+ // Anonymous parameters of an inheriting constructor are live for the entire
+ // duration of the constructor.
+ if (isa<CXXInheritedCtorInitExpr>(Loc))
+ return true;
+
if (LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, VR->getDecl()))
return true;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp
new file mode 100644
index 000000000000..f4c7e5978e19
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp
@@ -0,0 +1,156 @@
+//===--- TextDiagnostics.cpp - Text Diagnostics for Paths -------*- 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 the TextDiagnostics object.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/PathDiagnostic.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Version.h"
+#include "clang/CrossTU/CrossTranslationUnit.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Casting.h"
+
+using namespace clang;
+using namespace ento;
+using namespace tooling;
+
+namespace {
+/// Emitsd minimal diagnostics (report message + notes) for the 'none' output
+/// type to the standard error, or to to compliment many others. Emits detailed
+/// diagnostics in textual format for the 'text' output type.
+class TextDiagnostics : public PathDiagnosticConsumer {
+ DiagnosticsEngine &DiagEng;
+ const LangOptions &LO;
+ const bool IncludePath = false;
+ const bool ShouldEmitAsError = false;
+ const bool ApplyFixIts = false;
+ const bool ShouldDisplayCheckerName = false;
+
+public:
+ TextDiagnostics(DiagnosticsEngine &DiagEng, const LangOptions &LO,
+ bool ShouldIncludePath, const AnalyzerOptions &AnOpts)
+ : DiagEng(DiagEng), LO(LO), IncludePath(ShouldIncludePath),
+ ShouldEmitAsError(AnOpts.AnalyzerWerror),
+ ApplyFixIts(AnOpts.ShouldApplyFixIts),
+ ShouldDisplayCheckerName(AnOpts.ShouldDisplayCheckerNameForText) {}
+ ~TextDiagnostics() override {}
+
+ StringRef getName() const override { return "TextDiagnostics"; }
+
+ bool supportsLogicalOpControlFlow() const override { return true; }
+ bool supportsCrossFileDiagnostics() const override { return true; }
+
+ PathGenerationScheme getGenerationScheme() const override {
+ return IncludePath ? Minimal : None;
+ }
+
+ void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
+ FilesMade *filesMade) override {
+ unsigned WarnID =
+ ShouldEmitAsError
+ ? DiagEng.getCustomDiagID(DiagnosticsEngine::Error, "%0")
+ : DiagEng.getCustomDiagID(DiagnosticsEngine::Warning, "%0");
+ unsigned NoteID = DiagEng.getCustomDiagID(DiagnosticsEngine::Note, "%0");
+ SourceManager &SM = DiagEng.getSourceManager();
+
+ Replacements Repls;
+ auto reportPiece = [&](unsigned ID, FullSourceLoc Loc, StringRef String,
+ ArrayRef<SourceRange> Ranges,
+ ArrayRef<FixItHint> Fixits) {
+ if (!ApplyFixIts) {
+ DiagEng.Report(Loc, ID) << String << Ranges << Fixits;
+ return;
+ }
+
+ DiagEng.Report(Loc, ID) << String << Ranges;
+ for (const FixItHint &Hint : Fixits) {
+ Replacement Repl(SM, Hint.RemoveRange, Hint.CodeToInsert);
+
+ if (llvm::Error Err = Repls.add(Repl)) {
+ llvm::errs() << "Error applying replacement " << Repl.toString()
+ << ": " << Err << "\n";
+ }
+ }
+ };
+
+ for (std::vector<const PathDiagnostic *>::iterator I = Diags.begin(),
+ E = Diags.end();
+ I != E; ++I) {
+ const PathDiagnostic *PD = *I;
+ std::string WarningMsg =
+ (ShouldDisplayCheckerName ? " [" + PD->getCheckerName() + "]" : "")
+ .str();
+
+ reportPiece(WarnID, PD->getLocation().asLocation(),
+ (PD->getShortDescription() + WarningMsg).str(),
+ PD->path.back()->getRanges(), PD->path.back()->getFixits());
+
+ // First, add extra notes, even if paths should not be included.
+ for (const auto &Piece : PD->path) {
+ if (!isa<PathDiagnosticNotePiece>(Piece.get()))
+ continue;
+
+ reportPiece(NoteID, Piece->getLocation().asLocation(),
+ Piece->getString(), Piece->getRanges(),
+ Piece->getFixits());
+ }
+
+ if (!IncludePath)
+ continue;
+
+ // Then, add the path notes if necessary.
+ PathPieces FlatPath = PD->path.flatten(/*ShouldFlattenMacros=*/true);
+ for (const auto &Piece : FlatPath) {
+ if (isa<PathDiagnosticNotePiece>(Piece.get()))
+ continue;
+
+ reportPiece(NoteID, Piece->getLocation().asLocation(),
+ Piece->getString(), Piece->getRanges(),
+ Piece->getFixits());
+ }
+ }
+
+ if (!ApplyFixIts || Repls.empty())
+ return;
+
+ Rewriter Rewrite(SM, LO);
+ if (!applyAllReplacements(Repls, Rewrite)) {
+ llvm::errs() << "An error occured during applying fix-it.\n";
+ }
+
+ Rewrite.overwriteChangedFiles();
+ }
+};
+} // end anonymous namespace
+
+void ento::createTextPathDiagnosticConsumer(
+ AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ const std::string &Prefix, const clang::Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+ C.emplace_back(new TextDiagnostics(PP.getDiagnostics(), PP.getLangOpts(),
+ /*ShouldIncludePath*/ true, AnalyzerOpts));
+}
+
+void ento::createTextMinimalPathDiagnosticConsumer(
+ AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ const std::string &Prefix, const clang::Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+ C.emplace_back(new TextDiagnostics(PP.getDiagnostics(), PP.getLangOpts(),
+ /*ShouldIncludePath*/ false,
+ AnalyzerOpts));
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index fea8100c3b3b..392049e21c6e 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -12,7 +12,6 @@
#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
#include "ModelInjector.h"
-#include "clang/Analysis/PathDiagnostic.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
@@ -21,10 +20,12 @@
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CallGraph.h"
#include "clang/Analysis/CodeInjector.h"
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/CrossTU/CrossTranslationUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
+#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
@@ -32,7 +33,6 @@
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/FileSystem.h"
@@ -61,114 +61,6 @@ STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks.");
STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function.");
//===----------------------------------------------------------------------===//
-// Special PathDiagnosticConsumers.
-//===----------------------------------------------------------------------===//
-
-void ento::createPlistHTMLDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
- const std::string &prefix, const Preprocessor &PP,
- const cross_tu::CrossTranslationUnitContext &CTU) {
- createHTMLDiagnosticConsumer(AnalyzerOpts, C,
- llvm::sys::path::parent_path(prefix), PP, CTU);
- createPlistMultiFileDiagnosticConsumer(AnalyzerOpts, C, prefix, PP, CTU);
-}
-
-void ento::createTextPathDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
- const std::string &Prefix, const clang::Preprocessor &PP,
- const cross_tu::CrossTranslationUnitContext &CTU) {
- llvm_unreachable("'text' consumer should be enabled on ClangDiags");
-}
-
-namespace {
-class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer {
- DiagnosticsEngine &Diag;
- bool IncludePath = false, ShouldEmitAsError = false, FixitsAsRemarks = false;
-
-public:
- ClangDiagPathDiagConsumer(DiagnosticsEngine &Diag)
- : Diag(Diag) {}
- ~ClangDiagPathDiagConsumer() override {}
- StringRef getName() const override { return "ClangDiags"; }
-
- bool supportsLogicalOpControlFlow() const override { return true; }
- bool supportsCrossFileDiagnostics() const override { return true; }
-
- PathGenerationScheme getGenerationScheme() const override {
- return IncludePath ? Minimal : None;
- }
-
- void enablePaths() { IncludePath = true; }
- void enableWerror() { ShouldEmitAsError = true; }
- void enableFixitsAsRemarks() { FixitsAsRemarks = true; }
-
- void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
- FilesMade *filesMade) override {
- unsigned WarnID =
- ShouldEmitAsError
- ? Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0")
- : Diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0");
- unsigned NoteID = Diag.getCustomDiagID(DiagnosticsEngine::Note, "%0");
- unsigned RemarkID = Diag.getCustomDiagID(DiagnosticsEngine::Remark, "%0");
-
- auto reportPiece =
- [&](unsigned ID, SourceLocation Loc, StringRef String,
- ArrayRef<SourceRange> Ranges, ArrayRef<FixItHint> Fixits) {
- if (!FixitsAsRemarks) {
- Diag.Report(Loc, ID) << String << Ranges << Fixits;
- } else {
- Diag.Report(Loc, ID) << String << Ranges;
- for (const FixItHint &Hint : Fixits) {
- SourceManager &SM = Diag.getSourceManager();
- llvm::SmallString<128> Str;
- llvm::raw_svector_ostream OS(Str);
- // FIXME: Add support for InsertFromRange and
- // BeforePreviousInsertion.
- assert(!Hint.InsertFromRange.isValid() && "Not implemented yet!");
- assert(!Hint.BeforePreviousInsertions && "Not implemented yet!");
- OS << SM.getSpellingColumnNumber(Hint.RemoveRange.getBegin())
- << "-" << SM.getSpellingColumnNumber(Hint.RemoveRange.getEnd())
- << ": '" << Hint.CodeToInsert << "'";
- Diag.Report(Loc, RemarkID) << OS.str();
- }
- }
- };
-
- for (std::vector<const PathDiagnostic *>::iterator I = Diags.begin(),
- E = Diags.end();
- I != E; ++I) {
- const PathDiagnostic *PD = *I;
- reportPiece(WarnID, PD->getLocation().asLocation(),
- PD->getShortDescription(), PD->path.back()->getRanges(),
- PD->path.back()->getFixits());
-
- // First, add extra notes, even if paths should not be included.
- for (const auto &Piece : PD->path) {
- if (!isa<PathDiagnosticNotePiece>(Piece.get()))
- continue;
-
- reportPiece(NoteID, Piece->getLocation().asLocation(),
- Piece->getString(), Piece->getRanges(), Piece->getFixits());
- }
-
- if (!IncludePath)
- continue;
-
- // Then, add the path notes if necessary.
- PathPieces FlatPath = PD->path.flatten(/*ShouldFlattenMacros=*/true);
- for (const auto &Piece : FlatPath) {
- if (isa<PathDiagnosticNotePiece>(Piece.get()))
- continue;
-
- reportPiece(NoteID, Piece->getLocation().asLocation(),
- Piece->getString(), Piece->getRanges(), Piece->getFixits());
- }
- }
- }
-};
-} // end anonymous namespace
-
-//===----------------------------------------------------------------------===//
// AnalysisConsumer declaration.
//===----------------------------------------------------------------------===//
@@ -192,7 +84,7 @@ class AnalysisConsumer : public AnalysisASTConsumer,
public:
ASTContext *Ctx;
- const Preprocessor &PP;
+ Preprocessor &PP;
const std::string OutDir;
AnalyzerOptionsRef Opts;
ArrayRef<std::string> Plugins;
@@ -253,31 +145,16 @@ public:
}
void DigestAnalyzerOptions() {
- if (Opts->AnalysisDiagOpt != PD_NONE) {
- // Create the PathDiagnosticConsumer.
- ClangDiagPathDiagConsumer *clangDiags =
- new ClangDiagPathDiagConsumer(PP.getDiagnostics());
- PathConsumers.push_back(clangDiags);
-
- if (Opts->AnalyzerWerror)
- clangDiags->enableWerror();
-
- if (Opts->ShouldEmitFixItHintsAsRemarks)
- clangDiags->enableFixitsAsRemarks();
-
- if (Opts->AnalysisDiagOpt == PD_TEXT) {
- clangDiags->enablePaths();
-
- } else if (!OutDir.empty()) {
- switch (Opts->AnalysisDiagOpt) {
- default:
+ switch (Opts->AnalysisDiagOpt) {
+ case PD_NONE:
+ break;
#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN) \
case PD_##NAME: \
CREATEFN(*Opts.get(), PathConsumers, OutDir, PP, CTU); \
break;
#include "clang/StaticAnalyzer/Core/Analyses.def"
- }
- }
+ default:
+ llvm_unreachable("Unknown analyzer output type!");
}
// Create the analyzer component creators.
@@ -313,30 +190,29 @@ public:
else if (Mode == AM_Path) {
llvm::errs() << " (Path, ";
switch (IMode) {
- case ExprEngine::Inline_Minimal:
- llvm::errs() << " Inline_Minimal";
- break;
- case ExprEngine::Inline_Regular:
- llvm::errs() << " Inline_Regular";
- break;
+ case ExprEngine::Inline_Minimal:
+ llvm::errs() << " Inline_Minimal";
+ break;
+ case ExprEngine::Inline_Regular:
+ llvm::errs() << " Inline_Regular";
+ break;
}
llvm::errs() << ")";
- }
- else
+ } else
assert(Mode == (AM_Syntax | AM_Path) && "Unexpected mode!");
- llvm::errs() << ": " << Loc.getFilename() << ' '
- << getFunctionName(D) << '\n';
+ llvm::errs() << ": " << Loc.getFilename() << ' ' << getFunctionName(D)
+ << '\n';
}
}
void Initialize(ASTContext &Context) override {
Ctx = &Context;
- checkerMgr = createCheckerManager(
- *Ctx, *Opts, Plugins, CheckerRegistrationFns, PP.getDiagnostics());
+ checkerMgr = std::make_unique<CheckerManager>(*Ctx, *Opts, PP, Plugins,
+ CheckerRegistrationFns);
- Mgr = std::make_unique<AnalysisManager>(*Ctx, PathConsumers, CreateStoreMgr,
- CreateConstraintMgr,
+ Mgr = std::make_unique<AnalysisManager>(*Ctx, PP, PathConsumers,
+ CreateStoreMgr, CreateConstraintMgr,
checkerMgr.get(), *Opts, Injector);
}
@@ -469,7 +345,7 @@ private:
/// Print \p S to stderr if \c Opts->AnalyzerDisplayProgress is set.
void reportAnalyzerProgress(StringRef S);
-};
+}; // namespace
} // end anonymous namespace
@@ -503,6 +379,13 @@ static bool shouldSkipFunction(const Decl *D,
if (VisitedAsTopLevel.count(D))
return true;
+ // Skip analysis of inheriting constructors as top-level functions. These
+ // constructors don't even have a body written down in the code, so even if
+ // we find a bug, we won't be able to display it.
+ if (const auto *CD = dyn_cast<CXXConstructorDecl>(D))
+ if (CD->isInheritingConstructor())
+ return true;
+
// We want to re-analyse the functions as top level in the following cases:
// - The 'init' methods should be reanalyzed because
// ObjCNonNilReturnValueChecker assumes that '[super init]' never returns
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp
index f4f06e32cd1d..eb6014a0629d 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp
@@ -10,8 +10,9 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
+#include "clang/StaticAnalyzer/Frontend/AnalyzerHelpFlags.h"
#include "clang/Basic/Diagnostic.h"
+#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -24,53 +25,36 @@
using namespace clang;
using namespace ento;
-std::unique_ptr<CheckerManager> ento::createCheckerManager(
- ASTContext &context,
- AnalyzerOptions &opts,
- ArrayRef<std::string> plugins,
- ArrayRef<std::function<void(CheckerRegistry &)>> checkerRegistrationFns,
- DiagnosticsEngine &diags) {
- auto checkerMgr = std::make_unique<CheckerManager>(context, opts);
-
- CheckerRegistry allCheckers(plugins, diags, opts, context.getLangOpts(),
- checkerRegistrationFns);
-
- allCheckers.initializeManager(*checkerMgr);
- allCheckers.validateCheckerOptions();
- checkerMgr->finishedCheckerRegistration();
-
- return checkerMgr;
-}
-
-void ento::printCheckerHelp(raw_ostream &out, ArrayRef<std::string> plugins,
- AnalyzerOptions &anopts,
- DiagnosticsEngine &diags,
- const LangOptions &langOpts) {
+void ento::printCheckerHelp(raw_ostream &out, CompilerInstance &CI) {
out << "OVERVIEW: Clang Static Analyzer Checkers List\n\n";
out << "USAGE: -analyzer-checker <CHECKER or PACKAGE,...>\n\n";
- CheckerRegistry(plugins, diags, anopts, langOpts)
- .printCheckerWithDescList(out);
+ auto CheckerMgr = std::make_unique<CheckerManager>(
+ *CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(),
+ CI.getFrontendOpts().Plugins);
+
+ CheckerMgr->getCheckerRegistryData().printCheckerWithDescList(
+ *CI.getAnalyzerOpts(), out);
}
-void ento::printEnabledCheckerList(raw_ostream &out,
- ArrayRef<std::string> plugins,
- AnalyzerOptions &anopts,
- DiagnosticsEngine &diags,
- const LangOptions &langOpts) {
+void ento::printEnabledCheckerList(raw_ostream &out, CompilerInstance &CI) {
out << "OVERVIEW: Clang Static Analyzer Enabled Checkers List\n\n";
- CheckerRegistry(plugins, diags, anopts, langOpts)
- .printEnabledCheckerList(out);
+ auto CheckerMgr = std::make_unique<CheckerManager>(
+ *CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(),
+ CI.getFrontendOpts().Plugins);
+
+ CheckerMgr->getCheckerRegistryData().printEnabledCheckerList(out);
}
-void ento::printCheckerConfigList(raw_ostream &OS,
- ArrayRef<std::string> plugins,
- AnalyzerOptions &opts,
- DiagnosticsEngine &diags,
- const LangOptions &LangOpts) {
- CheckerRegistry(plugins, diags, opts, LangOpts)
- .printCheckerOptionList(OS);
+void ento::printCheckerConfigList(raw_ostream &out, CompilerInstance &CI) {
+
+ auto CheckerMgr = std::make_unique<CheckerManager>(
+ *CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(),
+ CI.getFrontendOpts().Plugins);
+
+ CheckerMgr->getCheckerRegistryData().printCheckerOptionList(
+ *CI.getAnalyzerOpts(), out);
}
void ento::printAnalyzerConfigList(raw_ostream &out) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
index f5c05281adab..528284ca8985 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
@@ -25,9 +25,12 @@
using namespace clang;
using namespace ento;
+using namespace checker_registry;
using llvm::sys::DynamicLibrary;
-using RegisterCheckersFn = void (*)(CheckerRegistry &);
+//===----------------------------------------------------------------------===//
+// Utilities.
+//===----------------------------------------------------------------------===//
static bool isCompatibleAPIVersion(const char *VersionString) {
// If the version string is null, its not an analyzer plugin.
@@ -39,80 +42,17 @@ static bool isCompatibleAPIVersion(const char *VersionString) {
return strcmp(VersionString, CLANG_ANALYZER_API_VERSION_STRING) == 0;
}
-namespace {
-template <class T> struct FullNameLT {
- bool operator()(const T &Lhs, const T &Rhs) {
- return Lhs.FullName < Rhs.FullName;
- }
-};
-
-using PackageNameLT = FullNameLT<CheckerRegistry::PackageInfo>;
-using CheckerNameLT = FullNameLT<CheckerRegistry::CheckerInfo>;
-} // end of anonymous namespace
-
-template <class CheckerOrPackageInfoList>
-static
- typename std::conditional<std::is_const<CheckerOrPackageInfoList>::value,
- typename CheckerOrPackageInfoList::const_iterator,
- typename CheckerOrPackageInfoList::iterator>::type
- binaryFind(CheckerOrPackageInfoList &Collection, StringRef FullName) {
-
- using CheckerOrPackage = typename CheckerOrPackageInfoList::value_type;
- using CheckerOrPackageFullNameLT = FullNameLT<CheckerOrPackage>;
-
- assert(std::is_sorted(Collection.begin(), Collection.end(),
- CheckerOrPackageFullNameLT{}) &&
- "In order to efficiently gather checkers/packages, this function "
- "expects them to be already sorted!");
-
- return llvm::lower_bound(Collection, CheckerOrPackage(FullName),
- CheckerOrPackageFullNameLT{});
-}
-
static constexpr char PackageSeparator = '.';
-static bool isInPackage(const CheckerRegistry::CheckerInfo &Checker,
- StringRef PackageName) {
- // Does the checker's full name have the package as a prefix?
- if (!Checker.FullName.startswith(PackageName))
- return false;
-
- // Is the package actually just the name of a specific checker?
- if (Checker.FullName.size() == PackageName.size())
- return true;
-
- // Is the checker in the package (or a subpackage)?
- if (Checker.FullName[PackageName.size()] == PackageSeparator)
- return true;
-
- return false;
-}
-
-CheckerRegistry::CheckerInfoListRange
-CheckerRegistry::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) {
- auto It = binaryFind(Checkers, CmdLineArg);
-
- if (!isInPackage(*It, CmdLineArg))
- return {Checkers.end(), Checkers.end()};
-
- // See how large the package is.
- // If the package doesn't exist, assume the option refers to a single
- // checker.
- size_t Size = 1;
- llvm::StringMap<size_t>::const_iterator PackageSize =
- PackageSizes.find(CmdLineArg);
-
- if (PackageSize != PackageSizes.end())
- Size = PackageSize->getValue();
-
- return {It, It + Size};
-}
+//===----------------------------------------------------------------------===//
+// Methods of CheckerRegistry.
+//===----------------------------------------------------------------------===//
CheckerRegistry::CheckerRegistry(
- ArrayRef<std::string> Plugins, DiagnosticsEngine &Diags,
- AnalyzerOptions &AnOpts, const LangOptions &LangOpts,
+ CheckerRegistryData &Data, ArrayRef<std::string> Plugins,
+ DiagnosticsEngine &Diags, AnalyzerOptions &AnOpts,
ArrayRef<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns)
- : Diags(Diags), AnOpts(AnOpts), LangOpts(LangOpts) {
+ : Data(Data), Diags(Diags), AnOpts(AnOpts) {
// Register builtin checkers.
#define GET_CHECKERS
@@ -152,9 +92,10 @@ CheckerRegistry::CheckerRegistry(
continue;
}
+ using RegisterPluginCheckerFn = void (*)(CheckerRegistry &);
// Register its checkers.
- RegisterCheckersFn RegisterPluginCheckers =
- reinterpret_cast<RegisterCheckersFn>(
+ RegisterPluginCheckerFn RegisterPluginCheckers =
+ reinterpret_cast<RegisterPluginCheckerFn>(
Lib.getAddressOfSymbol("clang_registerCheckers"));
if (RegisterPluginCheckers)
RegisterPluginCheckers(*this);
@@ -171,38 +112,67 @@ CheckerRegistry::CheckerRegistry(
// FIXME: Alphabetical sort puts 'experimental' in the middle.
// Would it be better to name it '~experimental' or something else
// that's ASCIIbetically last?
- llvm::sort(Packages, PackageNameLT{});
- llvm::sort(Checkers, CheckerNameLT{});
+ llvm::sort(Data.Packages, checker_registry::PackageNameLT{});
+ llvm::sort(Data.Checkers, checker_registry::CheckerNameLT{});
#define GET_CHECKER_DEPENDENCIES
#define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) \
addDependency(FULLNAME, DEPENDENCY);
+#define GET_CHECKER_WEAK_DEPENDENCIES
+
+#define CHECKER_WEAK_DEPENDENCY(FULLNAME, DEPENDENCY) \
+ addWeakDependency(FULLNAME, DEPENDENCY);
+
#define GET_CHECKER_OPTIONS
-#define CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, DEVELOPMENT_STATUS, IS_HIDDEN) \
- addCheckerOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, DEVELOPMENT_STATUS, IS_HIDDEN);
+#define CHECKER_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, \
+ DEVELOPMENT_STATUS, IS_HIDDEN) \
+ addCheckerOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, \
+ DEVELOPMENT_STATUS, IS_HIDDEN);
#define GET_PACKAGE_OPTIONS
-#define PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, DEVELOPMENT_STATUS, IS_HIDDEN) \
- addPackageOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, DEVELOPMENT_STATUS, IS_HIDDEN);
+#define PACKAGE_OPTION(TYPE, FULLNAME, CMDFLAG, DESC, DEFAULT_VAL, \
+ DEVELOPMENT_STATUS, IS_HIDDEN) \
+ addPackageOption(TYPE, FULLNAME, CMDFLAG, DEFAULT_VAL, DESC, \
+ DEVELOPMENT_STATUS, IS_HIDDEN);
#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
#undef CHECKER_DEPENDENCY
#undef GET_CHECKER_DEPENDENCIES
+#undef CHECKER_WEAK_DEPENDENCY
+#undef GET_CHECKER_WEAK_DEPENDENCIES
#undef CHECKER_OPTION
#undef GET_CHECKER_OPTIONS
#undef PACKAGE_OPTION
#undef GET_PACKAGE_OPTIONS
- resolveDependencies();
+ resolveDependencies<true>();
+ resolveDependencies<false>();
+
+#ifndef NDEBUG
+ for (auto &DepPair : Data.Dependencies) {
+ for (auto &WeakDepPair : Data.WeakDependencies) {
+ // Some assertions to enforce that strong dependencies are relations in
+ // between purely modeling checkers, and weak dependencies are about
+ // diagnostics.
+ assert(WeakDepPair != DepPair &&
+ "A checker cannot strong and weak depend on the same checker!");
+ assert(WeakDepPair.first != DepPair.second &&
+ "A strong dependency mustn't have weak dependencies!");
+ assert(WeakDepPair.second != DepPair.second &&
+ "A strong dependency mustn't be a weak dependency as well!");
+ }
+ }
+#endif
+
resolveCheckerAndPackageOptions();
// Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the
// command line.
for (const std::pair<std::string, bool> &Opt : AnOpts.CheckersAndPackages) {
CheckerInfoListRange CheckerForCmdLineArg =
- getMutableCheckersForCmdLineArg(Opt.first);
+ Data.getMutableCheckersForCmdLineArg(Opt.first);
if (CheckerForCmdLineArg.begin() == CheckerForCmdLineArg.end()) {
Diags.Report(diag::err_unknown_analyzer_checker_or_package) << Opt.first;
@@ -214,109 +184,169 @@ CheckerRegistry::CheckerRegistry(
: StateFromCmdLine::State_Disabled;
}
}
+ validateCheckerOptions();
}
-/// Collects dependencies in \p ret, returns false on failure.
-static bool
-collectDependenciesImpl(const CheckerRegistry::ConstCheckerInfoList &Deps,
- const LangOptions &LO,
- CheckerRegistry::CheckerInfoSet &Ret);
-
-/// Collects dependenies in \p enabledCheckers. Return None on failure.
-LLVM_NODISCARD
-static llvm::Optional<CheckerRegistry::CheckerInfoSet>
-collectDependencies(const CheckerRegistry::CheckerInfo &checker,
- const LangOptions &LO) {
-
- CheckerRegistry::CheckerInfoSet Ret;
- // Add dependencies to the enabled checkers only if all of them can be
- // enabled.
- if (!collectDependenciesImpl(checker.Dependencies, LO, Ret))
- return None;
-
- return Ret;
-}
-
-static bool
-collectDependenciesImpl(const CheckerRegistry::ConstCheckerInfoList &Deps,
- const LangOptions &LO,
- CheckerRegistry::CheckerInfoSet &Ret) {
+//===----------------------------------------------------------------------===//
+// Dependency resolving.
+//===----------------------------------------------------------------------===//
- for (const CheckerRegistry::CheckerInfo *Dependency : Deps) {
+template <typename IsEnabledFn>
+static bool collectStrongDependencies(const ConstCheckerInfoList &Deps,
+ const CheckerManager &Mgr,
+ CheckerInfoSet &Ret,
+ IsEnabledFn IsEnabled);
+
+/// Collects weak dependencies in \p enabledData.Checkers.
+template <typename IsEnabledFn>
+static void collectWeakDependencies(const ConstCheckerInfoList &Deps,
+ const CheckerManager &Mgr,
+ CheckerInfoSet &Ret, IsEnabledFn IsEnabled);
+
+void CheckerRegistry::initializeRegistry(const CheckerManager &Mgr) {
+ // First, we calculate the list of enabled checkers as specified by the
+ // invocation. Weak dependencies will not enable their unspecified strong
+ // depenencies, but its only after resolving strong dependencies for all
+ // checkers when we know whether they will be enabled.
+ CheckerInfoSet Tmp;
+ auto IsEnabledFromCmdLine = [&](const CheckerInfo *Checker) {
+ return !Checker->isDisabled(Mgr);
+ };
+ for (const CheckerInfo &Checker : Data.Checkers) {
+ if (!Checker.isEnabled(Mgr))
+ continue;
- if (Dependency->isDisabled(LO))
- return false;
+ CheckerInfoSet Deps;
+ if (!collectStrongDependencies(Checker.Dependencies, Mgr, Deps,
+ IsEnabledFromCmdLine)) {
+ // If we failed to enable any of the dependencies, don't enable this
+ // checker.
+ continue;
+ }
- // Collect dependencies recursively.
- if (!collectDependenciesImpl(Dependency->Dependencies, LO, Ret))
- return false;
+ Tmp.insert(Deps.begin(), Deps.end());
- Ret.insert(Dependency);
+ // Enable the checker.
+ Tmp.insert(&Checker);
}
- return true;
-}
-
-CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers() const {
-
- CheckerInfoSet EnabledCheckers;
-
- for (const CheckerInfo &Checker : Checkers) {
- if (!Checker.isEnabled(LangOpts))
+ // Calculate enabled checkers with the correct registration order. As this is
+ // done recursively, its arguably cheaper, but for sure less error prone to
+ // recalculate from scratch.
+ auto IsEnabled = [&](const CheckerInfo *Checker) {
+ return llvm::is_contained(Tmp, Checker);
+ };
+ for (const CheckerInfo &Checker : Data.Checkers) {
+ if (!Checker.isEnabled(Mgr))
continue;
- // Recursively enable its dependencies.
- llvm::Optional<CheckerInfoSet> Deps =
- collectDependencies(Checker, LangOpts);
+ CheckerInfoSet Deps;
- if (!Deps) {
+ collectWeakDependencies(Checker.WeakDependencies, Mgr, Deps, IsEnabled);
+
+ if (!collectStrongDependencies(Checker.Dependencies, Mgr, Deps,
+ IsEnabledFromCmdLine)) {
// If we failed to enable any of the dependencies, don't enable this
// checker.
continue;
}
// Note that set_union also preserves the order of insertion.
- EnabledCheckers.set_union(*Deps);
+ Data.EnabledCheckers.set_union(Deps);
+ Data.EnabledCheckers.insert(&Checker);
+ }
+}
- // Enable the checker.
- EnabledCheckers.insert(&Checker);
+template <typename IsEnabledFn>
+static bool collectStrongDependencies(const ConstCheckerInfoList &Deps,
+ const CheckerManager &Mgr,
+ CheckerInfoSet &Ret,
+ IsEnabledFn IsEnabled) {
+
+ for (const CheckerInfo *Dependency : Deps) {
+ if (!IsEnabled(Dependency))
+ return false;
+
+ // Collect dependencies recursively.
+ if (!collectStrongDependencies(Dependency->Dependencies, Mgr, Ret,
+ IsEnabled))
+ return false;
+ Ret.insert(Dependency);
}
- return EnabledCheckers;
+ return true;
+}
+
+template <typename IsEnabledFn>
+static void collectWeakDependencies(const ConstCheckerInfoList &WeakDeps,
+ const CheckerManager &Mgr,
+ CheckerInfoSet &Ret,
+ IsEnabledFn IsEnabled) {
+
+ for (const CheckerInfo *Dependency : WeakDeps) {
+ // Don't enable this checker if strong dependencies are unsatisfied, but
+ // assume that weak dependencies are transitive.
+ collectWeakDependencies(Dependency->WeakDependencies, Mgr, Ret, IsEnabled);
+
+ if (IsEnabled(Dependency) &&
+ collectStrongDependencies(Dependency->Dependencies, Mgr, Ret,
+ IsEnabled))
+ Ret.insert(Dependency);
+ }
}
-void CheckerRegistry::resolveDependencies() {
- for (const std::pair<StringRef, StringRef> &Entry : Dependencies) {
- auto CheckerIt = binaryFind(Checkers, Entry.first);
- assert(CheckerIt != Checkers.end() && CheckerIt->FullName == Entry.first &&
+template <bool IsWeak> void CheckerRegistry::resolveDependencies() {
+ for (const std::pair<StringRef, StringRef> &Entry :
+ (IsWeak ? Data.WeakDependencies : Data.Dependencies)) {
+
+ auto CheckerIt = binaryFind(Data.Checkers, Entry.first);
+ assert(CheckerIt != Data.Checkers.end() &&
+ CheckerIt->FullName == Entry.first &&
"Failed to find the checker while attempting to set up its "
"dependencies!");
- auto DependencyIt = binaryFind(Checkers, Entry.second);
- assert(DependencyIt != Checkers.end() &&
+ auto DependencyIt = binaryFind(Data.Checkers, Entry.second);
+ assert(DependencyIt != Data.Checkers.end() &&
DependencyIt->FullName == Entry.second &&
"Failed to find the dependency of a checker!");
- CheckerIt->Dependencies.emplace_back(&*DependencyIt);
+ // We do allow diagnostics from unit test/example dependency checkers.
+ assert((DependencyIt->FullName.startswith("test") ||
+ DependencyIt->FullName.startswith("example") || IsWeak ||
+ DependencyIt->IsHidden) &&
+ "Strong dependencies are modeling checkers, and as such "
+ "non-user facing! Mark them hidden in Checkers.td!");
+
+ if (IsWeak)
+ CheckerIt->WeakDependencies.emplace_back(&*DependencyIt);
+ else
+ CheckerIt->Dependencies.emplace_back(&*DependencyIt);
}
-
- Dependencies.clear();
}
void CheckerRegistry::addDependency(StringRef FullName, StringRef Dependency) {
- Dependencies.emplace_back(FullName, Dependency);
+ Data.Dependencies.emplace_back(FullName, Dependency);
}
+void CheckerRegistry::addWeakDependency(StringRef FullName,
+ StringRef Dependency) {
+ Data.WeakDependencies.emplace_back(FullName, Dependency);
+}
+
+//===----------------------------------------------------------------------===//
+// Checker option resolving and validating.
+//===----------------------------------------------------------------------===//
+
/// Insert the checker/package option to AnalyzerOptions' config table, and
/// validate it, if the user supplied it on the command line.
-static void insertAndValidate(StringRef FullName,
- const CheckerRegistry::CmdLineOption &Option,
+static void insertAndValidate(StringRef FullName, const CmdLineOption &Option,
AnalyzerOptions &AnOpts,
DiagnosticsEngine &Diags) {
std::string FullOption = (FullName + ":" + Option.OptionName).str();
- auto It = AnOpts.Config.insert({FullOption, Option.DefaultValStr});
+ auto It =
+ AnOpts.Config.insert({FullOption, std::string(Option.DefaultValStr)});
// Insertation was successful -- CmdLineOption's constructor will validate
// whether values received from plugins or TableGen files are correct.
@@ -337,7 +367,7 @@ static void insertAndValidate(StringRef FullName,
<< FullOption << "a boolean value";
}
- It.first->setValue(Option.DefaultValStr);
+ It.first->setValue(std::string(Option.DefaultValStr));
}
return;
}
@@ -351,17 +381,17 @@ static void insertAndValidate(StringRef FullName,
<< FullOption << "an integer value";
}
- It.first->setValue(Option.DefaultValStr);
+ It.first->setValue(std::string(Option.DefaultValStr));
}
return;
}
}
template <class T>
-static void
-insertOptionToCollection(StringRef FullName, T &Collection,
- const CheckerRegistry::CmdLineOption &Option,
- AnalyzerOptions &AnOpts, DiagnosticsEngine &Diags) {
+static void insertOptionToCollection(StringRef FullName, T &Collection,
+ const CmdLineOption &Option,
+ AnalyzerOptions &AnOpts,
+ DiagnosticsEngine &Diags) {
auto It = binaryFind(Collection, FullName);
assert(It != Collection.end() &&
"Failed to find the checker while attempting to add a command line "
@@ -374,22 +404,20 @@ insertOptionToCollection(StringRef FullName, T &Collection,
void CheckerRegistry::resolveCheckerAndPackageOptions() {
for (const std::pair<StringRef, CmdLineOption> &CheckerOptEntry :
- CheckerOptions) {
- insertOptionToCollection(CheckerOptEntry.first, Checkers,
+ Data.CheckerOptions) {
+ insertOptionToCollection(CheckerOptEntry.first, Data.Checkers,
CheckerOptEntry.second, AnOpts, Diags);
}
- CheckerOptions.clear();
for (const std::pair<StringRef, CmdLineOption> &PackageOptEntry :
- PackageOptions) {
- insertOptionToCollection(PackageOptEntry.first, Packages,
+ Data.PackageOptions) {
+ insertOptionToCollection(PackageOptEntry.first, Data.Packages,
PackageOptEntry.second, AnOpts, Diags);
}
- PackageOptions.clear();
}
void CheckerRegistry::addPackage(StringRef FullName) {
- Packages.emplace_back(PackageInfo(FullName));
+ Data.Packages.emplace_back(PackageInfo(FullName));
}
void CheckerRegistry::addPackageOption(StringRef OptionType,
@@ -399,22 +427,22 @@ void CheckerRegistry::addPackageOption(StringRef OptionType,
StringRef Description,
StringRef DevelopmentStatus,
bool IsHidden) {
- PackageOptions.emplace_back(
+ Data.PackageOptions.emplace_back(
PackageFullName, CmdLineOption{OptionType, OptionName, DefaultValStr,
Description, DevelopmentStatus, IsHidden});
}
-void CheckerRegistry::addChecker(InitializationFunction Rfn,
+void CheckerRegistry::addChecker(RegisterCheckerFn Rfn,
ShouldRegisterFunction Sfn, StringRef Name,
StringRef Desc, StringRef DocsUri,
bool IsHidden) {
- Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri, IsHidden);
+ Data.Checkers.emplace_back(Rfn, Sfn, Name, Desc, DocsUri, IsHidden);
// Record the presence of the checker in its packages.
StringRef PackageName, LeafName;
std::tie(PackageName, LeafName) = Name.rsplit(PackageSeparator);
while (!LeafName.empty()) {
- PackageSizes[PackageName] += 1;
+ Data.PackageSizes[PackageName] += 1;
std::tie(PackageName, LeafName) = PackageName.rsplit(PackageSeparator);
}
}
@@ -426,37 +454,33 @@ void CheckerRegistry::addCheckerOption(StringRef OptionType,
StringRef Description,
StringRef DevelopmentStatus,
bool IsHidden) {
- CheckerOptions.emplace_back(
+ Data.CheckerOptions.emplace_back(
CheckerFullName, CmdLineOption{OptionType, OptionName, DefaultValStr,
Description, DevelopmentStatus, IsHidden});
}
void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) const {
- // Collect checkers enabled by the options.
- CheckerInfoSet enabledCheckers = getEnabledCheckers();
-
// Initialize the CheckerManager with all enabled checkers.
- for (const auto *Checker : enabledCheckers) {
+ for (const auto *Checker : Data.EnabledCheckers) {
CheckerMgr.setCurrentCheckerName(CheckerNameRef(Checker->FullName));
Checker->Initialize(CheckerMgr);
}
}
-static void
-isOptionContainedIn(const CheckerRegistry::CmdLineOptionList &OptionList,
- StringRef SuppliedChecker, StringRef SuppliedOption,
- const AnalyzerOptions &AnOpts, DiagnosticsEngine &Diags) {
+static void isOptionContainedIn(const CmdLineOptionList &OptionList,
+ StringRef SuppliedChecker,
+ StringRef SuppliedOption,
+ const AnalyzerOptions &AnOpts,
+ DiagnosticsEngine &Diags) {
if (!AnOpts.ShouldEmitErrorsOnInvalidConfigValue)
return;
- using CmdLineOption = CheckerRegistry::CmdLineOption;
-
auto SameOptName = [SuppliedOption](const CmdLineOption &Opt) {
return Opt.OptionName == SuppliedOption;
};
- auto OptionIt = llvm::find_if(OptionList, SameOptName);
+ const auto *OptionIt = llvm::find_if(OptionList, SameOptName);
if (OptionIt == OptionList.end()) {
Diags.Report(diag::err_analyzer_checker_option_unknown)
@@ -485,16 +509,16 @@ void CheckerRegistry::validateCheckerOptions() const {
// it would return with an iterator to the first checker in the core, so we
// we really have to use find here, which uses operator==.
auto CheckerIt =
- llvm::find(Checkers, CheckerInfo(SuppliedCheckerOrPackage));
- if (CheckerIt != Checkers.end()) {
+ llvm::find(Data.Checkers, CheckerInfo(SuppliedCheckerOrPackage));
+ if (CheckerIt != Data.Checkers.end()) {
isOptionContainedIn(CheckerIt->CmdLineOptions, SuppliedCheckerOrPackage,
SuppliedOption, AnOpts, Diags);
continue;
}
- auto PackageIt =
- llvm::find(Packages, PackageInfo(SuppliedCheckerOrPackage));
- if (PackageIt != Packages.end()) {
+ const auto *PackageIt =
+ llvm::find(Data.Packages, PackageInfo(SuppliedCheckerOrPackage));
+ if (PackageIt != Data.Packages.end()) {
isOptionContainedIn(PackageIt->CmdLineOptions, SuppliedCheckerOrPackage,
SuppliedOption, AnOpts, Diags);
continue;
@@ -505,121 +529,3 @@ void CheckerRegistry::validateCheckerOptions() const {
}
}
-void CheckerRegistry::printCheckerWithDescList(raw_ostream &Out,
- size_t MaxNameChars) const {
- // FIXME: Print available packages.
-
- Out << "CHECKERS:\n";
-
- // Find the maximum option length.
- size_t OptionFieldWidth = 0;
- for (const auto &Checker : Checkers) {
- // Limit the amount of padding we are willing to give up for alignment.
- // Package.Name Description [Hidden]
- size_t NameLength = Checker.FullName.size();
- if (NameLength <= MaxNameChars)
- OptionFieldWidth = std::max(OptionFieldWidth, NameLength);
- }
-
- const size_t InitialPad = 2;
-
- auto Print = [=](llvm::raw_ostream &Out, const CheckerInfo &Checker,
- StringRef Description) {
- AnalyzerOptions::printFormattedEntry(Out, {Checker.FullName, Description},
- InitialPad, OptionFieldWidth);
- Out << '\n';
- };
-
- for (const auto &Checker : Checkers) {
- // The order of this if branches is significant, we wouldn't like to display
- // developer checkers even in the alpha output. For example,
- // alpha.cplusplus.IteratorModeling is a modeling checker, hence it's hidden
- // by default, and users (even when the user is a developer of an alpha
- // checker) shouldn't normally tinker with whether they should be enabled.
-
- if (Checker.IsHidden) {
- if (AnOpts.ShowCheckerHelpDeveloper)
- Print(Out, Checker, Checker.Desc);
- continue;
- }
-
- if (Checker.FullName.startswith("alpha")) {
- if (AnOpts.ShowCheckerHelpAlpha)
- Print(Out, Checker,
- ("(Enable only for development!) " + Checker.Desc).str());
- continue;
- }
-
- if (AnOpts.ShowCheckerHelp)
- Print(Out, Checker, Checker.Desc);
- }
-}
-
-void CheckerRegistry::printEnabledCheckerList(raw_ostream &Out) const {
- // Collect checkers enabled by the options.
- CheckerInfoSet EnabledCheckers = getEnabledCheckers();
-
- for (const auto *i : EnabledCheckers)
- Out << i->FullName << '\n';
-}
-
-void CheckerRegistry::printCheckerOptionList(raw_ostream &Out) const {
- Out << "OVERVIEW: Clang Static Analyzer Checker and Package Option List\n\n";
- Out << "USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>\n\n";
- Out << " -analyzer-config OPTION1=VALUE, -analyzer-config "
- "OPTION2=VALUE, ...\n\n";
- Out << "OPTIONS:\n\n";
-
- std::multimap<StringRef, const CmdLineOption &> OptionMap;
-
- for (const CheckerInfo &Checker : Checkers) {
- for (const CmdLineOption &Option : Checker.CmdLineOptions) {
- OptionMap.insert({Checker.FullName, Option});
- }
- }
-
- for (const PackageInfo &Package : Packages) {
- for (const CmdLineOption &Option : Package.CmdLineOptions) {
- OptionMap.insert({Package.FullName, Option});
- }
- }
-
- auto Print = [] (llvm::raw_ostream &Out, StringRef FullOption, StringRef Desc) {
- AnalyzerOptions::printFormattedEntry(Out, {FullOption, Desc},
- /*InitialPad*/ 2,
- /*EntryWidth*/ 50,
- /*MinLineWidth*/ 90);
- Out << "\n\n";
- };
- for (const std::pair<const StringRef, const CmdLineOption &> &Entry :
- OptionMap) {
- const CmdLineOption &Option = Entry.second;
- std::string FullOption = (Entry.first + ":" + Option.OptionName).str();
-
- std::string Desc =
- ("(" + Option.OptionType + ") " + Option.Description + " (default: " +
- (Option.DefaultValStr.empty() ? "\"\"" : Option.DefaultValStr) + ")")
- .str();
-
- // The list of these if branches is significant, we wouldn't like to
- // display hidden alpha checker options for
- // -analyzer-checker-option-help-alpha.
-
- if (Option.IsHidden) {
- if (AnOpts.ShowCheckerOptionDeveloperList)
- Print(Out, FullOption, Desc);
- continue;
- }
-
- if (Option.DevelopmentStatus == "alpha" ||
- Entry.first.startswith("alpha")) {
- if (AnOpts.ShowCheckerOptionAlphaList)
- Print(Out, FullOption,
- llvm::Twine("(Enable only for development!) " + Desc).str());
- continue;
- }
-
- if (AnOpts.ShowCheckerOptionList)
- Print(Out, FullOption, Desc);
- }
-}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CreateCheckerManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CreateCheckerManager.cpp
new file mode 100644
index 000000000000..21a60785eb52
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CreateCheckerManager.cpp
@@ -0,0 +1,50 @@
+//===- CheckerManager.h - Static Analyzer Checker Manager -------*- 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 the Static Analyzer Checker Manager.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
+#include <memory>
+
+namespace clang {
+namespace ento {
+
+CheckerManager::CheckerManager(
+ ASTContext &Context, AnalyzerOptions &AOptions, const Preprocessor &PP,
+ ArrayRef<std::string> plugins,
+ ArrayRef<std::function<void(CheckerRegistry &)>> checkerRegistrationFns)
+ : Context(&Context), LangOpts(Context.getLangOpts()), AOptions(AOptions),
+ PP(&PP), Diags(Context.getDiagnostics()),
+ RegistryData(std::make_unique<CheckerRegistryData>()) {
+ CheckerRegistry Registry(*RegistryData, plugins, Context.getDiagnostics(),
+ AOptions, checkerRegistrationFns);
+ Registry.initializeRegistry(*this);
+ Registry.initializeManager(*this);
+ finishedCheckerRegistration();
+}
+
+CheckerManager::CheckerManager(AnalyzerOptions &AOptions,
+ const LangOptions &LangOpts,
+ DiagnosticsEngine &Diags,
+ ArrayRef<std::string> plugins)
+ : LangOpts(LangOpts), AOptions(AOptions), Diags(Diags),
+ RegistryData(std::make_unique<CheckerRegistryData>()) {
+ CheckerRegistry Registry(*RegistryData, plugins, Diags, AOptions, {});
+ Registry.initializeRegistry(*this);
+}
+
+CheckerManager::~CheckerManager() {
+ for (const auto &CheckerDtor : CheckerDtors)
+ CheckerDtor();
+}
+
+} // namespace ento
+} // namespace clang