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/AnalyzerStatsChecker.cpp23
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp15
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp594
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp184
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp370
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp89
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp24
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp31
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp1068
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp238
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp41
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp12
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp19
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp67
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp10
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp154
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp21
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp17
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp58
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp41
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp50
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp9
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp24
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp28
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp155
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp51
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp49
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp10
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp46
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp73
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp250
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp325
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h110
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp185
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp176
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp15
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp41
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp30
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp1656
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp38
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp28
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp22
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.h23
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp80
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp24
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp48
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp54
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp12
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp6
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h21
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp51
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp10
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp719
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp85
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp38
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp55
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp26
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp43
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp11
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp17
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp8
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp27
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp21
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp227
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp23
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp19
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp6
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp8
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp18
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp8
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp44
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp19
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp21
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp19
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp36
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp14
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp84
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp40
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h1
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp61
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp68
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp23
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp17
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp7
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp58
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp73
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp147
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp2627
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp298
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp846
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp105
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h99
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp203
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.h106
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp22
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp20
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp3
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustReturnsNonnullChecker.cpp60
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp90
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp20
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp85
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp7
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp14
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp80
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp33
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp51
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp56
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp33
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp70
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp54
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp41
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp5
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h6
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp12
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp86
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h24
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp51
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp9
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp15
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h15
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp353
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp5
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/APSIntType.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp11
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp74
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp30
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp174
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp907
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugSuppression.cpp169
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallDescription.cpp169
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp292
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp38
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp12
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp33
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp6
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp80
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp94
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp73
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicType.cpp8
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp19
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp30
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp1011
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp194
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp486
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp293
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp43
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp688
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp13
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp7
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/MemRegion.cpp287
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp33
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp77
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp1224
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp86
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RegionStore.cpp932
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp7
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp980
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp183
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp394
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp21
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp474
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Store.cpp85
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp105
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp11
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/WorkList.cpp16
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp189
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp15
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp12
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp7
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp3
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h3
181 files changed, 16506 insertions, 8091 deletions
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
index c06604b6cffe..a54f1b1e71d4 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
@@ -7,18 +7,20 @@
//===----------------------------------------------------------------------===//
// This file reports various statistics about analyzer visitation.
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/DeclObjC.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -51,15 +53,14 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
const Decl *D = LC->getDecl();
// Iterate over the exploded graph.
- for (ExplodedGraph::node_iterator I = G.nodes_begin();
- I != G.nodes_end(); ++I) {
- const ProgramPoint &P = I->getLocation();
+ for (const ExplodedNode &N : G.nodes()) {
+ const ProgramPoint &P = N.getLocation();
// Only check the coverage in the top level function (optimization).
if (D != P.getLocationContext()->getDecl())
continue;
- if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
+ if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
const CFGBlock *CB = BE->getBlock();
reachable.insert(CB);
}
@@ -93,11 +94,10 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
if (!Loc.isValid())
return;
- if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) {
+ if (isa<FunctionDecl, ObjCMethodDecl>(D)) {
const NamedDecl *ND = cast<NamedDecl>(D);
output << *ND;
- }
- else if (isa<BlockDecl>(D)) {
+ } else if (isa<BlockDecl>(D)) {
output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn();
}
@@ -115,16 +115,13 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
output.str(), PathDiagnosticLocation(D, SM));
// Emit warning for each block we bailed out on.
- typedef CoreEngine::BlocksExhausted::const_iterator ExhaustedIterator;
const CoreEngine &CE = Eng.getCoreEngine();
- for (ExhaustedIterator I = CE.blocks_exhausted_begin(),
- E = CE.blocks_exhausted_end(); I != E; ++I) {
- const BlockEdge &BE = I->first;
+ for (const BlockEdge &BE : make_first_range(CE.exhausted_blocks())) {
const CFGBlock *Exit = BE.getDst();
if (Exit->empty())
continue;
const CFGElement &CE = Exit->front();
- if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {
+ if (std::optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {
SmallString<128> bufI;
llvm::raw_svector_ostream outputI(bufI);
outputI << "(" << NameOfRootFunction << ")" <<
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
index 605b11874ef5..c990ad138f89 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
@@ -25,7 +25,7 @@ using namespace ento;
namespace {
class ArrayBoundChecker :
public Checker<check::Location> {
- mutable std::unique_ptr<BuiltinBug> BT;
+ const BugType BT{this, "Out-of-bound array access"};
public:
void checkLocation(SVal l, bool isLoad, const Stmt* S,
@@ -58,25 +58,20 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS,
DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType());
- ProgramStateRef StInBound = state->assumeInBound(Idx, ElementCount, true);
- ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false);
+ ProgramStateRef StInBound, StOutBound;
+ std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, ElementCount);
if (StOutBound && !StInBound) {
ExplodedNode *N = C.generateErrorNode(StOutBound);
if (!N)
return;
- if (!BT)
- BT.reset(new BuiltinBug(
- this, "Out-of-bound array access",
- "Access out-of-bound array element (buffer overflow)"));
-
// FIXME: It would be nice to eventually make this diagnostic more clear,
// e.g., by referencing the original declaration or by saying *why* this
// reference is outside the range.
// Generate a report for this bug.
- auto report =
- std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
+ auto report = std::make_unique<PathSensitiveBugReport>(
+ BT, "Access out-of-bound array element (buffer overflow)", N);
report->addRange(LoadS->getSourceRange());
C.emitReport(std::move(report));
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
index 2a5fe9d8ed92..6c7a1601402e 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
@@ -11,9 +11,10 @@
//
//===----------------------------------------------------------------------===//
-#include "Taint.h"
#include "clang/AST/CharUnits.h"
+#include "clang/AST/ParentMapContext.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Checkers/Taint.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -22,77 +23,134 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
using namespace taint;
+using llvm::formatv;
namespace {
-class ArrayBoundCheckerV2 :
- public Checker<check::Location> {
- mutable std::unique_ptr<BuiltinBug> BT;
+enum OOB_Kind { OOB_Precedes, OOB_Exceeds, OOB_Taint };
- enum OOB_Kind { OOB_Precedes, OOB_Excedes, OOB_Tainted };
-
- void reportOOB(CheckerContext &C, ProgramStateRef errorState, OOB_Kind kind,
- std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const;
-
-public:
- void checkLocation(SVal l, bool isLoad, const Stmt*S,
- CheckerContext &C) const;
+struct Messages {
+ std::string Short, Full;
};
-// FIXME: Eventually replace RegionRawOffset with this class.
-class RegionRawOffsetV2 {
-private:
- const SubRegion *baseRegion;
- SVal byteOffset;
+// NOTE: The `ArraySubscriptExpr` and `UnaryOperator` callbacks are `PostStmt`
+// instead of `PreStmt` because the current implementation passes the whole
+// expression to `CheckerContext::getSVal()` which only works after the
+// symbolic evaluation of the expression. (To turn them into `PreStmt`
+// callbacks, we'd need to duplicate the logic that evaluates these
+// expressions.) The `MemberExpr` callback would work as `PreStmt` but it's
+// defined as `PostStmt` for the sake of consistency with the other callbacks.
+class ArrayBoundCheckerV2 : public Checker<check::PostStmt<ArraySubscriptExpr>,
+ check::PostStmt<UnaryOperator>,
+ check::PostStmt<MemberExpr>> {
+ BugType BT{this, "Out-of-bound access"};
+ BugType TaintBT{this, "Out-of-bound access", categories::TaintedData};
- RegionRawOffsetV2()
- : baseRegion(nullptr), byteOffset(UnknownVal()) {}
+ void performCheck(const Expr *E, CheckerContext &C) const;
-public:
- RegionRawOffsetV2(const SubRegion* base, SVal offset)
- : baseRegion(base), byteOffset(offset) {}
+ void reportOOB(CheckerContext &C, ProgramStateRef ErrorState, OOB_Kind Kind,
+ NonLoc Offset, Messages Msgs) const;
- NonLoc getByteOffset() const { return byteOffset.castAs<NonLoc>(); }
- const SubRegion *getRegion() const { return baseRegion; }
+ static bool isFromCtypeMacro(const Stmt *S, ASTContext &AC);
- static RegionRawOffsetV2 computeOffset(ProgramStateRef state,
- SValBuilder &svalBuilder,
- SVal location);
+ static bool isInAddressOf(const Stmt *S, ASTContext &AC);
- void dump() const;
- void dumpToStream(raw_ostream &os) const;
+public:
+ void checkPostStmt(const ArraySubscriptExpr *E, CheckerContext &C) const {
+ performCheck(E, C);
+ }
+ void checkPostStmt(const UnaryOperator *E, CheckerContext &C) const {
+ if (E->getOpcode() == UO_Deref)
+ performCheck(E, C);
+ }
+ void checkPostStmt(const MemberExpr *E, CheckerContext &C) const {
+ if (E->isArrow())
+ performCheck(E->getBase(), C);
+ }
};
-}
-static SVal computeExtentBegin(SValBuilder &svalBuilder,
- const MemRegion *region) {
- const MemSpaceRegion *SR = region->getMemorySpace();
- if (SR->getKind() == MemRegion::UnknownSpaceRegionKind)
- return UnknownVal();
- else
- return svalBuilder.makeZeroArrayIndex();
+} // anonymous namespace
+
+/// For a given Location that can be represented as a symbolic expression
+/// Arr[Idx] (or perhaps Arr[Idx1][Idx2] etc.), return the parent memory block
+/// Arr and the distance of Location from the beginning of Arr (expressed in a
+/// NonLoc that specifies the number of CharUnits). Returns nullopt when these
+/// cannot be determined.
+static std::optional<std::pair<const SubRegion *, NonLoc>>
+computeOffset(ProgramStateRef State, SValBuilder &SVB, SVal Location) {
+ QualType T = SVB.getArrayIndexType();
+ auto EvalBinOp = [&SVB, State, T](BinaryOperatorKind Op, NonLoc L, NonLoc R) {
+ // We will use this utility to add and multiply values.
+ return SVB.evalBinOpNN(State, Op, L, R, T).getAs<NonLoc>();
+ };
+
+ const SubRegion *OwnerRegion = nullptr;
+ std::optional<NonLoc> Offset = SVB.makeZeroArrayIndex();
+
+ const ElementRegion *CurRegion =
+ dyn_cast_or_null<ElementRegion>(Location.getAsRegion());
+
+ while (CurRegion) {
+ const auto Index = CurRegion->getIndex().getAs<NonLoc>();
+ if (!Index)
+ return std::nullopt;
+
+ QualType ElemType = CurRegion->getElementType();
+
+ // FIXME: The following early return was presumably added to safeguard the
+ // getTypeSizeInChars() call (which doesn't accept an incomplete type), but
+ // it seems that `ElemType` cannot be incomplete at this point.
+ if (ElemType->isIncompleteType())
+ return std::nullopt;
+
+ // Calculate Delta = Index * sizeof(ElemType).
+ NonLoc Size = SVB.makeArrayIndex(
+ SVB.getContext().getTypeSizeInChars(ElemType).getQuantity());
+ auto Delta = EvalBinOp(BO_Mul, *Index, Size);
+ if (!Delta)
+ return std::nullopt;
+
+ // Perform Offset += Delta.
+ Offset = EvalBinOp(BO_Add, *Offset, *Delta);
+ if (!Offset)
+ return std::nullopt;
+
+ OwnerRegion = CurRegion->getSuperRegion()->getAs<SubRegion>();
+ // When this is just another ElementRegion layer, we need to continue the
+ // offset calculations:
+ CurRegion = dyn_cast_or_null<ElementRegion>(OwnerRegion);
+ }
+
+ if (OwnerRegion)
+ return std::make_pair(OwnerRegion, *Offset);
+
+ return std::nullopt;
}
// TODO: once the constraint manager is smart enough to handle non simplified
// symbolic expressions remove this function. Note that this can not be used in
// the constraint manager as is, since this does not handle overflows. It is
// safe to assume, however, that memory offsets will not overflow.
+// NOTE: callers of this function need to be aware of the effects of overflows
+// and signed<->unsigned conversions!
static std::pair<NonLoc, nonloc::ConcreteInt>
getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent,
SValBuilder &svalBuilder) {
- Optional<nonloc::SymbolVal> SymVal = offset.getAs<nonloc::SymbolVal>();
+ std::optional<nonloc::SymbolVal> SymVal = offset.getAs<nonloc::SymbolVal>();
if (SymVal && SymVal->isExpression()) {
if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SymVal->getSymbol())) {
llvm::APSInt constant =
APSIntType(extent.getValue()).convert(SIE->getRHS());
switch (SIE->getOpcode()) {
case BO_Mul:
- // The constant should never be 0 here, since it the result of scaling
- // based on the size of a type which is never 0.
+ // The constant should never be 0 here, becasue multiplication by zero
+ // is simplified by the engine.
if ((extent.getValue() % constant) != 0)
return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent);
else
@@ -113,10 +171,154 @@ getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent,
return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent);
}
-void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
- const Stmt* LoadS,
- CheckerContext &checkerContext) const {
+// Evaluate the comparison Value < Threshold with the help of the custom
+// simplification algorithm defined for this checker. Return a pair of states,
+// where the first one corresponds to "value below threshold" and the second
+// corresponds to "value at or above threshold". Returns {nullptr, nullptr} in
+// the case when the evaluation fails.
+// If the optional argument CheckEquality is true, then use BO_EQ instead of
+// the default BO_LT after consistently applying the same simplification steps.
+static std::pair<ProgramStateRef, ProgramStateRef>
+compareValueToThreshold(ProgramStateRef State, NonLoc Value, NonLoc Threshold,
+ SValBuilder &SVB, bool CheckEquality = false) {
+ if (auto ConcreteThreshold = Threshold.getAs<nonloc::ConcreteInt>()) {
+ std::tie(Value, Threshold) = getSimplifiedOffsets(Value, *ConcreteThreshold, SVB);
+ }
+ if (auto ConcreteThreshold = Threshold.getAs<nonloc::ConcreteInt>()) {
+ QualType T = Value.getType(SVB.getContext());
+ if (T->isUnsignedIntegerType() && ConcreteThreshold->getValue().isNegative()) {
+ // In this case we reduced the bound check to a comparison of the form
+ // (symbol or value with unsigned type) < (negative number)
+ // which is always false. We are handling these cases separately because
+ // evalBinOpNN can perform a signed->unsigned conversion that turns the
+ // negative number into a huge positive value and leads to wildly
+ // inaccurate conclusions.
+ return {nullptr, State};
+ }
+ }
+ const BinaryOperatorKind OpKind = CheckEquality ? BO_EQ : BO_LT;
+ auto BelowThreshold =
+ SVB.evalBinOpNN(State, OpKind, Value, Threshold, SVB.getConditionType())
+ .getAs<NonLoc>();
+
+ if (BelowThreshold)
+ return State->assume(*BelowThreshold);
+
+ return {nullptr, nullptr};
+}
+
+static std::string getRegionName(const SubRegion *Region) {
+ if (std::string RegName = Region->getDescriptiveName(); !RegName.empty())
+ return RegName;
+
+ // Field regions only have descriptive names when their parent has a
+ // descriptive name; so we provide a fallback representation for them:
+ if (const auto *FR = Region->getAs<FieldRegion>()) {
+ if (StringRef Name = FR->getDecl()->getName(); !Name.empty())
+ return formatv("the field '{0}'", Name);
+ return "the unnamed field";
+ }
+
+ if (isa<AllocaRegion>(Region))
+ return "the memory returned by 'alloca'";
+
+ if (isa<SymbolicRegion>(Region) &&
+ isa<HeapSpaceRegion>(Region->getMemorySpace()))
+ return "the heap area";
+
+ if (isa<StringRegion>(Region))
+ return "the string literal";
+
+ return "the region";
+}
+
+static std::optional<int64_t> getConcreteValue(NonLoc SV) {
+ if (auto ConcreteVal = SV.getAs<nonloc::ConcreteInt>()) {
+ return ConcreteVal->getValue().tryExtValue();
+ }
+ return std::nullopt;
+}
+
+static std::string getShortMsg(OOB_Kind Kind, std::string RegName) {
+ static const char *ShortMsgTemplates[] = {
+ "Out of bound access to memory preceding {0}",
+ "Out of bound access to memory after the end of {0}",
+ "Potential out of bound access to {0} with tainted offset"};
+
+ return formatv(ShortMsgTemplates[Kind], RegName);
+}
+
+static Messages getPrecedesMsgs(const SubRegion *Region, NonLoc Offset) {
+ std::string RegName = getRegionName(Region);
+ SmallString<128> Buf;
+ llvm::raw_svector_ostream Out(Buf);
+ Out << "Access of " << RegName << " at negative byte offset";
+ if (auto ConcreteIdx = Offset.getAs<nonloc::ConcreteInt>())
+ Out << ' ' << ConcreteIdx->getValue();
+ return {getShortMsg(OOB_Precedes, RegName), std::string(Buf)};
+}
+
+static Messages getExceedsMsgs(ASTContext &ACtx, const SubRegion *Region,
+ NonLoc Offset, NonLoc Extent, SVal Location) {
+ std::string RegName = getRegionName(Region);
+ const auto *EReg = Location.getAsRegion()->getAs<ElementRegion>();
+ assert(EReg && "this checker only handles element access");
+ QualType ElemType = EReg->getElementType();
+
+ std::optional<int64_t> OffsetN = getConcreteValue(Offset);
+ std::optional<int64_t> ExtentN = getConcreteValue(Extent);
+
+ bool UseByteOffsets = true;
+ if (int64_t ElemSize = ACtx.getTypeSizeInChars(ElemType).getQuantity()) {
+ const bool OffsetHasRemainder = OffsetN && *OffsetN % ElemSize;
+ const bool ExtentHasRemainder = ExtentN && *ExtentN % ElemSize;
+ if (!OffsetHasRemainder && !ExtentHasRemainder) {
+ UseByteOffsets = false;
+ if (OffsetN)
+ *OffsetN /= ElemSize;
+ if (ExtentN)
+ *ExtentN /= ElemSize;
+ }
+ }
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream Out(Buf);
+ Out << "Access of ";
+ if (!ExtentN && !UseByteOffsets)
+ Out << "'" << ElemType.getAsString() << "' element in ";
+ Out << RegName << " at ";
+ if (OffsetN) {
+ Out << (UseByteOffsets ? "byte offset " : "index ") << *OffsetN;
+ } else {
+ Out << "an overflowing " << (UseByteOffsets ? "byte offset" : "index");
+ }
+ if (ExtentN) {
+ Out << ", while it holds only ";
+ if (*ExtentN != 1)
+ Out << *ExtentN;
+ else
+ Out << "a single";
+ if (UseByteOffsets)
+ Out << " byte";
+ else
+ Out << " '" << ElemType.getAsString() << "' element";
+
+ if (*ExtentN > 1)
+ Out << "s";
+ }
+
+ return {getShortMsg(OOB_Exceeds, RegName), std::string(Buf)};
+}
+
+static Messages getTaintMsgs(const SubRegion *Region, const char *OffsetName) {
+ std::string RegName = getRegionName(Region);
+ return {formatv("Potential out of bound access to {0} with tainted {1}",
+ RegName, OffsetName),
+ formatv("Access of {0} with a tainted {1} that may be too large",
+ RegName, OffsetName)};
+}
+
+void ArrayBoundCheckerV2::performCheck(const Expr *E, CheckerContext &C) const {
// NOTE: Instead of using ProgramState::assumeInBound(), we are prototyping
// some new logic here that reasons directly about memory region extents.
// Once that logic is more mature, we can bring it back to assumeInBound()
@@ -126,230 +328,152 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
// memory access is within the extent of the base region. Since we
// have some flexibility in defining the base region, we can achieve
// various levels of conservatism in our buffer overflow checking.
- ProgramStateRef state = checkerContext.getState();
- SValBuilder &svalBuilder = checkerContext.getSValBuilder();
- const RegionRawOffsetV2 &rawOffset =
- RegionRawOffsetV2::computeOffset(state, svalBuilder, location);
+ const SVal Location = C.getSVal(E);
- if (!rawOffset.getRegion())
+ // The header ctype.h (from e.g. glibc) implements the isXXXXX() macros as
+ // #define isXXXXX(arg) (LOOKUP_TABLE[arg] & BITMASK_FOR_XXXXX)
+ // and incomplete analysis of these leads to false positives. As even
+ // accurate reports would be confusing for the users, just disable reports
+ // from these macros:
+ if (isFromCtypeMacro(E, C.getASTContext()))
return;
- NonLoc rawOffsetVal = rawOffset.getByteOffset();
-
- // CHECK LOWER BOUND: Is byteOffset < extent begin?
- // If so, we are doing a load/store
- // before the first valid offset in the memory region.
-
- SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion());
+ ProgramStateRef State = C.getState();
+ SValBuilder &SVB = C.getSValBuilder();
- if (Optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) {
- if (NV->getAs<nonloc::ConcreteInt>()) {
- std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets =
- getSimplifiedOffsets(rawOffset.getByteOffset(),
- NV->castAs<nonloc::ConcreteInt>(),
- svalBuilder);
- rawOffsetVal = simplifiedOffsets.first;
- *NV = simplifiedOffsets.second;
- }
-
- SVal lowerBound = svalBuilder.evalBinOpNN(state, BO_LT, rawOffsetVal, *NV,
- svalBuilder.getConditionType());
+ const std::optional<std::pair<const SubRegion *, NonLoc>> &RawOffset =
+ computeOffset(State, SVB, Location);
- Optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>();
- if (!lowerBoundToCheck)
- return;
-
- ProgramStateRef state_precedesLowerBound, state_withinLowerBound;
- std::tie(state_precedesLowerBound, state_withinLowerBound) =
- state->assume(*lowerBoundToCheck);
+ if (!RawOffset)
+ return;
- // Are we constrained enough to definitely precede the lower bound?
- if (state_precedesLowerBound && !state_withinLowerBound) {
- reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes);
+ auto [Reg, ByteOffset] = *RawOffset;
+
+ // CHECK LOWER BOUND
+ const MemSpaceRegion *Space = Reg->getMemorySpace();
+ if (!(isa<SymbolicRegion>(Reg) && isa<UnknownSpaceRegion>(Space))) {
+ // A symbolic region in unknown space represents an unknown pointer that
+ // may point into the middle of an array, so we don't look for underflows.
+ // Both conditions are significant because we want to check underflows in
+ // symbolic regions on the heap (which may be introduced by checkers like
+ // MallocChecker that call SValBuilder::getConjuredHeapSymbolVal()) and
+ // non-symbolic regions (e.g. a field subregion of a symbolic region) in
+ // unknown space.
+ auto [PrecedesLowerBound, WithinLowerBound] = compareValueToThreshold(
+ State, ByteOffset, SVB.makeZeroArrayIndex(), SVB);
+
+ if (PrecedesLowerBound && !WithinLowerBound) {
+ // We know that the index definitely precedes the lower bound.
+ Messages Msgs = getPrecedesMsgs(Reg, ByteOffset);
+ reportOOB(C, PrecedesLowerBound, OOB_Precedes, ByteOffset, Msgs);
return;
}
- // Otherwise, assume the constraint of the lower bound.
- assert(state_withinLowerBound);
- state = state_withinLowerBound;
+ if (WithinLowerBound)
+ State = WithinLowerBound;
}
- do {
- // CHECK UPPER BOUND: Is byteOffset >= size(baseRegion)? If so,
- // we are doing a load/store after the last valid offset.
- const MemRegion *MR = rawOffset.getRegion();
- DefinedOrUnknownSVal Size = getDynamicExtent(state, MR, svalBuilder);
- if (!Size.getAs<NonLoc>())
- break;
-
- if (Size.getAs<nonloc::ConcreteInt>()) {
- std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets =
- getSimplifiedOffsets(rawOffset.getByteOffset(),
- Size.castAs<nonloc::ConcreteInt>(), svalBuilder);
- rawOffsetVal = simplifiedOffsets.first;
- Size = simplifiedOffsets.second;
- }
-
- SVal upperbound = svalBuilder.evalBinOpNN(state, BO_GE, rawOffsetVal,
- Size.castAs<NonLoc>(),
- svalBuilder.getConditionType());
-
- Optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>();
- if (!upperboundToCheck)
- break;
-
- ProgramStateRef state_exceedsUpperBound, state_withinUpperBound;
- std::tie(state_exceedsUpperBound, state_withinUpperBound) =
- state->assume(*upperboundToCheck);
-
- // If we are under constrained and the index variables are tainted, report.
- if (state_exceedsUpperBound && state_withinUpperBound) {
- SVal ByteOffset = rawOffset.getByteOffset();
- if (isTainted(state, ByteOffset)) {
- reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted,
- std::make_unique<TaintBugVisitor>(ByteOffset));
+ // CHECK UPPER BOUND
+ DefinedOrUnknownSVal Size = getDynamicExtent(State, Reg, SVB);
+ if (auto KnownSize = Size.getAs<NonLoc>()) {
+ auto [WithinUpperBound, ExceedsUpperBound] =
+ compareValueToThreshold(State, ByteOffset, *KnownSize, SVB);
+
+ if (ExceedsUpperBound) {
+ if (!WithinUpperBound) {
+ // We know that the index definitely exceeds the upper bound.
+ if (isa<ArraySubscriptExpr>(E) && isInAddressOf(E, C.getASTContext())) {
+ // ...but this is within an addressof expression, so we need to check
+ // for the exceptional case that `&array[size]` is valid.
+ auto [EqualsToThreshold, NotEqualToThreshold] =
+ compareValueToThreshold(ExceedsUpperBound, ByteOffset, *KnownSize,
+ SVB, /*CheckEquality=*/true);
+ if (EqualsToThreshold && !NotEqualToThreshold) {
+ // We are definitely in the exceptional case, so return early
+ // instead of reporting a bug.
+ C.addTransition(EqualsToThreshold);
+ return;
+ }
+ }
+ Messages Msgs = getExceedsMsgs(C.getASTContext(), Reg, ByteOffset,
+ *KnownSize, Location);
+ reportOOB(C, ExceedsUpperBound, OOB_Exceeds, ByteOffset, Msgs);
+ return;
+ }
+ if (isTainted(State, ByteOffset)) {
+ // Both cases are possible, but the offset is tainted, so report.
+ std::string RegName = getRegionName(Reg);
+
+ // Diagnostic detail: "tainted offset" is always correct, but the
+ // common case is that 'idx' is tainted in 'arr[idx]' and then it's
+ // nicer to say "tainted index".
+ const char *OffsetName = "offset";
+ if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
+ if (isTainted(State, ASE->getIdx(), C.getLocationContext()))
+ OffsetName = "index";
+
+ Messages Msgs = getTaintMsgs(Reg, OffsetName);
+ reportOOB(C, ExceedsUpperBound, OOB_Taint, ByteOffset, Msgs);
return;
}
- } else if (state_exceedsUpperBound) {
- // If we are constrained enough to definitely exceed the upper bound,
- // report.
- assert(!state_withinUpperBound);
- reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes);
- return;
}
- assert(state_withinUpperBound);
- state = state_withinUpperBound;
+ if (WithinUpperBound)
+ State = WithinUpperBound;
}
- while (false);
- checkerContext.addTransition(state);
+ C.addTransition(State);
}
-void ArrayBoundCheckerV2::reportOOB(
- CheckerContext &checkerContext, ProgramStateRef errorState, OOB_Kind kind,
- std::unique_ptr<BugReporterVisitor> Visitor) const {
+void ArrayBoundCheckerV2::reportOOB(CheckerContext &C,
+ ProgramStateRef ErrorState, OOB_Kind Kind,
+ NonLoc Offset, Messages Msgs) const {
- ExplodedNode *errorNode = checkerContext.generateErrorNode(errorState);
- if (!errorNode)
+ ExplodedNode *ErrorNode = C.generateErrorNode(ErrorState);
+ if (!ErrorNode)
return;
- if (!BT)
- BT.reset(new BuiltinBug(this, "Out-of-bound access"));
-
- // FIXME: This diagnostics are preliminary. We should get far better
- // diagnostics for explaining buffer overruns.
-
- SmallString<256> buf;
- llvm::raw_svector_ostream os(buf);
- os << "Out of bound memory access ";
- switch (kind) {
- case OOB_Precedes:
- os << "(accessed memory precedes memory block)";
- break;
- case OOB_Excedes:
- os << "(access exceeds upper limit of memory block)";
- break;
- case OOB_Tainted:
- os << "(index is tainted)";
- break;
- }
+ auto BR = std::make_unique<PathSensitiveBugReport>(
+ Kind == OOB_Taint ? TaintBT : BT, Msgs.Short, Msgs.Full, ErrorNode);
- auto BR = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), errorNode);
- BR->addVisitor(std::move(Visitor));
- checkerContext.emitReport(std::move(BR));
-}
+ // Track back the propagation of taintedness.
+ if (Kind == OOB_Taint)
+ for (SymbolRef Sym : getTaintedSymbols(ErrorState, Offset))
+ BR->markInteresting(Sym);
-#ifndef NDEBUG
-LLVM_DUMP_METHOD void RegionRawOffsetV2::dump() const {
- dumpToStream(llvm::errs());
+ C.emitReport(std::move(BR));
}
-void RegionRawOffsetV2::dumpToStream(raw_ostream &os) const {
- os << "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}';
-}
-#endif
+bool ArrayBoundCheckerV2::isFromCtypeMacro(const Stmt *S, ASTContext &ACtx) {
+ SourceLocation Loc = S->getBeginLoc();
+ if (!Loc.isMacroID())
+ return false;
-// Lazily computes a value to be used by 'computeOffset'. If 'val'
-// is unknown or undefined, we lazily substitute '0'. Otherwise,
-// return 'val'.
-static inline SVal getValue(SVal val, SValBuilder &svalBuilder) {
- return val.getAs<UndefinedVal>() ? svalBuilder.makeArrayIndex(0) : val;
-}
+ StringRef MacroName = Lexer::getImmediateMacroName(
+ Loc, ACtx.getSourceManager(), ACtx.getLangOpts());
-// Scale a base value by a scaling factor, and return the scaled
-// value as an SVal. Used by 'computeOffset'.
-static inline SVal scaleValue(ProgramStateRef state,
- NonLoc baseVal, CharUnits scaling,
- SValBuilder &sb) {
- return sb.evalBinOpNN(state, BO_Mul, baseVal,
- sb.makeArrayIndex(scaling.getQuantity()),
- sb.getArrayIndexType());
-}
+ if (MacroName.size() < 7 || MacroName[0] != 'i' || MacroName[1] != 's')
+ return false;
-// Add an SVal to another, treating unknown and undefined values as
-// summing to UnknownVal. Used by 'computeOffset'.
-static SVal addValue(ProgramStateRef state, SVal x, SVal y,
- SValBuilder &svalBuilder) {
- // We treat UnknownVals and UndefinedVals the same here because we
- // only care about computing offsets.
- if (x.isUnknownOrUndef() || y.isUnknownOrUndef())
- return UnknownVal();
-
- return svalBuilder.evalBinOpNN(state, BO_Add, x.castAs<NonLoc>(),
- y.castAs<NonLoc>(),
- svalBuilder.getArrayIndexType());
+ return ((MacroName == "isalnum") || (MacroName == "isalpha") ||
+ (MacroName == "isblank") || (MacroName == "isdigit") ||
+ (MacroName == "isgraph") || (MacroName == "islower") ||
+ (MacroName == "isnctrl") || (MacroName == "isprint") ||
+ (MacroName == "ispunct") || (MacroName == "isspace") ||
+ (MacroName == "isupper") || (MacroName == "isxdigit"));
}
-/// Compute a raw byte offset from a base region. Used for array bounds
-/// checking.
-RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state,
- SValBuilder &svalBuilder,
- SVal location)
-{
- const MemRegion *region = location.getAsRegion();
- SVal offset = UndefinedVal();
-
- while (region) {
- switch (region->getKind()) {
- default: {
- if (const SubRegion *subReg = dyn_cast<SubRegion>(region)) {
- offset = getValue(offset, svalBuilder);
- if (!offset.isUnknownOrUndef())
- return RegionRawOffsetV2(subReg, offset);
- }
- return RegionRawOffsetV2();
- }
- case MemRegion::ElementRegionKind: {
- const ElementRegion *elemReg = cast<ElementRegion>(region);
- SVal index = elemReg->getIndex();
- if (!index.getAs<NonLoc>())
- return RegionRawOffsetV2();
- QualType elemType = elemReg->getElementType();
- // If the element is an incomplete type, go no further.
- ASTContext &astContext = svalBuilder.getContext();
- if (elemType->isIncompleteType())
- return RegionRawOffsetV2();
-
- // Update the offset.
- offset = addValue(state,
- getValue(offset, svalBuilder),
- scaleValue(state,
- index.castAs<NonLoc>(),
- astContext.getTypeSizeInChars(elemType),
- svalBuilder),
- svalBuilder);
-
- if (offset.isUnknownOrUndef())
- return RegionRawOffsetV2();
-
- region = elemReg->getSuperRegion();
- continue;
- }
- }
- }
- return RegionRawOffsetV2();
+bool ArrayBoundCheckerV2::isInAddressOf(const Stmt *S, ASTContext &ACtx) {
+ ParentMapContext &ParentCtx = ACtx.getParentMapContext();
+ do {
+ const DynTypedNodeList Parents = ParentCtx.getParents(*S);
+ if (Parents.empty())
+ return false;
+ S = Parents[0].get<Stmt>();
+ } while (isa_and_nonnull<ParenExpr, ImplicitCastExpr>(S));
+ const auto *UnaryOp = dyn_cast_or_null<UnaryOperator>(S);
+ return UnaryOp && UnaryOp->getOpcode() == UO_AddrOf;
}
void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index a86a410ebcbc..c72a97cc01e9 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -12,7 +12,6 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
@@ -20,18 +19,22 @@
#include "clang/AST/StmtObjC.h"
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
#include "clang/Analysis/SelectorExtras.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/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -41,7 +44,7 @@ namespace {
class APIMisuse : public BugType {
public:
APIMisuse(const CheckerBase *checker, const char *name)
- : BugType(checker, name, "API Misuse (Apple)") {}
+ : BugType(checker, name, categories::AppleAPIMisuse) {}
};
} // end anonymous namespace
@@ -93,56 +96,64 @@ static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID,
//===----------------------------------------------------------------------===//
namespace {
- class NilArgChecker : public Checker<check::PreObjCMessage,
- check::PostStmt<ObjCDictionaryLiteral>,
- check::PostStmt<ObjCArrayLiteral> > {
- mutable std::unique_ptr<APIMisuse> BT;
-
- mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors;
- mutable Selector ArrayWithObjectSel;
- mutable Selector AddObjectSel;
- mutable Selector InsertObjectAtIndexSel;
- mutable Selector ReplaceObjectAtIndexWithObjectSel;
- mutable Selector SetObjectAtIndexedSubscriptSel;
- mutable Selector ArrayByAddingObjectSel;
- mutable Selector DictionaryWithObjectForKeySel;
- mutable Selector SetObjectForKeySel;
- mutable Selector SetObjectForKeyedSubscriptSel;
- mutable Selector RemoveObjectForKeySel;
-
- void warnIfNilExpr(const Expr *E,
- const char *Msg,
- CheckerContext &C) const;
-
- void warnIfNilArg(CheckerContext &C,
- const ObjCMethodCall &msg, unsigned Arg,
- FoundationClass Class,
- bool CanBeSubscript = false) const;
-
- void generateBugReport(ExplodedNode *N,
- StringRef Msg,
- SourceRange Range,
- const Expr *Expr,
- CheckerContext &C) const;
-
- public:
- void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
- void checkPostStmt(const ObjCDictionaryLiteral *DL,
- CheckerContext &C) const;
- void checkPostStmt(const ObjCArrayLiteral *AL,
- CheckerContext &C) const;
- };
+class NilArgChecker : public Checker<check::PreObjCMessage,
+ check::PostStmt<ObjCDictionaryLiteral>,
+ check::PostStmt<ObjCArrayLiteral>,
+ EventDispatcher<ImplicitNullDerefEvent>> {
+ mutable std::unique_ptr<APIMisuse> BT;
+
+ mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors;
+ mutable Selector ArrayWithObjectSel;
+ mutable Selector AddObjectSel;
+ mutable Selector InsertObjectAtIndexSel;
+ mutable Selector ReplaceObjectAtIndexWithObjectSel;
+ mutable Selector SetObjectAtIndexedSubscriptSel;
+ mutable Selector ArrayByAddingObjectSel;
+ mutable Selector DictionaryWithObjectForKeySel;
+ mutable Selector SetObjectForKeySel;
+ mutable Selector SetObjectForKeyedSubscriptSel;
+ mutable Selector RemoveObjectForKeySel;
+
+ void warnIfNilExpr(const Expr *E, const char *Msg, CheckerContext &C) const;
+
+ void warnIfNilArg(CheckerContext &C, const ObjCMethodCall &msg, unsigned Arg,
+ FoundationClass Class, bool CanBeSubscript = false) const;
+
+ void generateBugReport(ExplodedNode *N, StringRef Msg, SourceRange Range,
+ const Expr *Expr, CheckerContext &C) const;
+
+public:
+ void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
+ void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const;
+ void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const;
+};
} // end anonymous namespace
void NilArgChecker::warnIfNilExpr(const Expr *E,
const char *Msg,
CheckerContext &C) const {
- ProgramStateRef State = C.getState();
- if (State->isNull(C.getSVal(E)).isConstrainedTrue()) {
+ auto Location = C.getSVal(E).getAs<Loc>();
+ if (!Location)
+ return;
+
+ auto [NonNull, Null] = C.getState()->assume(*Location);
+ // If it's known to be null.
+ if (!NonNull && Null) {
if (ExplodedNode *N = C.generateErrorNode()) {
generateBugReport(N, Msg, E->getSourceRange(), E, C);
+ return;
+ }
+ }
+
+ // If it might be null, assume that it cannot after this operation.
+ if (Null) {
+ // One needs to make sure the pointer is non-null to be used here.
+ if (ExplodedNode *N = C.generateSink(Null, C.getPredecessor())) {
+ dispatchEvent({*Location, /*IsLoad=*/false, N, &C.getBugReporter(),
+ /*IsDirectDereference=*/false});
}
+ C.addTransition(NonNull);
}
}
@@ -341,15 +352,11 @@ void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
namespace {
class CFNumberChecker : public Checker< check::PreStmt<CallExpr> > {
mutable std::unique_ptr<APIMisuse> BT;
- mutable IdentifierInfo *ICreate, *IGetValue;
+ mutable IdentifierInfo *ICreate = nullptr, *IGetValue = nullptr;
public:
- CFNumberChecker() : ICreate(nullptr), IGetValue(nullptr) {}
+ CFNumberChecker() = default;
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
-
-private:
- void EmitError(const TypedRegion* R, const Expr *Ex,
- uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
};
} // end anonymous namespace
@@ -372,7 +379,7 @@ enum CFNumberType {
kCFNumberCGFloatType = 16
};
-static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
+static std::optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
if (i < kCFNumberCharType)
@@ -393,7 +400,7 @@ static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
case kCFNumberCGFloatType:
// FIXME: We need a way to map from names to Type*.
default:
- return None;
+ return std::nullopt;
}
return Ctx.getTypeSize(T);
@@ -445,12 +452,13 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE,
// FIXME: We really should allow ranges of valid theType values, and
// bifurcate the state appropriately.
- Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>();
+ std::optional<nonloc::ConcreteInt> V =
+ dyn_cast<nonloc::ConcreteInt>(TheTypeVal);
if (!V)
return;
uint64_t NumberKind = V->getValue().getLimitedValue();
- Optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind);
+ std::optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind);
// FIXME: In some cases we can emit an error.
if (!OptCFNumberSize)
@@ -465,7 +473,7 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE,
// FIXME: Eventually we should handle arbitrary locations. We can do this
// by having an enhanced memory model that does low-level typing.
- Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
+ std::optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
if (!LV)
return;
@@ -533,10 +541,12 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE,
namespace {
class CFRetainReleaseChecker : public Checker<check::PreCall> {
mutable APIMisuse BT{this, "null passed to CF memory management function"};
- CallDescription CFRetain{"CFRetain", 1},
- CFRelease{"CFRelease", 1},
- CFMakeCollectable{"CFMakeCollectable", 1},
- CFAutorelease{"CFAutorelease", 1};
+ const CallDescriptionSet ModelledCalls = {
+ {{"CFRetain"}, 1},
+ {{"CFRelease"}, 1},
+ {{"CFMakeCollectable"}, 1},
+ {{"CFAutorelease"}, 1},
+ };
public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
@@ -550,13 +560,12 @@ void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call,
return;
// Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease.
- if (!(Call.isCalled(CFRetain) || Call.isCalled(CFRelease) ||
- Call.isCalled(CFMakeCollectable) || Call.isCalled(CFAutorelease)))
+ if (!ModelledCalls.contains(Call))
return;
// Get the argument's value.
SVal ArgVal = Call.getArgSVal(0);
- Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
+ std::optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
if (!DefArgVal)
return;
@@ -744,7 +753,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
return;
// Verify that all arguments have Objective-C types.
- Optional<ExplodedNode*> errorNode;
+ std::optional<ExplodedNode *> errorNode;
for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
QualType ArgTy = msg.getArgExpr(I)->getType();
@@ -756,7 +765,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
continue;
// Ignore pointer constants.
- if (msg.getArgSVal(I).getAs<loc::ConcreteInt>())
+ if (isa<loc::ConcreteInt>(msg.getArgSVal(I)))
continue;
// Ignore pointer types annotated with 'NSObject' attribute.
@@ -768,10 +777,10 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
continue;
// Generate only one error node to use for all bug reports.
- if (!errorNode.hasValue())
+ if (!errorNode)
errorNode = C.generateNonFatalErrorNode();
- if (!errorNode.getValue())
+ if (!*errorNode)
continue;
SmallString<128> sbuf;
@@ -788,8 +797,8 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
ArgTy.print(os, C.getLangOpts());
os << "'";
- auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(),
- errorNode.getValue());
+ auto R =
+ std::make_unique<PathSensitiveBugReport>(*BT, os.str(), *errorNode);
R->addRange(msg.getArgSourceRange(I));
C.emitReport(std::move(R));
}
@@ -810,13 +819,13 @@ class ObjCLoopChecker
check::PostObjCMessage,
check::DeadSymbols,
check::PointerEscape > {
- mutable IdentifierInfo *CountSelectorII;
+ mutable IdentifierInfo *CountSelectorII = nullptr;
bool isCollectionCountMethod(const ObjCMethodCall &M,
CheckerContext &C) const;
public:
- ObjCLoopChecker() : CountSelectorII(nullptr) {}
+ ObjCLoopChecker() = default;
void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
@@ -859,7 +868,8 @@ static ProgramStateRef checkCollectionNonNil(CheckerContext &C,
return nullptr;
SVal CollectionVal = C.getSVal(FCS->getCollection());
- Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>();
+ std::optional<DefinedSVal> KnownCollection =
+ CollectionVal.getAs<DefinedSVal>();
if (!KnownCollection)
return State;
@@ -891,7 +901,7 @@ static ProgramStateRef checkElementNonNil(CheckerContext &C,
const Stmt *Element = FCS->getElement();
// FIXME: Copied from ExprEngineObjC.
- Optional<Loc> ElementLoc;
+ std::optional<Loc> ElementLoc;
if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
assert(ElemDecl->getInit() == nullptr);
@@ -905,7 +915,7 @@ static ProgramStateRef checkElementNonNil(CheckerContext &C,
// Go ahead and assume the value is non-nil.
SVal Val = State->getSVal(*ElementLoc);
- return State->assume(Val.castAs<DefinedOrUnknownSVal>(), true);
+ return State->assume(cast<DefinedOrUnknownSVal>(Val), true);
}
/// Returns NULL state if the collection is known to contain elements
@@ -930,8 +940,8 @@ assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
nonloc::SymbolVal(*CountS),
SvalBuilder.makeIntVal(0, (*CountS)->getType()),
SvalBuilder.getConditionType());
- Optional<DefinedSVal> CountGreaterThanZero =
- CountGreaterThanZeroVal.getAs<DefinedSVal>();
+ std::optional<DefinedSVal> CountGreaterThanZero =
+ CountGreaterThanZeroVal.getAs<DefinedSVal>();
if (!CountGreaterThanZero) {
// The SValBuilder cannot construct a valid SVal for this condition.
// This means we cannot properly reason about it.
@@ -959,14 +969,13 @@ static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N,
return false;
ProgramPoint P = N->getLocation();
- if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
+ if (std::optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
return BE->getSrc()->getLoopTarget() == FCS;
}
// Keep looking for a block edge.
- for (ExplodedNode::const_pred_iterator I = N->pred_begin(),
- E = N->pred_end(); I != E; ++I) {
- if (alreadyExecutedAtLeastOneLoopIteration(*I, FCS))
+ for (const ExplodedNode *N : N->preds()) {
+ if (alreadyExecutedAtLeastOneLoopIteration(N, FCS))
return true;
}
@@ -1094,12 +1103,8 @@ ObjCLoopChecker::checkPointerEscape(ProgramStateRef State,
PointerEscapeKind Kind) const {
SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call);
- // Remove the invalidated symbols form the collection count map.
- for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
- E = Escaped.end();
- I != E; ++I) {
- SymbolRef Sym = *I;
-
+ // Remove the invalidated symbols from the collection count map.
+ for (SymbolRef Sym : Escaped) {
// Don't invalidate this symbol's count if we know the method being called
// is declared on an immutable class. This isn't completely correct if the
// receiver is also passed as an argument, but in most uses of NSArray,
@@ -1121,9 +1126,7 @@ void ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper,
// Remove the dead symbols from the collection count map.
ContainerCountMapTy Tracked = State->get<ContainerCountMap>();
- for (ContainerCountMapTy::iterator I = Tracked.begin(),
- E = Tracked.end(); I != E; ++I) {
- SymbolRef Sym = I->first;
+ for (SymbolRef Sym : llvm::make_first_range(Tracked)) {
if (SymReaper.isDead(Sym)) {
State = State->remove<ContainerCountMap>(Sym);
State = State->remove<ContainerNonEmptyMap>(Sym);
@@ -1142,13 +1145,13 @@ class ObjCNonNilReturnValueChecker
check::PostStmt<ObjCArrayLiteral>,
check::PostStmt<ObjCDictionaryLiteral>,
check::PostStmt<ObjCBoxedExpr> > {
- mutable bool Initialized;
+ mutable bool Initialized = false;
mutable Selector ObjectAtIndex;
mutable Selector ObjectAtIndexedSubscript;
mutable Selector NullSelector;
public:
- ObjCNonNilReturnValueChecker() : Initialized(false) {}
+ ObjCNonNilReturnValueChecker() = default;
ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
ProgramStateRef State,
@@ -1176,7 +1179,8 @@ ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr,
ProgramStateRef State,
CheckerContext &C) const {
SVal Val = C.getSVal(NonNullExpr);
- if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>())
+ if (std::optional<DefinedOrUnknownSVal> DV =
+ Val.getAs<DefinedOrUnknownSVal>())
return State->assume(*DV, true);
return State;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp
new file mode 100644
index 000000000000..339927c165fe
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp
@@ -0,0 +1,370 @@
+//== BitwiseShiftChecker.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 BitwiseShiftChecker, which is a path-sensitive checker
+// that looks for undefined behavior when the operands of the bitwise shift
+// operators '<<' and '>>' are invalid (negative or too large).
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/CharUnits.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 "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "llvm/Support/FormatVariadic.h"
+#include <memory>
+
+using namespace clang;
+using namespace ento;
+using llvm::formatv;
+
+namespace {
+enum class OperandSide { Left, Right };
+
+using BugReportPtr = std::unique_ptr<PathSensitiveBugReport>;
+
+struct NoteTagTemplate {
+ llvm::StringLiteral SignInfo;
+ llvm::StringLiteral UpperBoundIntro;
+};
+
+constexpr NoteTagTemplate NoteTagTemplates[] = {
+ {"", "right operand of bit shift is less than "},
+ {"left operand of bit shift is non-negative", " and right operand is less than "},
+ {"right operand of bit shift is non-negative", " but less than "},
+ {"both operands of bit shift are non-negative", " and right operand is less than "}
+};
+
+/// An implementation detail class which is introduced to split the checker
+/// logic into several methods while maintaining a consistently updated state
+/// and access to other contextual data.
+class BitwiseShiftValidator {
+ CheckerContext &Ctx;
+ ProgramStateRef FoldedState;
+ const BinaryOperator *const Op;
+ const BugType &BT;
+ const bool PedanticFlag;
+
+ // The following data members are only used for note tag creation:
+ enum { NonNegLeft = 1, NonNegRight = 2 };
+ unsigned NonNegOperands = 0;
+
+ std::optional<unsigned> UpperBoundBitCount = std::nullopt;
+
+public:
+ BitwiseShiftValidator(const BinaryOperator *O, CheckerContext &C,
+ const BugType &B, bool P)
+ : Ctx(C), FoldedState(C.getState()), Op(O), BT(B), PedanticFlag(P) {}
+ void run();
+
+private:
+ const Expr *operandExpr(OperandSide Side) const {
+ return Side == OperandSide::Left ? Op->getLHS() : Op->getRHS();
+ }
+
+ bool shouldPerformPedanticChecks() const {
+ // The pedantic flag has no effect under C++20 because the affected issues
+ // are no longer undefined under that version of the standard.
+ return PedanticFlag && !Ctx.getASTContext().getLangOpts().CPlusPlus20;
+ }
+
+ bool assumeRequirement(OperandSide Side, BinaryOperator::Opcode Cmp, unsigned Limit);
+
+ void recordAssumption(OperandSide Side, BinaryOperator::Opcode Cmp, unsigned Limit);
+ const NoteTag *createNoteTag() const;
+
+ BugReportPtr createBugReport(StringRef ShortMsg, StringRef Msg) const;
+
+ BugReportPtr checkOvershift();
+ BugReportPtr checkOperandNegative(OperandSide Side);
+ BugReportPtr checkLeftShiftOverflow();
+
+ bool isLeftShift() const { return Op->getOpcode() == BO_Shl; }
+ StringRef shiftDir() const { return isLeftShift() ? "left" : "right"; }
+ static StringRef pluralSuffix(unsigned n) { return n <= 1 ? "" : "s"; }
+ static StringRef verbSuffix(unsigned n) { return n <= 1 ? "s" : ""; }
+};
+
+void BitwiseShiftValidator::run() {
+ // Report a bug if the right operand is >= the bit width of the type of the
+ // left operand:
+ if (BugReportPtr BR = checkOvershift()) {
+ Ctx.emitReport(std::move(BR));
+ return;
+ }
+
+ // Report a bug if the right operand is negative:
+ if (BugReportPtr BR = checkOperandNegative(OperandSide::Right)) {
+ Ctx.emitReport(std::move(BR));
+ return;
+ }
+
+ if (shouldPerformPedanticChecks()) {
+ // Report a bug if the left operand is negative:
+ if (BugReportPtr BR = checkOperandNegative(OperandSide::Left)) {
+ Ctx.emitReport(std::move(BR));
+ return;
+ }
+
+ // Report a bug when left shift of a concrete signed value overflows:
+ if (BugReportPtr BR = checkLeftShiftOverflow()) {
+ Ctx.emitReport(std::move(BR));
+ return;
+ }
+ }
+
+ // No bugs detected, update the state and add a single note tag which
+ // summarizes the new assumptions.
+ Ctx.addTransition(FoldedState, createNoteTag());
+}
+
+/// This method checks a requirement that must be satisfied by the value on the
+/// given Side of a bitwise shift operator in well-defined code. If the
+/// requirement is incompatible with prior knowledge, this method reports
+/// failure by returning false.
+bool BitwiseShiftValidator::assumeRequirement(OperandSide Side,
+ BinaryOperator::Opcode Comparison,
+ unsigned Limit) {
+ SValBuilder &SVB = Ctx.getSValBuilder();
+
+ const SVal OperandVal = Ctx.getSVal(operandExpr(Side));
+ const auto LimitVal = SVB.makeIntVal(Limit, Ctx.getASTContext().IntTy);
+ // Note that the type of `LimitVal` must be a signed, because otherwise a
+ // negative `Val` could be converted to a large positive value.
+
+ auto ResultVal = SVB.evalBinOp(FoldedState, Comparison, OperandVal, LimitVal,
+ SVB.getConditionType());
+ if (auto DURes = ResultVal.getAs<DefinedOrUnknownSVal>()) {
+ auto [StTrue, StFalse] = FoldedState->assume(DURes.value());
+ if (!StTrue) {
+ // We detected undefined behavior (the caller will report it).
+ FoldedState = StFalse;
+ return false;
+ }
+ // The code may be valid, so let's assume that it's valid:
+ FoldedState = StTrue;
+ if (StFalse) {
+ // Record note tag data for the assumption that we made
+ recordAssumption(Side, Comparison, Limit);
+ }
+ }
+ return true;
+}
+
+BugReportPtr BitwiseShiftValidator::checkOvershift() {
+ const QualType LHSTy = Op->getLHS()->getType();
+ const unsigned LHSBitWidth = Ctx.getASTContext().getIntWidth(LHSTy);
+
+ if (assumeRequirement(OperandSide::Right, BO_LT, LHSBitWidth))
+ return nullptr;
+
+ const SVal Right = Ctx.getSVal(operandExpr(OperandSide::Right));
+
+ std::string RightOpStr = "", LowerBoundStr = "";
+ if (auto ConcreteRight = Right.getAs<nonloc::ConcreteInt>())
+ RightOpStr = formatv(" '{0}'", ConcreteRight->getValue());
+ else {
+ SValBuilder &SVB = Ctx.getSValBuilder();
+ if (const llvm::APSInt *MinRight = SVB.getMinValue(FoldedState, Right)) {
+ LowerBoundStr = formatv(" >= {0},", MinRight->getExtValue());
+ }
+ }
+
+ std::string ShortMsg = formatv(
+ "{0} shift{1}{2} overflows the capacity of '{3}'",
+ isLeftShift() ? "Left" : "Right", RightOpStr.empty() ? "" : " by",
+ RightOpStr, LHSTy.getAsString());
+ std::string Msg = formatv(
+ "The result of {0} shift is undefined because the right "
+ "operand{1} is{2} not smaller than {3}, the capacity of '{4}'",
+ shiftDir(), RightOpStr, LowerBoundStr, LHSBitWidth, LHSTy.getAsString());
+ return createBugReport(ShortMsg, Msg);
+}
+
+// Before C++20, at 5.8 [expr.shift] (N4296, 2014-11-19) the standard says
+// 1. "... The behaviour is undefined if the right operand is negative..."
+// 2. "The value of E1 << E2 ...
+// if E1 has a signed type and non-negative value ...
+// otherwise, the behavior is undefined."
+// 3. "The value of E1 >> E2 ...
+// If E1 has a signed type and a negative value,
+// the resulting value is implementation-defined."
+// However, negative left arguments work in practice and the C++20 standard
+// eliminates conditions 2 and 3.
+BugReportPtr BitwiseShiftValidator::checkOperandNegative(OperandSide Side) {
+ // If the type is unsigned, it cannot be negative
+ if (!operandExpr(Side)->getType()->isSignedIntegerType())
+ return nullptr;
+
+ // Main check: determine whether the operand is constrained to be negative
+ if (assumeRequirement(Side, BO_GE, 0))
+ return nullptr;
+
+ std::string ShortMsg = formatv("{0} operand is negative in {1} shift",
+ Side == OperandSide::Left ? "Left" : "Right",
+ shiftDir())
+ .str();
+ std::string Msg = formatv("The result of {0} shift is undefined "
+ "because the {1} operand is negative",
+ shiftDir(),
+ Side == OperandSide::Left ? "left" : "right")
+ .str();
+
+ return createBugReport(ShortMsg, Msg);
+}
+
+BugReportPtr BitwiseShiftValidator::checkLeftShiftOverflow() {
+ // A right shift cannot be an overflowing left shift...
+ if (!isLeftShift())
+ return nullptr;
+
+ // In C++ it's well-defined to shift to the sign bit. In C however, it's UB.
+ // 5.8.2 [expr.shift] (N4296, 2014-11-19)
+ const bool ShouldPreserveSignBit = !Ctx.getLangOpts().CPlusPlus;
+
+ const Expr *LHS = operandExpr(OperandSide::Left);
+ const QualType LHSTy = LHS->getType();
+ const unsigned LeftBitWidth = Ctx.getASTContext().getIntWidth(LHSTy);
+ assert(LeftBitWidth > 0);
+
+ // Quote "For unsigned lhs, the value of LHS << RHS is the value of LHS *
+ // 2^RHS, reduced modulo maximum value of the return type plus 1."
+ if (LHSTy->isUnsignedIntegerType())
+ return nullptr;
+
+ // We only support concrete integers as left operand.
+ const auto Left = Ctx.getSVal(LHS).getAs<nonloc::ConcreteInt>();
+ if (!Left.has_value())
+ return nullptr;
+
+ // We should have already reported a bug if the left operand of the shift was
+ // negative, so it cannot be negative here.
+ assert(Left->getValue().isNonNegative());
+
+ const unsigned LeftAvailableBitWidth =
+ LeftBitWidth - static_cast<unsigned>(ShouldPreserveSignBit);
+ const unsigned UsedBitsInLeftOperand = Left->getValue().getActiveBits();
+ assert(LeftBitWidth >= UsedBitsInLeftOperand);
+ const unsigned MaximalAllowedShift =
+ LeftAvailableBitWidth - UsedBitsInLeftOperand;
+
+ if (assumeRequirement(OperandSide::Right, BO_LT, MaximalAllowedShift + 1))
+ return nullptr;
+
+ const std::string CapacityMsg =
+ formatv("because '{0}' can hold only {1} bits ({2} the sign bit)",
+ LHSTy.getAsString(), LeftAvailableBitWidth,
+ ShouldPreserveSignBit ? "excluding" : "including");
+
+ const SVal Right = Ctx.getSVal(Op->getRHS());
+
+ std::string ShortMsg, Msg;
+ if (const auto ConcreteRight = Right.getAs<nonloc::ConcreteInt>()) {
+ // Here ConcreteRight must contain a small non-negative integer, because
+ // otherwise one of the earlier checks should've reported a bug.
+ const unsigned RHS = ConcreteRight->getValue().getExtValue();
+ assert(RHS > MaximalAllowedShift);
+ const unsigned OverflownBits = RHS - MaximalAllowedShift;
+ ShortMsg = formatv(
+ "The shift '{0} << {1}' overflows the capacity of '{2}'",
+ Left->getValue(), ConcreteRight->getValue(), LHSTy.getAsString());
+ Msg = formatv(
+ "The shift '{0} << {1}' is undefined {2}, so {3} bit{4} overflow{5}",
+ Left->getValue(), ConcreteRight->getValue(), CapacityMsg, OverflownBits,
+ pluralSuffix(OverflownBits), verbSuffix(OverflownBits));
+ } else {
+ ShortMsg = formatv("Left shift of '{0}' overflows the capacity of '{1}'",
+ Left->getValue(), LHSTy.getAsString());
+ Msg = formatv(
+ "Left shift of '{0}' is undefined {1}, so some bits overflow",
+ Left->getValue(), CapacityMsg);
+ }
+
+ return createBugReport(ShortMsg, Msg);
+}
+
+void BitwiseShiftValidator::recordAssumption(OperandSide Side,
+ BinaryOperator::Opcode Comparison,
+ unsigned Limit) {
+ switch (Comparison) {
+ case BO_GE:
+ assert(Limit == 0);
+ NonNegOperands |= (Side == OperandSide::Left ? NonNegLeft : NonNegRight);
+ break;
+ case BO_LT:
+ assert(Side == OperandSide::Right);
+ if (!UpperBoundBitCount || Limit < UpperBoundBitCount.value())
+ UpperBoundBitCount = Limit;
+ break;
+ default:
+ llvm_unreachable("this checker does not use other comparison operators");
+ }
+}
+
+const NoteTag *BitwiseShiftValidator::createNoteTag() const {
+ if (!NonNegOperands && !UpperBoundBitCount)
+ return nullptr;
+
+ SmallString<128> Buf;
+ llvm::raw_svector_ostream Out(Buf);
+ Out << "Assuming ";
+ NoteTagTemplate Templ = NoteTagTemplates[NonNegOperands];
+ Out << Templ.SignInfo;
+ if (UpperBoundBitCount)
+ Out << Templ.UpperBoundIntro << UpperBoundBitCount.value();
+ const std::string Msg(Out.str());
+
+ return Ctx.getNoteTag(Msg, /*isPrunable=*/true);
+}
+
+std::unique_ptr<PathSensitiveBugReport>
+BitwiseShiftValidator::createBugReport(StringRef ShortMsg, StringRef Msg) const {
+ ProgramStateRef State = Ctx.getState();
+ if (ExplodedNode *ErrNode = Ctx.generateErrorNode(State)) {
+ auto BR =
+ std::make_unique<PathSensitiveBugReport>(BT, ShortMsg, Msg, ErrNode);
+ bugreporter::trackExpressionValue(ErrNode, Op->getLHS(), *BR);
+ bugreporter::trackExpressionValue(ErrNode, Op->getRHS(), *BR);
+ return BR;
+ }
+ return nullptr;
+}
+} // anonymous namespace
+
+class BitwiseShiftChecker : public Checker<check::PreStmt<BinaryOperator>> {
+ BugType BT{this, "Bitwise shift", "Suspicious operation"};
+
+public:
+ void checkPreStmt(const BinaryOperator *B, CheckerContext &Ctx) const {
+ BinaryOperator::Opcode Op = B->getOpcode();
+
+ if (Op != BO_Shl && Op != BO_Shr)
+ return;
+
+ BitwiseShiftValidator(B, Ctx, BT, Pedantic).run();
+ }
+
+ bool Pedantic = false;
+};
+
+void ento::registerBitwiseShiftChecker(CheckerManager &Mgr) {
+ auto *Chk = Mgr.registerChecker<BitwiseShiftChecker>();
+ const AnalyzerOptions &Opts = Mgr.getAnalyzerOptions();
+ Chk->Pedantic = Opts.getCheckerBooleanOption(Chk, "Pedantic");
+}
+
+bool ento::shouldRegisterBitwiseShiftChecker(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 2752b37f9b3f..66e080adb138 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -24,20 +25,31 @@ using namespace clang;
using namespace ento;
namespace {
-
class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
-
- mutable IdentifierInfo *IILockGuard, *IIUniqueLock;
-
- CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
- PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
- MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
-
- StringRef ClassLockGuard, ClassUniqueLock;
-
- mutable bool IdentifierInfoInitialized;
-
- std::unique_ptr<BugType> BlockInCritSectionBugType;
+ mutable IdentifierInfo *IILockGuard = nullptr;
+ mutable IdentifierInfo *IIUniqueLock = nullptr;
+ mutable bool IdentifierInfoInitialized = false;
+
+ const CallDescription LockFn{{"lock"}};
+ const CallDescription UnlockFn{{"unlock"}};
+ const CallDescription SleepFn{{"sleep"}};
+ const CallDescription GetcFn{{"getc"}};
+ const CallDescription FgetsFn{{"fgets"}};
+ const CallDescription ReadFn{{"read"}};
+ const CallDescription RecvFn{{"recv"}};
+ const CallDescription PthreadLockFn{{"pthread_mutex_lock"}};
+ const CallDescription PthreadTryLockFn{{"pthread_mutex_trylock"}};
+ const CallDescription PthreadUnlockFn{{"pthread_mutex_unlock"}};
+ const CallDescription MtxLock{{"mtx_lock"}};
+ const CallDescription MtxTimedLock{{"mtx_timedlock"}};
+ const CallDescription MtxTryLock{{"mtx_trylock"}};
+ const CallDescription MtxUnlock{{"mtx_unlock"}};
+
+ const llvm::StringLiteral ClassLockGuard{"lock_guard"};
+ const llvm::StringLiteral ClassUniqueLock{"unique_lock"};
+
+ const BugType BlockInCritSectionBugType{
+ this, "Call to blocking function in critical section", "Blocking Error"};
void initIdentifierInfo(ASTContext &Ctx) const;
@@ -46,8 +58,6 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
CheckerContext &C) const;
public:
- BlockInCriticalSectionChecker();
-
bool isBlockingFunction(const CallEvent &Call) const;
bool isLockFunction(const CallEvent &Call) const;
bool isUnlockFunction(const CallEvent &Call) const;
@@ -62,26 +72,6 @@ public:
REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
-BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
- : IILockGuard(nullptr), IIUniqueLock(nullptr),
- LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
- FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"),
- PthreadLockFn("pthread_mutex_lock"),
- PthreadTryLockFn("pthread_mutex_trylock"),
- PthreadUnlockFn("pthread_mutex_unlock"),
- MtxLock("mtx_lock"),
- MtxTimedLock("mtx_timedlock"),
- MtxTryLock("mtx_trylock"),
- MtxUnlock("mtx_unlock"),
- ClassLockGuard("lock_guard"),
- ClassUniqueLock("unique_lock"),
- IdentifierInfoInitialized(false) {
- // Initialize the bug type.
- BlockInCritSectionBugType.reset(
- new BugType(this, "Call to blocking function in critical section",
- "Blocking Error"));
-}
-
void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const {
if (!IdentifierInfoInitialized) {
/* In case of checking C code, or when the corresponding headers are not
@@ -96,14 +86,7 @@ void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const {
}
bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
- if (Call.isCalled(SleepFn)
- || Call.isCalled(GetcFn)
- || Call.isCalled(FgetsFn)
- || Call.isCalled(ReadFn)
- || Call.isCalled(RecvFn)) {
- return true;
- }
- return false;
+ return matchesAny(Call, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn);
}
bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
@@ -113,15 +96,8 @@ bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const
return true;
}
- if (Call.isCalled(LockFn)
- || Call.isCalled(PthreadLockFn)
- || Call.isCalled(PthreadTryLockFn)
- || Call.isCalled(MtxLock)
- || Call.isCalled(MtxTimedLock)
- || Call.isCalled(MtxTryLock)) {
- return true;
- }
- return false;
+ return matchesAny(Call, LockFn, PthreadLockFn, PthreadTryLockFn, MtxLock,
+ MtxTimedLock, MtxTryLock);
}
bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
@@ -132,12 +108,7 @@ bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) cons
return true;
}
- if (Call.isCalled(UnlockFn)
- || Call.isCalled(PthreadUnlockFn)
- || Call.isCalled(MtxUnlock)) {
- return true;
- }
- return false;
+ return matchesAny(Call, UnlockFn, PthreadUnlockFn, MtxUnlock);
}
void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
@@ -173,7 +144,7 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection(
llvm::raw_string_ostream os(msg);
os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
<< "' inside of critical section";
- auto R = std::make_unique<PathSensitiveBugReport>(*BlockInCritSectionBugType,
+ auto R = std::make_unique<PathSensitiveBugReport>(BlockInCritSectionBugType,
os.str(), ErrNode);
R->addRange(Call.getSourceRange());
R->markInteresting(BlockDescSym);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
index 6c0caf3c4e78..a09db6d2d0ec 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
@@ -12,31 +12,33 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Checkers/Taint.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 <optional>
using namespace clang;
using namespace ento;
namespace {
class BoolAssignmentChecker : public Checker< check::Bind > {
- mutable std::unique_ptr<BuiltinBug> BT;
- void emitReport(ProgramStateRef state, CheckerContext &C) const;
+ const BugType BT{this, "Assignment of a non-Boolean value"};
+ void emitReport(ProgramStateRef state, CheckerContext &C,
+ bool IsTainted = false) const;
+
public:
void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const;
};
} // end anonymous namespace
-void BoolAssignmentChecker::emitReport(ProgramStateRef state,
- CheckerContext &C) const {
+void BoolAssignmentChecker::emitReport(ProgramStateRef state, CheckerContext &C,
+ bool IsTainted) const {
if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) {
- if (!BT)
- BT.reset(new BuiltinBug(this, "Assignment of a non-Boolean value"));
-
- C.emitReport(
- std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N));
+ StringRef Msg = IsTainted ? "Might assign a tainted non-Boolean value"
+ : "Assignment of a non-Boolean value";
+ C.emitReport(std::make_unique<PathSensitiveBugReport>(BT, Msg, N));
}
}
@@ -70,7 +72,7 @@ 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<NonLoc> NV = val.getAs<NonLoc>();
+ std::optional<NonLoc> NV = val.getAs<NonLoc>();
if (!NV)
return;
@@ -90,6 +92,8 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S,
if (!StIn)
emitReport(StOut, C);
+ if (StIn && StOut && taint::isTainted(state, *NV))
+ emitReport(StOut, C, /*IsTainted=*/true);
}
void ento::registerBoolAssignmentChecker(CheckerManager &mgr) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
index 13781b336426..61521c259ca9 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
@@ -66,7 +66,8 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
case Builtin::BI__builtin_expect:
case Builtin::BI__builtin_expect_with_probability:
case Builtin::BI__builtin_assume_aligned:
- case Builtin::BI__builtin_addressof: {
+ case Builtin::BI__builtin_addressof:
+ case Builtin::BI__builtin_function_start: {
// For __builtin_unpredictable, __builtin_expect,
// __builtin_expect_with_probability and __builtin_assume_aligned,
// just return the value of the subexpression.
@@ -80,22 +81,20 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
case Builtin::BI__builtin_alloca_with_align:
case Builtin::BI__builtin_alloca: {
- // FIXME: Refactor into StoreManager itself?
- MemRegionManager& RM = C.getStoreManager().getRegionManager();
- const AllocaRegion* R =
- RM.getAllocaRegion(CE, C.blockCount(), C.getLocationContext());
-
- // Set the extent of the region in bytes. This enables us to use the
- // SVal of the argument directly. If we save the extent in bits, we
- // cannot represent values like symbol*8.
- auto Size = Call.getArgSVal(0);
- if (Size.isUndef())
- return true; // Return true to model purity.
-
- state = setDynamicExtent(state, R, Size.castAs<DefinedOrUnknownSVal>(),
- C.getSValBuilder());
+ SValBuilder &SVB = C.getSValBuilder();
+ const loc::MemRegionVal R =
+ SVB.getAllocaRegionVal(CE, C.getLocationContext(), C.blockCount());
- C.addTransition(state->BindExpr(CE, LCtx, loc::MemRegionVal(R)));
+ // Set the extent of the region in bytes. This enables us to use the SVal
+ // of the argument directly. If we saved the extent in bits, it'd be more
+ // difficult to reason about values like symbol*8.
+ auto Size = Call.getArgSVal(0);
+ if (auto DefSize = Size.getAs<DefinedOrUnknownSVal>()) {
+ // This `getAs()` is mostly paranoia, because core.CallAndMessage reports
+ // undefined function arguments (unless it's disabled somehow).
+ state = setDynamicExtent(state, R.getRegion(), *DefSize, SVB);
+ }
+ C.addTransition(state->BindExpr(CE, LCtx, R));
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 69b90be9aa7e..b7b64c3da4f6 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -12,11 +12,13 @@
//===----------------------------------------------------------------------===//
#include "InterCheckerAPI.h"
+#include "clang/Basic/Builtins.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/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
@@ -25,30 +27,21 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
+#include <functional>
+#include <optional>
using namespace clang;
using namespace ento;
+using namespace std::placeholders;
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.
-};
+struct SourceArgExpr : AnyArgExpr {};
+struct DestinationArgExpr : AnyArgExpr {};
+struct SizeArgExpr : AnyArgExpr {};
using ErrorMessage = SmallString<128>;
enum class AccessKind { write, read };
@@ -72,6 +65,16 @@ static ErrorMessage createOutOfBoundErrorMsg(StringRef FunctionDescription,
}
enum class ConcatFnKind { none = 0, strcat = 1, strlcat = 2 };
+
+enum class CharKind { Regular = 0, Wide };
+constexpr CharKind CK_Regular = CharKind::Regular;
+constexpr CharKind CK_Wide = CharKind::Wide;
+
+static QualType getCharPtrType(ASTContext &Ctx, CharKind CK) {
+ return Ctx.getPointerType(CK == CharKind::Regular ? Ctx.CharTy
+ : Ctx.WideCharTy);
+}
+
class CStringChecker : public Checker< eval::Call,
check::PreStmt<DeclStmt>,
check::LiveSymbols,
@@ -79,23 +82,25 @@ class CStringChecker : public Checker< eval::Call,
check::RegionChanges
> {
mutable std::unique_ptr<BugType> BT_Null, BT_Bounds, BT_Overlap,
- BT_NotCString, BT_AdditionOverflow;
+ BT_NotCString, BT_AdditionOverflow, BT_UninitRead;
- mutable const char *CurrentFunctionDescription;
+ mutable const char *CurrentFunctionDescription = nullptr;
public:
/// The filter is used to filter out the diagnostics which are not enabled by
/// the user.
struct CStringChecksFilter {
- DefaultBool CheckCStringNullArg;
- DefaultBool CheckCStringOutOfBounds;
- DefaultBool CheckCStringBufferOverlap;
- DefaultBool CheckCStringNotNullTerm;
+ bool CheckCStringNullArg = false;
+ bool CheckCStringOutOfBounds = false;
+ bool CheckCStringBufferOverlap = false;
+ bool CheckCStringNotNullTerm = false;
+ bool CheckCStringUninitializedRead = false;
CheckerNameRef CheckNameCStringNullArg;
CheckerNameRef CheckNameCStringOutOfBounds;
CheckerNameRef CheckNameCStringBufferOverlap;
CheckerNameRef CheckNameCStringNotNullTerm;
+ CheckerNameRef CheckNameCStringUninitializedRead;
};
CStringChecksFilter Filter;
@@ -115,33 +120,52 @@ public:
const LocationContext *LCtx,
const CallEvent *Call) const;
- typedef void (CStringChecker::*FnCheck)(CheckerContext &,
- const CallExpr *) const;
+ using FnCheck = std::function<void(const CStringChecker *, CheckerContext &,
+ const CallEvent &)>;
+
CallDescriptionMap<FnCheck> Callbacks = {
- {{CDF_MaybeBuiltin, "memcpy", 3}, &CStringChecker::evalMemcpy},
- {{CDF_MaybeBuiltin, "mempcpy", 3}, &CStringChecker::evalMempcpy},
- {{CDF_MaybeBuiltin, "memcmp", 3}, &CStringChecker::evalMemcmp},
- {{CDF_MaybeBuiltin, "memmove", 3}, &CStringChecker::evalMemmove},
- {{CDF_MaybeBuiltin, "memset", 3}, &CStringChecker::evalMemset},
- {{CDF_MaybeBuiltin, "explicit_memset", 3}, &CStringChecker::evalMemset},
- {{CDF_MaybeBuiltin, "strcpy", 2}, &CStringChecker::evalStrcpy},
- {{CDF_MaybeBuiltin, "strncpy", 3}, &CStringChecker::evalStrncpy},
- {{CDF_MaybeBuiltin, "stpcpy", 2}, &CStringChecker::evalStpcpy},
- {{CDF_MaybeBuiltin, "strlcpy", 3}, &CStringChecker::evalStrlcpy},
- {{CDF_MaybeBuiltin, "strcat", 2}, &CStringChecker::evalStrcat},
- {{CDF_MaybeBuiltin, "strncat", 3}, &CStringChecker::evalStrncat},
- {{CDF_MaybeBuiltin, "strlcat", 3}, &CStringChecker::evalStrlcat},
- {{CDF_MaybeBuiltin, "strlen", 1}, &CStringChecker::evalstrLength},
- {{CDF_MaybeBuiltin, "strnlen", 2}, &CStringChecker::evalstrnLength},
- {{CDF_MaybeBuiltin, "strcmp", 2}, &CStringChecker::evalStrcmp},
- {{CDF_MaybeBuiltin, "strncmp", 3}, &CStringChecker::evalStrncmp},
- {{CDF_MaybeBuiltin, "strcasecmp", 2}, &CStringChecker::evalStrcasecmp},
- {{CDF_MaybeBuiltin, "strncasecmp", 3}, &CStringChecker::evalStrncasecmp},
- {{CDF_MaybeBuiltin, "strsep", 2}, &CStringChecker::evalStrsep},
- {{CDF_MaybeBuiltin, "bcopy", 3}, &CStringChecker::evalBcopy},
- {{CDF_MaybeBuiltin, "bcmp", 3}, &CStringChecker::evalMemcmp},
- {{CDF_MaybeBuiltin, "bzero", 2}, &CStringChecker::evalBzero},
- {{CDF_MaybeBuiltin, "explicit_bzero", 2}, &CStringChecker::evalBzero},
+ {{CDF_MaybeBuiltin, {"memcpy"}, 3},
+ std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Regular)},
+ {{CDF_MaybeBuiltin, {"wmemcpy"}, 3},
+ std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Wide)},
+ {{CDF_MaybeBuiltin, {"mempcpy"}, 3},
+ std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Regular)},
+ {{CDF_None, {"wmempcpy"}, 3},
+ std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Wide)},
+ {{CDF_MaybeBuiltin, {"memcmp"}, 3},
+ std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)},
+ {{CDF_MaybeBuiltin, {"wmemcmp"}, 3},
+ std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Wide)},
+ {{CDF_MaybeBuiltin, {"memmove"}, 3},
+ std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Regular)},
+ {{CDF_MaybeBuiltin, {"wmemmove"}, 3},
+ std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Wide)},
+ {{CDF_MaybeBuiltin, {"memset"}, 3}, &CStringChecker::evalMemset},
+ {{CDF_MaybeBuiltin, {"explicit_memset"}, 3}, &CStringChecker::evalMemset},
+ {{CDF_MaybeBuiltin, {"strcpy"}, 2}, &CStringChecker::evalStrcpy},
+ {{CDF_MaybeBuiltin, {"strncpy"}, 3}, &CStringChecker::evalStrncpy},
+ {{CDF_MaybeBuiltin, {"stpcpy"}, 2}, &CStringChecker::evalStpcpy},
+ {{CDF_MaybeBuiltin, {"strlcpy"}, 3}, &CStringChecker::evalStrlcpy},
+ {{CDF_MaybeBuiltin, {"strcat"}, 2}, &CStringChecker::evalStrcat},
+ {{CDF_MaybeBuiltin, {"strncat"}, 3}, &CStringChecker::evalStrncat},
+ {{CDF_MaybeBuiltin, {"strlcat"}, 3}, &CStringChecker::evalStrlcat},
+ {{CDF_MaybeBuiltin, {"strlen"}, 1}, &CStringChecker::evalstrLength},
+ {{CDF_MaybeBuiltin, {"wcslen"}, 1}, &CStringChecker::evalstrLength},
+ {{CDF_MaybeBuiltin, {"strnlen"}, 2}, &CStringChecker::evalstrnLength},
+ {{CDF_MaybeBuiltin, {"wcsnlen"}, 2}, &CStringChecker::evalstrnLength},
+ {{CDF_MaybeBuiltin, {"strcmp"}, 2}, &CStringChecker::evalStrcmp},
+ {{CDF_MaybeBuiltin, {"strncmp"}, 3}, &CStringChecker::evalStrncmp},
+ {{CDF_MaybeBuiltin, {"strcasecmp"}, 2}, &CStringChecker::evalStrcasecmp},
+ {{CDF_MaybeBuiltin, {"strncasecmp"}, 3},
+ &CStringChecker::evalStrncasecmp},
+ {{CDF_MaybeBuiltin, {"strsep"}, 2}, &CStringChecker::evalStrsep},
+ {{CDF_MaybeBuiltin, {"bcopy"}, 3}, &CStringChecker::evalBcopy},
+ {{CDF_MaybeBuiltin, {"bcmp"}, 3},
+ std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)},
+ {{CDF_MaybeBuiltin, {"bzero"}, 2}, &CStringChecker::evalBzero},
+ {{CDF_MaybeBuiltin, {"explicit_bzero"}, 2}, &CStringChecker::evalBzero},
+ {{CDF_MaybeBuiltin, {"sprintf"}, 2}, &CStringChecker::evalSprintf},
+ {{CDF_MaybeBuiltin, {"snprintf"}, 2}, &CStringChecker::evalSnprintf},
};
// These require a bit of special handling.
@@ -149,51 +173,53 @@ public:
StdCopyBackward{{"std", "copy_backward"}, 3};
FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) const;
- void evalMemcpy(CheckerContext &C, const CallExpr *CE) const;
- void evalMempcpy(CheckerContext &C, const CallExpr *CE) const;
- void evalMemmove(CheckerContext &C, const CallExpr *CE) const;
- void evalBcopy(CheckerContext &C, const CallExpr *CE) const;
- void evalCopyCommon(CheckerContext &C, const CallExpr *CE,
+ void evalMemcpy(CheckerContext &C, const CallEvent &Call, CharKind CK) const;
+ void evalMempcpy(CheckerContext &C, const CallEvent &Call, CharKind CK) const;
+ void evalMemmove(CheckerContext &C, const CallEvent &Call, CharKind CK) const;
+ void evalBcopy(CheckerContext &C, const CallEvent &Call) const;
+ void evalCopyCommon(CheckerContext &C, const CallEvent &Call,
ProgramStateRef state, SizeArgExpr Size,
DestinationArgExpr Dest, SourceArgExpr Source,
- bool Restricted, bool IsMempcpy) const;
+ bool Restricted, bool IsMempcpy, CharKind CK) const;
- void evalMemcmp(CheckerContext &C, const CallExpr *CE) const;
+ void evalMemcmp(CheckerContext &C, const CallEvent &Call, CharKind CK) const;
- void evalstrLength(CheckerContext &C, const CallExpr *CE) const;
- void evalstrnLength(CheckerContext &C, const CallExpr *CE) const;
- void evalstrLengthCommon(CheckerContext &C,
- const CallExpr *CE,
+ void evalstrLength(CheckerContext &C, const CallEvent &Call) const;
+ void evalstrnLength(CheckerContext &C, const CallEvent &Call) const;
+ void evalstrLengthCommon(CheckerContext &C, const CallEvent &Call,
bool IsStrnlen = false) const;
- void evalStrcpy(CheckerContext &C, const CallExpr *CE) const;
- void evalStrncpy(CheckerContext &C, const CallExpr *CE) const;
- void evalStpcpy(CheckerContext &C, const CallExpr *CE) const;
- void evalStrlcpy(CheckerContext &C, const CallExpr *CE) const;
- void evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool ReturnEnd,
- bool IsBounded, ConcatFnKind appendK,
+ void evalStrcpy(CheckerContext &C, const CallEvent &Call) const;
+ void evalStrncpy(CheckerContext &C, const CallEvent &Call) const;
+ void evalStpcpy(CheckerContext &C, const CallEvent &Call) const;
+ void evalStrlcpy(CheckerContext &C, const CallEvent &Call) const;
+ void evalStrcpyCommon(CheckerContext &C, const CallEvent &Call,
+ bool ReturnEnd, bool IsBounded, ConcatFnKind appendK,
bool returnPtr = true) const;
- void evalStrcat(CheckerContext &C, const CallExpr *CE) const;
- void evalStrncat(CheckerContext &C, const CallExpr *CE) const;
- void evalStrlcat(CheckerContext &C, const CallExpr *CE) const;
+ void evalStrcat(CheckerContext &C, const CallEvent &Call) const;
+ void evalStrncat(CheckerContext &C, const CallEvent &Call) const;
+ void evalStrlcat(CheckerContext &C, const CallEvent &Call) const;
- void evalStrcmp(CheckerContext &C, const CallExpr *CE) const;
- void evalStrncmp(CheckerContext &C, const CallExpr *CE) const;
- void evalStrcasecmp(CheckerContext &C, const CallExpr *CE) const;
- void evalStrncasecmp(CheckerContext &C, const CallExpr *CE) const;
- void evalStrcmpCommon(CheckerContext &C,
- const CallExpr *CE,
- bool IsBounded = false,
- bool IgnoreCase = false) const;
+ void evalStrcmp(CheckerContext &C, const CallEvent &Call) const;
+ void evalStrncmp(CheckerContext &C, const CallEvent &Call) const;
+ void evalStrcasecmp(CheckerContext &C, const CallEvent &Call) const;
+ void evalStrncasecmp(CheckerContext &C, const CallEvent &Call) const;
+ void evalStrcmpCommon(CheckerContext &C, const CallEvent &Call,
+ bool IsBounded = false, bool IgnoreCase = false) const;
- void evalStrsep(CheckerContext &C, const CallExpr *CE) const;
+ void evalStrsep(CheckerContext &C, const CallEvent &Call) const;
- void evalStdCopy(CheckerContext &C, const CallExpr *CE) const;
- void evalStdCopyBackward(CheckerContext &C, const CallExpr *CE) const;
- void evalStdCopyCommon(CheckerContext &C, const CallExpr *CE) const;
- void evalMemset(CheckerContext &C, const CallExpr *CE) const;
- void evalBzero(CheckerContext &C, const CallExpr *CE) const;
+ void evalStdCopy(CheckerContext &C, const CallEvent &Call) const;
+ void evalStdCopyBackward(CheckerContext &C, const CallEvent &Call) const;
+ void evalStdCopyCommon(CheckerContext &C, const CallEvent &Call) const;
+ void evalMemset(CheckerContext &C, const CallEvent &Call) const;
+ void evalBzero(CheckerContext &C, const CallEvent &Call) const;
+
+ void evalSprintf(CheckerContext &C, const CallEvent &Call) const;
+ void evalSnprintf(CheckerContext &C, const CallEvent &Call) const;
+ void evalSprintfCommon(CheckerContext &C, const CallEvent &Call,
+ bool IsBounded, bool IsBuiltin) const;
// Utility methods
std::pair<ProgramStateRef , ProgramStateRef >
@@ -219,11 +245,34 @@ public:
const Expr *expr,
SVal val) const;
- static ProgramStateRef InvalidateBuffer(CheckerContext &C,
- ProgramStateRef state,
- const Expr *Ex, SVal V,
- bool IsSourceBuffer,
- const Expr *Size);
+ /// Invalidate the destination buffer determined by characters copied.
+ static ProgramStateRef
+ invalidateDestinationBufferBySize(CheckerContext &C, ProgramStateRef S,
+ const Expr *BufE, SVal BufV, SVal SizeV,
+ QualType SizeTy);
+
+ /// Operation never overflows, do not invalidate the super region.
+ static ProgramStateRef invalidateDestinationBufferNeverOverflows(
+ CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV);
+
+ /// We do not know whether the operation can overflow (e.g. size is unknown),
+ /// invalidate the super region and escape related pointers.
+ static ProgramStateRef invalidateDestinationBufferAlwaysEscapeSuperRegion(
+ CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV);
+
+ /// Invalidate the source buffer for escaping pointers.
+ static ProgramStateRef invalidateSourceBuffer(CheckerContext &C,
+ ProgramStateRef S,
+ const Expr *BufE, SVal BufV);
+
+ /// @param InvalidationTraitOperations Determine how to invlidate the
+ /// MemRegion by setting the invalidation traits. Return true to cause pointer
+ /// escape, or false otherwise.
+ static ProgramStateRef invalidateBufferAux(
+ CheckerContext &C, ProgramStateRef State, const Expr *Ex, SVal V,
+ llvm::function_ref<bool(RegionAndSymbolInvalidationTraits &,
+ const MemRegion *)>
+ InvalidationTraitOperations);
static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
const MemRegion *MR);
@@ -237,13 +286,16 @@ public:
AnyArgExpr Arg, SVal l) const;
ProgramStateRef CheckLocation(CheckerContext &C, ProgramStateRef state,
AnyArgExpr Buffer, SVal Element,
- AccessKind Access) const;
+ AccessKind Access,
+ CharKind CK = CharKind::Regular) const;
ProgramStateRef CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
AnyArgExpr Buffer, SizeArgExpr Size,
- AccessKind Access) const;
+ AccessKind Access,
+ CharKind CK = CharKind::Regular) const;
ProgramStateRef CheckOverlap(CheckerContext &C, ProgramStateRef state,
SizeArgExpr Size, AnyArgExpr First,
- AnyArgExpr Second) const;
+ AnyArgExpr Second,
+ CharKind CK = CharKind::Regular) const;
void emitOverlapBug(CheckerContext &C,
ProgramStateRef state,
const Stmt *First,
@@ -256,7 +308,8 @@ public:
void emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
const Stmt *S, StringRef WarningMsg) const;
void emitAdditionOverflowBug(CheckerContext &C, ProgramStateRef State) const;
-
+ void emitUninitializedReadBug(CheckerContext &C, ProgramStateRef State,
+ const Expr *E) const;
ProgramStateRef checkAdditionOverflow(CheckerContext &C,
ProgramStateRef state,
NonLoc left,
@@ -265,10 +318,9 @@ public:
// Return true if the destination buffer of the copy function may be in bound.
// Expects SVal of Size to be positive and unsigned.
// Expects SVal of FirstBuf to be a FieldRegion.
- static bool IsFirstBufInBound(CheckerContext &C,
- ProgramStateRef state,
- const Expr *FirstBuf,
- const Expr *Size);
+ static bool isFirstBufInBound(CheckerContext &C, ProgramStateRef State,
+ SVal BufVal, QualType BufTy, SVal LengthVal,
+ QualType LengthTy);
};
} //end anonymous namespace
@@ -282,7 +334,7 @@ REGISTER_MAP_WITH_PROGRAMSTATE(CStringLength, const MemRegion *, SVal)
std::pair<ProgramStateRef , ProgramStateRef >
CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef state, SVal V,
QualType Ty) {
- Optional<DefinedSVal> val = V.getAs<DefinedSVal>();
+ std::optional<DefinedSVal> val = V.getAs<DefinedSVal>();
if (!val)
return std::pair<ProgramStateRef , ProgramStateRef >(state, state);
@@ -325,7 +377,8 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
ProgramStateRef state,
AnyArgExpr Buffer, SVal Element,
- AccessKind Access) const {
+ AccessKind Access,
+ CharKind CK) const {
// If a previous check has failed, propagate the failure.
if (!state)
@@ -340,19 +393,38 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
if (!ER)
return state;
- if (ER->getValueType() != C.getASTContext().CharTy)
- return state;
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ ASTContext &Ctx = svalBuilder.getContext();
+
+ // Get the index of the accessed element.
+ NonLoc Idx = ER->getIndex();
+
+ if (CK == CharKind::Regular) {
+ if (ER->getValueType() != Ctx.CharTy)
+ return state;
+ } else {
+ if (ER->getValueType() != Ctx.WideCharTy)
+ return state;
+
+ QualType SizeTy = Ctx.getSizeType();
+ NonLoc WideSize =
+ svalBuilder
+ .makeIntVal(Ctx.getTypeSizeInChars(Ctx.WideCharTy).getQuantity(),
+ SizeTy)
+ .castAs<NonLoc>();
+ SVal Offset = svalBuilder.evalBinOpNN(state, BO_Mul, Idx, WideSize, SizeTy);
+ if (Offset.isUnknown())
+ return state;
+ Idx = Offset.castAs<NonLoc>();
+ }
// Get the size of the array.
const auto *superReg = cast<SubRegion>(ER->getSuperRegion());
DefinedOrUnknownSVal Size =
getDynamicExtent(state, superReg, C.getSValBuilder());
- // Get the index of the accessed element.
- DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
-
- ProgramStateRef StInBound = state->assumeInBound(Idx, Size, true);
- ProgramStateRef StOutBound = state->assumeInBound(Idx, Size, false);
+ ProgramStateRef StInBound, StOutBound;
+ std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, Size);
if (StOutBound && !StInBound) {
// These checks are either enabled by the CString out-of-bounds checker
// explicitly or implicitly by the Malloc checker.
@@ -367,16 +439,24 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
return nullptr;
}
+ // Ensure that we wouldn't read uninitialized value.
+ if (Access == AccessKind::read) {
+ if (Filter.CheckCStringUninitializedRead &&
+ StInBound->getSVal(ER).isUndef()) {
+ emitUninitializedReadBug(C, StInBound, Buffer.Expression);
+ return nullptr;
+ }
+ }
+
// Array bound check succeeded. From this point forward the array bound
// should always succeed.
return StInBound;
}
-ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C,
- ProgramStateRef State,
- AnyArgExpr Buffer,
- SizeArgExpr Size,
- AccessKind Access) const {
+ProgramStateRef
+CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
+ AnyArgExpr Buffer, SizeArgExpr Size,
+ AccessKind Access, CharKind CK) const {
// If a previous check has failed, propagate the failure.
if (!State)
return nullptr;
@@ -385,7 +465,7 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C,
ASTContext &Ctx = svalBuilder.getContext();
QualType SizeTy = Size.Expression->getType();
- QualType PtrTy = Ctx.getPointerType(Ctx.CharTy);
+ QualType PtrTy = getCharPtrType(Ctx, CK);
// Check that the first buffer is non-null.
SVal BufVal = C.getSVal(Buffer.Expression);
@@ -397,11 +477,19 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C,
if (!Filter.CheckCStringOutOfBounds)
return State;
+ SVal BufStart =
+ svalBuilder.evalCast(BufVal, PtrTy, Buffer.Expression->getType());
+
+ // Check if the first byte of the buffer is accessible.
+ State = CheckLocation(C, State, Buffer, BufStart, Access, CK);
+ if (!State)
+ return nullptr;
+
// 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.Expression);
- Optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
+ std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
if (!Length)
return State;
@@ -413,14 +501,11 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C,
NonLoc LastOffset = Offset.castAs<NonLoc>();
// Check that the first buffer is sufficiently long.
- SVal BufStart =
- svalBuilder.evalCast(BufVal, PtrTy, Buffer.Expression->getType());
- if (Optional<Loc> BufLoc = BufStart.getAs<Loc>()) {
+ if (std::optional<Loc> BufLoc = BufStart.getAs<Loc>()) {
SVal BufEnd =
svalBuilder.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy);
-
- State = CheckLocation(C, State, Buffer, BufEnd, Access);
+ State = CheckLocation(C, State, Buffer, BufEnd, Access, CK);
// If the buffer isn't large enough, abort.
if (!State)
@@ -434,7 +519,8 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C,
ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
ProgramStateRef state,
SizeArgExpr Size, AnyArgExpr First,
- AnyArgExpr Second) const {
+ AnyArgExpr Second,
+ CharKind CK) const {
if (!Filter.CheckCStringBufferOverlap)
return state;
@@ -448,16 +534,21 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
ProgramStateRef stateTrue, stateFalse;
+ // Assume different address spaces cannot overlap.
+ if (First.Expression->getType()->getPointeeType().getAddressSpace() !=
+ Second.Expression->getType()->getPointeeType().getAddressSpace())
+ return state;
+
// Get the buffer values and make sure they're known locations.
const LocationContext *LCtx = C.getLocationContext();
SVal firstVal = state->getSVal(First.Expression, LCtx);
SVal secondVal = state->getSVal(Second.Expression, LCtx);
- Optional<Loc> firstLoc = firstVal.getAs<Loc>();
+ std::optional<Loc> firstLoc = firstVal.getAs<Loc>();
if (!firstLoc)
return state;
- Optional<Loc> secondLoc = secondVal.getAs<Loc>();
+ std::optional<Loc> secondLoc = secondVal.getAs<Loc>();
if (!secondLoc)
return state;
@@ -480,7 +571,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
QualType cmpTy = svalBuilder.getConditionType();
SVal reverse =
svalBuilder.evalBinOpLL(state, BO_GT, *firstLoc, *secondLoc, cmpTy);
- Optional<DefinedOrUnknownSVal> reverseTest =
+ std::optional<DefinedOrUnknownSVal> reverseTest =
reverse.getAs<DefinedOrUnknownSVal>();
if (!reverseTest)
return state;
@@ -501,31 +592,31 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
// Get the length, and make sure it too is known.
SVal LengthVal = state->getSVal(Size.Expression, LCtx);
- Optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
+ std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
if (!Length)
return state;
// Convert the first buffer's start address to char*.
// Bail out if the cast fails.
ASTContext &Ctx = svalBuilder.getContext();
- QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy);
+ QualType CharPtrTy = getCharPtrType(Ctx, CK);
SVal FirstStart =
svalBuilder.evalCast(*firstLoc, CharPtrTy, First.Expression->getType());
- Optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>();
+ std::optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>();
if (!FirstStartLoc)
return state;
// Compute the end of the first buffer. Bail out if THAT fails.
SVal FirstEnd = svalBuilder.evalBinOpLN(state, BO_Add, *FirstStartLoc,
*Length, CharPtrTy);
- Optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>();
+ std::optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>();
if (!FirstEndLoc)
return state;
// Is the end of the first buffer past the start of the second buffer?
SVal Overlap =
svalBuilder.evalBinOpLL(state, BO_GT, *FirstEndLoc, *secondLoc, cmpTy);
- Optional<DefinedOrUnknownSVal> OverlapTest =
+ std::optional<DefinedOrUnknownSVal> OverlapTest =
Overlap.getAs<DefinedOrUnknownSVal>();
if (!OverlapTest)
return state;
@@ -565,13 +656,15 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state,
void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State,
const Stmt *S, StringRef WarningMsg) const {
if (ExplodedNode *N = C.generateErrorNode(State)) {
- if (!BT_Null)
- BT_Null.reset(new BuiltinBug(
- Filter.CheckNameCStringNullArg, categories::UnixAPI,
- "Null pointer argument in call to byte string function"));
+ if (!BT_Null) {
+ // FIXME: This call uses the string constant 'categories::UnixAPI' as the
+ // description of the bug; it should be replaced by a real description.
+ BT_Null.reset(
+ new BugType(Filter.CheckNameCStringNullArg, categories::UnixAPI));
+ }
- BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Null.get());
- auto Report = std::make_unique<PathSensitiveBugReport>(*BT, WarningMsg, N);
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(*BT_Null, WarningMsg, N);
Report->addRange(S->getSourceRange());
if (const auto *Ex = dyn_cast<Expr>(S))
bugreporter::trackExpressionValue(N, Ex, *Report);
@@ -579,23 +672,39 @@ void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State,
}
}
+void CStringChecker::emitUninitializedReadBug(CheckerContext &C,
+ ProgramStateRef State,
+ const Expr *E) const {
+ if (ExplodedNode *N = C.generateErrorNode(State)) {
+ const char *Msg =
+ "Bytes string function accesses uninitialized/garbage values";
+ if (!BT_UninitRead)
+ BT_UninitRead.reset(new BugType(Filter.CheckNameCStringUninitializedRead,
+ "Accessing unitialized/garbage values"));
+
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(*BT_UninitRead, Msg, N);
+ Report->addRange(E->getSourceRange());
+ bugreporter::trackExpressionValue(N, E, *Report);
+ C.emitReport(std::move(Report));
+ }
+}
+
void CStringChecker::emitOutOfBoundsBug(CheckerContext &C,
ProgramStateRef State, const Stmt *S,
StringRef WarningMsg) const {
if (ExplodedNode *N = C.generateErrorNode(State)) {
if (!BT_Bounds)
- BT_Bounds.reset(new BuiltinBug(
- Filter.CheckCStringOutOfBounds ? Filter.CheckNameCStringOutOfBounds
- : Filter.CheckNameCStringNullArg,
- "Out-of-bound array access",
- "Byte string function accesses out-of-bound array element"));
-
- BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Bounds.get());
+ BT_Bounds.reset(new BugType(Filter.CheckCStringOutOfBounds
+ ? Filter.CheckNameCStringOutOfBounds
+ : Filter.CheckNameCStringNullArg,
+ "Out-of-bound array access"));
// FIXME: It would be nice to eventually make this diagnostic more clear,
// e.g., by referencing the original declaration or by saying *why* this
// reference is outside the range.
- auto Report = std::make_unique<PathSensitiveBugReport>(*BT, WarningMsg, N);
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(*BT_Bounds, WarningMsg, N);
Report->addRange(S->getSourceRange());
C.emitReport(std::move(Report));
}
@@ -605,10 +714,12 @@ void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
const Stmt *S,
StringRef WarningMsg) const {
if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
- if (!BT_NotCString)
- BT_NotCString.reset(new BuiltinBug(
- Filter.CheckNameCStringNotNullTerm, categories::UnixAPI,
- "Argument is not a null-terminated string."));
+ if (!BT_NotCString) {
+ // FIXME: This call uses the string constant 'categories::UnixAPI' as the
+ // description of the bug; it should be replaced by a real description.
+ BT_NotCString.reset(
+ new BugType(Filter.CheckNameCStringNotNullTerm, categories::UnixAPI));
+ }
auto Report =
std::make_unique<PathSensitiveBugReport>(*BT_NotCString, WarningMsg, N);
@@ -621,10 +732,13 @@ void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
void CStringChecker::emitAdditionOverflowBug(CheckerContext &C,
ProgramStateRef State) const {
if (ExplodedNode *N = C.generateErrorNode(State)) {
- if (!BT_NotCString)
- BT_NotCString.reset(
- new BuiltinBug(Filter.CheckNameCStringOutOfBounds, "API",
- "Sum of expressions causes overflow."));
+ if (!BT_AdditionOverflow) {
+ // FIXME: This call uses the word "API" as the description of the bug;
+ // it should be replaced by a better error message (if this unlikely
+ // situation continues to exist as a separate bug type).
+ BT_AdditionOverflow.reset(
+ new BugType(Filter.CheckNameCStringOutOfBounds, "API"));
+ }
// This isn't a great error message, but this should never occur in real
// code anyway -- you'd have to create a buffer longer than a size_t can
@@ -633,8 +747,8 @@ void CStringChecker::emitAdditionOverflowBug(CheckerContext &C,
"This expression will create a string whose length is too big to "
"be represented as a size_t";
- auto Report =
- std::make_unique<PathSensitiveBugReport>(*BT_NotCString, WarningMsg, N);
+ auto Report = std::make_unique<PathSensitiveBugReport>(*BT_AdditionOverflow,
+ WarningMsg, N);
C.emitReport(std::move(Report));
}
}
@@ -659,7 +773,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,
NonLoc maxVal = svalBuilder.makeIntVal(maxValInt);
SVal maxMinusRight;
- if (right.getAs<nonloc::ConcreteInt>()) {
+ if (isa<nonloc::ConcreteInt>(right)) {
maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, right,
sizeTy);
} else {
@@ -670,7 +784,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,
left = right;
}
- if (Optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) {
+ if (std::optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) {
QualType cmpTy = svalBuilder.getConditionType();
// If left > max - right, we have an overflow.
SVal willOverflow = svalBuilder.evalBinOpNN(state, BO_GT, left,
@@ -756,7 +870,7 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C,
C.blockCount());
if (!hypothetical) {
- if (Optional<NonLoc> strLn = strLength.getAs<NonLoc>()) {
+ if (std::optional<NonLoc> strLn = strLength.getAs<NonLoc>()) {
// In case of unbounded calls strlen etc bound the range to SIZE_MAX/4
BasicValueFactory &BVF = svalBuilder.getBasicValueFactory();
const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy);
@@ -764,8 +878,8 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C,
const llvm::APSInt *maxLengthInt = BVF.evalAPSInt(BO_Div, maxValInt,
fourInt);
NonLoc maxLength = svalBuilder.makeIntVal(*maxLengthInt);
- SVal evalLength = svalBuilder.evalBinOpNN(state, BO_LE, *strLn,
- maxLength, sizeTy);
+ SVal evalLength = svalBuilder.evalBinOpNN(state, BO_LE, *strLn, maxLength,
+ svalBuilder.getConditionType());
state = state->assume(evalLength.castAs<DefinedOrUnknownSVal>(), true);
}
state = state->set<CStringLength>(MR, strLength);
@@ -782,7 +896,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
// If we can't get a region, see if it's something we /know/ isn't a
// C string. In the context of locations, the only time we can issue such
// a warning is for labels.
- if (Optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) {
+ if (std::optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) {
if (Filter.CheckCStringNotNullTerm) {
SmallString<120> buf;
llvm::raw_svector_ostream os(buf);
@@ -811,11 +925,25 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
SValBuilder &svalBuilder = C.getSValBuilder();
QualType sizeTy = svalBuilder.getContext().getSizeType();
const StringLiteral *strLit = cast<StringRegion>(MR)->getStringLiteral();
- return svalBuilder.makeIntVal(strLit->getByteLength(), sizeTy);
+ return svalBuilder.makeIntVal(strLit->getLength(), sizeTy);
+ }
+ case MemRegion::NonParamVarRegionKind: {
+ // If we have a global constant with a string literal initializer,
+ // compute the initializer's length.
+ const VarDecl *Decl = cast<NonParamVarRegion>(MR)->getDecl();
+ if (Decl->getType().isConstQualified() && Decl->hasGlobalStorage()) {
+ if (const Expr *Init = Decl->getInit()) {
+ if (auto *StrLit = dyn_cast<StringLiteral>(Init)) {
+ SValBuilder &SvalBuilder = C.getSValBuilder();
+ QualType SizeTy = SvalBuilder.getContext().getSizeType();
+ return SvalBuilder.makeIntVal(StrLit->getLength(), SizeTy);
+ }
+ }
+ }
+ [[fallthrough]];
}
case MemRegion::SymbolicRegionKind:
case MemRegion::AllocaRegionKind:
- case MemRegion::NonParamVarRegionKind:
case MemRegion::ParamVarRegionKind:
case MemRegion::FieldRegionKind:
case MemRegion::ObjCIvarRegionKind:
@@ -869,43 +997,40 @@ const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C,
return strRegion->getStringLiteral();
}
-bool CStringChecker::IsFirstBufInBound(CheckerContext &C,
- ProgramStateRef state,
- const Expr *FirstBuf,
- const Expr *Size) {
+bool CStringChecker::isFirstBufInBound(CheckerContext &C, ProgramStateRef State,
+ SVal BufVal, QualType BufTy,
+ SVal LengthVal, QualType LengthTy) {
// If we do not know that the buffer is long enough we return 'true'.
// Otherwise the parent region of this field region would also get
// invalidated, which would lead to warnings based on an unknown state.
+ if (LengthVal.isUnknown())
+ return false;
+
// Originally copied from CheckBufferAccess and CheckLocation.
- SValBuilder &svalBuilder = C.getSValBuilder();
- ASTContext &Ctx = svalBuilder.getContext();
- const LocationContext *LCtx = C.getLocationContext();
+ SValBuilder &SB = C.getSValBuilder();
+ ASTContext &Ctx = C.getASTContext();
- QualType sizeTy = Size->getType();
QualType PtrTy = Ctx.getPointerType(Ctx.CharTy);
- SVal BufVal = state->getSVal(FirstBuf, LCtx);
- SVal LengthVal = state->getSVal(Size, LCtx);
- Optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
+ std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
if (!Length)
return true; // cf top comment.
// Compute the offset of the last element to be accessed: size-1.
- NonLoc One = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>();
- SVal Offset = svalBuilder.evalBinOpNN(state, BO_Sub, *Length, One, sizeTy);
+ NonLoc One = SB.makeIntVal(1, LengthTy).castAs<NonLoc>();
+ SVal Offset = SB.evalBinOpNN(State, BO_Sub, *Length, One, LengthTy);
if (Offset.isUnknown())
return true; // cf top comment
NonLoc LastOffset = Offset.castAs<NonLoc>();
// Check that the first buffer is sufficiently long.
- SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType());
- Optional<Loc> BufLoc = BufStart.getAs<Loc>();
+ SVal BufStart = SB.evalCast(BufVal, PtrTy, BufTy);
+ std::optional<Loc> BufLoc = BufStart.getAs<Loc>();
if (!BufLoc)
return true; // cf top comment.
- SVal BufEnd =
- svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, LastOffset, PtrTy);
+ SVal BufEnd = SB.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy);
// Check for out of bound array element access.
const MemRegion *R = BufEnd.getAsRegion();
@@ -919,33 +1044,95 @@ bool CStringChecker::IsFirstBufInBound(CheckerContext &C,
// FIXME: Does this crash when a non-standard definition
// of a library function is encountered?
assert(ER->getValueType() == C.getASTContext().CharTy &&
- "IsFirstBufInBound should only be called with char* ElementRegions");
+ "isFirstBufInBound should only be called with char* ElementRegions");
// Get the size of the array.
const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion());
- DefinedOrUnknownSVal SizeDV = getDynamicExtent(state, superReg, svalBuilder);
+ DefinedOrUnknownSVal SizeDV = getDynamicExtent(State, superReg, SB);
// Get the index of the accessed element.
DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
- ProgramStateRef StInBound = state->assumeInBound(Idx, SizeDV, true);
+ ProgramStateRef StInBound = State->assumeInBound(Idx, SizeDV, true);
return static_cast<bool>(StInBound);
}
-ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C,
- ProgramStateRef state,
- const Expr *E, SVal V,
- bool IsSourceBuffer,
- const Expr *Size) {
- Optional<Loc> L = V.getAs<Loc>();
+ProgramStateRef CStringChecker::invalidateDestinationBufferBySize(
+ CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV,
+ SVal SizeV, QualType SizeTy) {
+ auto InvalidationTraitOperations =
+ [&C, S, BufTy = BufE->getType(), BufV, SizeV,
+ SizeTy](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) {
+ // If destination buffer is a field region and access is in bound, do
+ // not invalidate its super region.
+ if (MemRegion::FieldRegionKind == R->getKind() &&
+ isFirstBufInBound(C, S, BufV, BufTy, SizeV, SizeTy)) {
+ ITraits.setTrait(
+ R,
+ RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
+ }
+ return false;
+ };
+
+ return invalidateBufferAux(C, S, BufE, BufV, InvalidationTraitOperations);
+}
+
+ProgramStateRef
+CStringChecker::invalidateDestinationBufferAlwaysEscapeSuperRegion(
+ CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV) {
+ auto InvalidationTraitOperations = [](RegionAndSymbolInvalidationTraits &,
+ const MemRegion *R) {
+ return isa<FieldRegion>(R);
+ };
+
+ return invalidateBufferAux(C, S, BufE, BufV, InvalidationTraitOperations);
+}
+
+ProgramStateRef CStringChecker::invalidateDestinationBufferNeverOverflows(
+ CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV) {
+ auto InvalidationTraitOperations =
+ [](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) {
+ if (MemRegion::FieldRegionKind == R->getKind())
+ ITraits.setTrait(
+ R,
+ RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
+ return false;
+ };
+
+ return invalidateBufferAux(C, S, BufE, BufV, InvalidationTraitOperations);
+}
+
+ProgramStateRef CStringChecker::invalidateSourceBuffer(CheckerContext &C,
+ ProgramStateRef S,
+ const Expr *BufE,
+ SVal BufV) {
+ auto InvalidationTraitOperations =
+ [](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) {
+ ITraits.setTrait(
+ R->getBaseRegion(),
+ RegionAndSymbolInvalidationTraits::TK_PreserveContents);
+ ITraits.setTrait(R,
+ RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
+ return true;
+ };
+
+ return invalidateBufferAux(C, S, BufE, BufV, InvalidationTraitOperations);
+}
+
+ProgramStateRef CStringChecker::invalidateBufferAux(
+ CheckerContext &C, ProgramStateRef State, const Expr *E, SVal V,
+ llvm::function_ref<bool(RegionAndSymbolInvalidationTraits &,
+ const MemRegion *)>
+ InvalidationTraitOperations) {
+ std::optional<Loc> L = V.getAs<Loc>();
if (!L)
- return state;
+ return State;
// FIXME: This is a simplified version of what's in CFRefCount.cpp -- it makes
// some assumptions about the value that CFRefCount can't. Even so, it should
// probably be refactored.
- if (Optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) {
+ if (std::optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) {
const MemRegion *R = MR->getRegion()->StripCasts();
// Are we dealing with an ElementRegion? If so, we should be invalidating
@@ -957,29 +1144,10 @@ ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C,
// Invalidate this region.
const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
-
- bool CausesPointerEscape = false;
RegionAndSymbolInvalidationTraits ITraits;
- // Invalidate and escape only indirect regions accessible through the source
- // buffer.
- if (IsSourceBuffer) {
- ITraits.setTrait(R->getBaseRegion(),
- RegionAndSymbolInvalidationTraits::TK_PreserveContents);
- ITraits.setTrait(R, RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
- CausesPointerEscape = true;
- } else {
- const MemRegion::Kind& K = R->getKind();
- if (K == MemRegion::FieldRegionKind)
- if (Size && IsFirstBufInBound(C, state, E, Size)) {
- // If destination buffer is a field region and access is in bound,
- // do not invalidate its super region.
- ITraits.setTrait(
- R,
- RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
- }
- }
+ bool CausesPointerEscape = InvalidationTraitOperations(ITraits, R);
- return state->invalidateRegions(R, E, C.blockCount(), LCtx,
+ return State->invalidateRegions(R, E, C.blockCount(), LCtx,
CausesPointerEscape, nullptr, nullptr,
&ITraits);
}
@@ -987,7 +1155,7 @@ ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C,
// If we have a non-region value by chance, just remove the binding.
// FIXME: is this necessary or correct? This handles the non-Region
// cases. Is it ever valid to store to these?
- return state->killBinding(*L);
+ return State->killBinding(*L);
}
bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
@@ -1009,23 +1177,20 @@ bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
case MemRegion::CXXThisRegionKind:
case MemRegion::CXXTempObjectRegionKind:
os << "a C++ temp object of type "
- << cast<TypedValueRegion>(MR)->getValueType().getAsString();
+ << cast<TypedValueRegion>(MR)->getValueType();
return true;
case MemRegion::NonParamVarRegionKind:
- os << "a variable of type"
- << cast<TypedValueRegion>(MR)->getValueType().getAsString();
+ os << "a variable of type" << cast<TypedValueRegion>(MR)->getValueType();
return true;
case MemRegion::ParamVarRegionKind:
- os << "a parameter of type"
- << cast<TypedValueRegion>(MR)->getValueType().getAsString();
+ os << "a parameter of type" << cast<TypedValueRegion>(MR)->getValueType();
return true;
case MemRegion::FieldRegionKind:
- os << "a field of type "
- << cast<TypedValueRegion>(MR)->getValueType().getAsString();
+ os << "a field of type " << cast<TypedValueRegion>(MR)->getValueType();
return true;
case MemRegion::ObjCIvarRegionKind:
os << "an instance variable of type "
- << cast<TypedValueRegion>(MR)->getValueType().getAsString();
+ << cast<TypedValueRegion>(MR)->getValueType();
return true;
default:
return false;
@@ -1048,7 +1213,7 @@ bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal,
RegionOffset Offset = MR->getAsOffset();
const MemRegion *BR = Offset.getRegion();
- Optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>();
+ std::optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>();
if (!SizeNL)
return false;
@@ -1087,8 +1252,8 @@ bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal,
} else {
// If the destination buffer's extent is not equal to the value of
// third argument, just invalidate buffer.
- State = InvalidateBuffer(C, State, DstBuffer, MemVal,
- /*IsSourceBuffer*/ false, Size);
+ State = invalidateDestinationBufferBySize(C, State, DstBuffer, MemVal,
+ SizeVal, Size->getType());
}
if (StateNullChar && !StateNonNullChar) {
@@ -1113,8 +1278,8 @@ bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal,
} else {
// If the offset is not zero and char value is not concrete, we can do
// nothing but invalidate the buffer.
- State = InvalidateBuffer(C, State, DstBuffer, MemVal,
- /*IsSourceBuffer*/ false, Size);
+ State = invalidateDestinationBufferBySize(C, State, DstBuffer, MemVal,
+ SizeVal, Size->getType());
}
return true;
}
@@ -1123,11 +1288,11 @@ bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal,
// evaluation of individual function calls.
//===----------------------------------------------------------------------===//
-void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE,
+void CStringChecker::evalCopyCommon(CheckerContext &C, const CallEvent &Call,
ProgramStateRef state, SizeArgExpr Size,
DestinationArgExpr Dest,
SourceArgExpr Source, bool Restricted,
- bool IsMempcpy) const {
+ bool IsMempcpy, CharKind CK) const {
CurrentFunctionDescription = "memory copy function";
// See if the size argument is zero.
@@ -1145,7 +1310,8 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE,
// If the size is zero, there won't be any actual memory access, so
// just bind the return value to the destination buffer and return.
if (stateZeroSize && !stateNonZeroSize) {
- stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, destVal);
+ stateZeroSize =
+ stateZeroSize->BindExpr(Call.getOriginExpr(), LCtx, destVal);
C.addTransition(stateZeroSize);
return;
}
@@ -1170,11 +1336,11 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE,
return;
// Ensure the accesses are valid and that the buffers do not overlap.
- state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write);
- state = CheckBufferAccess(C, state, Source, Size, AccessKind::read);
+ state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write, CK);
+ state = CheckBufferAccess(C, state, Source, Size, AccessKind::read, CK);
if (Restricted)
- state = CheckOverlap(C, state, Size, Dest, Source);
+ state = CheckOverlap(C, state, Size, Dest, Source, CK);
if (!state)
return;
@@ -1185,7 +1351,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE,
// Get the byte after the last byte copied.
SValBuilder &SvalBuilder = C.getSValBuilder();
ASTContext &Ctx = SvalBuilder.getContext();
- QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy);
+ QualType CharPtrTy = getCharPtrType(Ctx, CK);
SVal DestRegCharVal =
SvalBuilder.evalCast(destVal, CharPtrTy, Dest.Expression->getType());
SVal lastElement = C.getSValBuilder().evalBinOp(
@@ -1193,15 +1359,15 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE,
// If we don't know how much we copied, we can at least
// conjure a return value for later.
if (lastElement.isUnknown())
- lastElement = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx,
- C.blockCount());
+ lastElement = C.getSValBuilder().conjureSymbolVal(
+ nullptr, Call.getOriginExpr(), LCtx, C.blockCount());
// The byte after the last byte copied is the return value.
- state = state->BindExpr(CE, LCtx, lastElement);
+ state = state->BindExpr(Call.getOriginExpr(), LCtx, lastElement);
} else {
// All other copies return the destination buffer.
// (Well, bcopy() has a void return type, but this won't hurt.)
- state = state->BindExpr(CE, LCtx, destVal);
+ state = state->BindExpr(Call.getOriginExpr(), LCtx, destVal);
}
// Invalidate the destination (regular invalidation without pointer-escaping
@@ -1210,76 +1376,82 @@ void CStringChecker::evalCopyCommon(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
// copied region, but that's still an improvement over blank invalidation.
- state =
- InvalidateBuffer(C, state, Dest.Expression, C.getSVal(Dest.Expression),
- /*IsSourceBuffer*/ false, Size.Expression);
+ state = invalidateDestinationBufferBySize(
+ C, state, Dest.Expression, C.getSVal(Dest.Expression), sizeVal,
+ Size.Expression->getType());
// Invalidate the source (const-invalidation without const-pointer-escaping
// the address of the top-level region).
- state = InvalidateBuffer(C, state, Source.Expression,
- C.getSVal(Source.Expression),
- /*IsSourceBuffer*/ true, nullptr);
+ state = invalidateSourceBuffer(C, state, Source.Expression,
+ C.getSVal(Source.Expression));
C.addTransition(state);
}
}
-void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalMemcpy(CheckerContext &C, const CallEvent &Call,
+ CharKind CK) const {
// void *memcpy(void *restrict dst, const void *restrict src, size_t n);
// The return value is the address of the destination buffer.
- DestinationArgExpr Dest = {CE->getArg(0), 0};
- SourceArgExpr Src = {CE->getArg(1), 1};
- SizeArgExpr Size = {CE->getArg(2), 2};
+ DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}};
+ SourceArgExpr Src = {{Call.getArgExpr(1), 1}};
+ SizeArgExpr Size = {{Call.getArgExpr(2), 2}};
ProgramStateRef State = C.getState();
constexpr bool IsRestricted = true;
constexpr bool IsMempcpy = false;
- evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy);
+ evalCopyCommon(C, Call, State, Size, Dest, Src, IsRestricted, IsMempcpy, CK);
}
-void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalMempcpy(CheckerContext &C, const CallEvent &Call,
+ CharKind CK) 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.
- DestinationArgExpr Dest = {CE->getArg(0), 0};
- SourceArgExpr Src = {CE->getArg(1), 1};
- SizeArgExpr Size = {CE->getArg(2), 2};
+ DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}};
+ SourceArgExpr Src = {{Call.getArgExpr(1), 1}};
+ SizeArgExpr Size = {{Call.getArgExpr(2), 2}};
constexpr bool IsRestricted = true;
constexpr bool IsMempcpy = true;
- evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy);
+ evalCopyCommon(C, Call, C.getState(), Size, Dest, Src, IsRestricted,
+ IsMempcpy, CK);
}
-void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalMemmove(CheckerContext &C, const CallEvent &Call,
+ CharKind CK) const {
// void *memmove(void *dst, const void *src, size_t n);
// The return value is the address of the destination buffer.
- DestinationArgExpr Dest = {CE->getArg(0), 0};
- SourceArgExpr Src = {CE->getArg(1), 1};
- SizeArgExpr Size = {CE->getArg(2), 2};
+ DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}};
+ SourceArgExpr Src = {{Call.getArgExpr(1), 1}};
+ SizeArgExpr Size = {{Call.getArgExpr(2), 2}};
constexpr bool IsRestricted = false;
constexpr bool IsMempcpy = false;
- evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy);
+ evalCopyCommon(C, Call, C.getState(), Size, Dest, Src, IsRestricted,
+ IsMempcpy, CK);
}
-void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalBcopy(CheckerContext &C, const CallEvent &Call) const {
// void bcopy(const void *src, void *dst, size_t n);
- SourceArgExpr Src(CE->getArg(0), 0);
- DestinationArgExpr Dest = {CE->getArg(1), 1};
- SizeArgExpr Size = {CE->getArg(2), 2};
+ SourceArgExpr Src{{Call.getArgExpr(0), 0}};
+ DestinationArgExpr Dest = {{Call.getArgExpr(1), 1}};
+ SizeArgExpr Size = {{Call.getArgExpr(2), 2}};
constexpr bool IsRestricted = false;
constexpr bool IsMempcpy = false;
- evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy);
+ evalCopyCommon(C, Call, C.getState(), Size, Dest, Src, IsRestricted,
+ IsMempcpy, CharKind::Regular);
}
-void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalMemcmp(CheckerContext &C, const CallEvent &Call,
+ CharKind CK) const {
// int memcmp(const void *s1, const void *s2, size_t n);
CurrentFunctionDescription = "memory comparison function";
- AnyArgExpr Left = {CE->getArg(0), 0};
- AnyArgExpr Right = {CE->getArg(1), 1};
- SizeArgExpr Size = {CE->getArg(2), 2};
+ AnyArgExpr Left = {Call.getArgExpr(0), 0};
+ AnyArgExpr Right = {Call.getArgExpr(1), 1};
+ SizeArgExpr Size = {{Call.getArgExpr(2), 2}};
ProgramStateRef State = C.getState();
SValBuilder &Builder = C.getSValBuilder();
@@ -1297,7 +1469,8 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
// have to check either of the buffers.
if (stateZeroSize) {
State = stateZeroSize;
- State = State->BindExpr(CE, LCtx, Builder.makeZeroVal(CE->getType()));
+ State = State->BindExpr(Call.getOriginExpr(), LCtx,
+ Builder.makeZeroVal(Call.getResultType()));
C.addTransition(State);
}
@@ -1323,8 +1496,8 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
State = SameBuffer;
State = CheckBufferAccess(C, State, Left, Size, AccessKind::read);
if (State) {
- State =
- SameBuffer->BindExpr(CE, LCtx, Builder.makeZeroVal(CE->getType()));
+ State = SameBuffer->BindExpr(Call.getOriginExpr(), LCtx,
+ Builder.makeZeroVal(Call.getResultType()));
C.addTransition(State);
}
return;
@@ -1333,37 +1506,39 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
// If the two arguments might be different buffers, we have to check
// the size of both of them.
assert(NotSameBuffer);
- State = CheckBufferAccess(C, State, Right, Size, AccessKind::read);
- State = CheckBufferAccess(C, State, Left, Size, AccessKind::read);
+ State = CheckBufferAccess(C, State, Right, Size, AccessKind::read, CK);
+ State = CheckBufferAccess(C, State, Left, Size, AccessKind::read, CK);
if (State) {
// The return value is the comparison result, which we don't know.
- SVal CmpV = Builder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
- State = State->BindExpr(CE, LCtx, CmpV);
+ SVal CmpV = Builder.conjureSymbolVal(nullptr, Call.getOriginExpr(), LCtx,
+ C.blockCount());
+ State = State->BindExpr(Call.getOriginExpr(), LCtx, CmpV);
C.addTransition(State);
}
}
}
void CStringChecker::evalstrLength(CheckerContext &C,
- const CallExpr *CE) const {
+ const CallEvent &Call) const {
// size_t strlen(const char *s);
- evalstrLengthCommon(C, CE, /* IsStrnlen = */ false);
+ evalstrLengthCommon(C, Call, /* IsStrnlen = */ false);
}
void CStringChecker::evalstrnLength(CheckerContext &C,
- const CallExpr *CE) const {
+ const CallEvent &Call) const {
// size_t strnlen(const char *s, size_t maxlen);
- evalstrLengthCommon(C, CE, /* IsStrnlen = */ true);
+ evalstrLengthCommon(C, Call, /* IsStrnlen = */ true);
}
-void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
+void CStringChecker::evalstrLengthCommon(CheckerContext &C,
+ const CallEvent &Call,
bool IsStrnlen) const {
CurrentFunctionDescription = "string length function";
ProgramStateRef state = C.getState();
const LocationContext *LCtx = C.getLocationContext();
if (IsStrnlen) {
- const Expr *maxlenExpr = CE->getArg(1);
+ const Expr *maxlenExpr = Call.getArgExpr(1);
SVal maxlenVal = state->getSVal(maxlenExpr, LCtx);
ProgramStateRef stateZeroSize, stateNonZeroSize;
@@ -1373,8 +1548,8 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
// If the size can be zero, the result will be 0 in that case, and we don't
// have to check the string itself.
if (stateZeroSize) {
- SVal zero = C.getSValBuilder().makeZeroVal(CE->getType());
- stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, zero);
+ SVal zero = C.getSValBuilder().makeZeroVal(Call.getResultType());
+ stateZeroSize = stateZeroSize->BindExpr(Call.getOriginExpr(), LCtx, zero);
C.addTransition(stateZeroSize);
}
@@ -1387,7 +1562,7 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
}
// Check that the string argument is non-null.
- AnyArgExpr Arg = {CE->getArg(0), 0};
+ AnyArgExpr Arg = {Call.getArgExpr(0), 0};
SVal ArgVal = state->getSVal(Arg.Expression, LCtx);
state = checkNonNull(C, state, Arg, ArgVal);
@@ -1410,11 +1585,11 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
// It's a little unfortunate to be getting this again,
// but it's not that expensive...
- const Expr *maxlenExpr = CE->getArg(1);
+ const Expr *maxlenExpr = Call.getArgExpr(1);
SVal maxlenVal = state->getSVal(maxlenExpr, LCtx);
- Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
- Optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>();
+ std::optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
+ std::optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>();
if (strLengthNL && maxlenValNL) {
ProgramStateRef stateStringTooLong, stateStringNotTooLong;
@@ -1439,8 +1614,8 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
// no guarantee the full string length will actually be returned.
// All we know is the return value is the min of the string length
// and the limit. This is better than nothing.
- result = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx,
- C.blockCount());
+ result = C.getSValBuilder().conjureSymbolVal(
+ nullptr, Call.getOriginExpr(), LCtx, C.blockCount());
NonLoc resultNL = result.castAs<NonLoc>();
if (strLengthNL) {
@@ -1463,78 +1638,85 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
// If we don't know the length of the string, conjure a return
// value, so it can be used in constraints, at least.
if (result.isUnknown()) {
- result = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx,
- C.blockCount());
+ result = C.getSValBuilder().conjureSymbolVal(
+ nullptr, Call.getOriginExpr(), LCtx, C.blockCount());
}
}
// Bind the return value.
assert(!result.isUnknown() && "Should have conjured a value by now");
- state = state->BindExpr(CE, LCtx, result);
+ state = state->BindExpr(Call.getOriginExpr(), LCtx, result);
C.addTransition(state);
}
-void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalStrcpy(CheckerContext &C,
+ const CallEvent &Call) const {
// char *strcpy(char *restrict dst, const char *restrict src);
- evalStrcpyCommon(C, CE,
+ evalStrcpyCommon(C, Call,
/* ReturnEnd = */ false,
/* IsBounded = */ false,
/* appendK = */ ConcatFnKind::none);
}
-void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalStrncpy(CheckerContext &C,
+ const CallEvent &Call) const {
// char *strncpy(char *restrict dst, const char *restrict src, size_t n);
- evalStrcpyCommon(C, CE,
+ evalStrcpyCommon(C, Call,
/* ReturnEnd = */ false,
/* IsBounded = */ true,
/* appendK = */ ConcatFnKind::none);
}
-void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalStpcpy(CheckerContext &C,
+ const CallEvent &Call) const {
// char *stpcpy(char *restrict dst, const char *restrict src);
- evalStrcpyCommon(C, CE,
+ evalStrcpyCommon(C, Call,
/* ReturnEnd = */ true,
/* IsBounded = */ false,
/* appendK = */ ConcatFnKind::none);
}
-void CStringChecker::evalStrlcpy(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalStrlcpy(CheckerContext &C,
+ const CallEvent &Call) const {
// size_t strlcpy(char *dest, const char *src, size_t size);
- evalStrcpyCommon(C, CE,
+ evalStrcpyCommon(C, Call,
/* ReturnEnd = */ true,
/* IsBounded = */ true,
/* appendK = */ ConcatFnKind::none,
/* returnPtr = */ false);
}
-void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalStrcat(CheckerContext &C,
+ const CallEvent &Call) const {
// char *strcat(char *restrict s1, const char *restrict s2);
- evalStrcpyCommon(C, CE,
+ evalStrcpyCommon(C, Call,
/* ReturnEnd = */ false,
/* IsBounded = */ false,
/* appendK = */ ConcatFnKind::strcat);
}
-void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const {
- //char *strncat(char *restrict s1, const char *restrict s2, size_t n);
- evalStrcpyCommon(C, CE,
+void CStringChecker::evalStrncat(CheckerContext &C,
+ const CallEvent &Call) const {
+ // char *strncat(char *restrict s1, const char *restrict s2, size_t n);
+ evalStrcpyCommon(C, Call,
/* ReturnEnd = */ false,
/* IsBounded = */ true,
/* appendK = */ ConcatFnKind::strcat);
}
-void CStringChecker::evalStrlcat(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalStrlcat(CheckerContext &C,
+ const CallEvent &Call) const {
// size_t strlcat(char *dst, const char *src, size_t size);
// It will append at most size - strlen(dst) - 1 bytes,
// NULL-terminating the result.
- evalStrcpyCommon(C, CE,
+ evalStrcpyCommon(C, Call,
/* ReturnEnd = */ false,
/* IsBounded = */ true,
/* appendK = */ ConcatFnKind::strlcat,
/* returnPtr = */ false);
}
-void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
+void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallEvent &Call,
bool ReturnEnd, bool IsBounded,
ConcatFnKind appendK,
bool returnPtr) const {
@@ -1547,14 +1729,14 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
const LocationContext *LCtx = C.getLocationContext();
// Check that the destination is non-null.
- DestinationArgExpr Dst = {CE->getArg(0), 0};
+ DestinationArgExpr Dst = {{Call.getArgExpr(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.
- SourceArgExpr srcExpr = {CE->getArg(1), 1};
+ SourceArgExpr srcExpr = {{Call.getArgExpr(1), 1}};
SVal srcVal = state->getSVal(srcExpr.Expression, LCtx);
state = checkNonNull(C, state, srcExpr, srcVal);
if (!state)
@@ -1562,11 +1744,11 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// Get the string length of the source.
SVal strLength = getCStringLength(C, state, srcExpr.Expression, srcVal);
- Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
+ std::optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
// Get the string length of the destination buffer.
SVal dstStrLength = getCStringLength(C, state, Dst.Expression, DstVal);
- Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>();
+ std::optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>();
// If the source isn't a valid C string, give up.
if (strLength.isUndef())
@@ -1585,11 +1767,12 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// 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};
+ SizeArgExpr SrcExprAsSizeDummy = {
+ {srcExpr.Expression, srcExpr.ArgumentIndex}};
state = CheckOverlap(
C, state,
- (IsBounded ? SizeArgExpr{CE->getArg(2), 2} : SrcExprAsSizeDummy), Dst,
- srcExpr);
+ (IsBounded ? SizeArgExpr{{Call.getArgExpr(2), 2}} : SrcExprAsSizeDummy),
+ Dst, srcExpr);
if (!state)
return;
@@ -1597,14 +1780,14 @@ 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.
- SizeArgExpr lenExpr = {CE->getArg(2), 2};
+ SizeArgExpr lenExpr = {{Call.getArgExpr(2), 2}};
SVal lenVal = state->getSVal(lenExpr.Expression, LCtx);
// Protect against misdeclared strncpy().
lenVal =
svalBuilder.evalCast(lenVal, sizeTy, lenExpr.Expression->getType());
- Optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>();
+ std::optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>();
// If we know both values, we might be able to figure out how much
// we're copying.
@@ -1641,12 +1824,12 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// amountCopied = min (size - dstLen - 1 , srcLen)
SVal freeSpace = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL,
*dstStrLengthNL, sizeTy);
- if (!freeSpace.getAs<NonLoc>())
+ if (!isa<NonLoc>(freeSpace))
return;
freeSpace =
svalBuilder.evalBinOp(state, BO_Sub, freeSpace,
svalBuilder.makeIntVal(1, sizeTy), sizeTy);
- Optional<NonLoc> freeSpaceNL = freeSpace.getAs<NonLoc>();
+ std::optional<NonLoc> freeSpaceNL = freeSpace.getAs<NonLoc>();
// While unlikely, it is possible that the subtraction is
// too complex to compute, let's check whether it succeeded.
@@ -1711,16 +1894,19 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// If the size is known to be zero, we're done.
if (StateZeroSize && !StateNonZeroSize) {
if (returnPtr) {
- StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, DstVal);
+ StateZeroSize =
+ StateZeroSize->BindExpr(Call.getOriginExpr(), LCtx, DstVal);
} else {
if (appendK == ConcatFnKind::none) {
// strlcpy returns strlen(src)
- StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, strLength);
+ StateZeroSize = StateZeroSize->BindExpr(Call.getOriginExpr(),
+ LCtx, strLength);
} else {
// strlcat returns strlen(src) + strlen(dst)
SVal retSize = svalBuilder.evalBinOp(
state, BO_Add, strLength, dstStrLength, sizeTy);
- StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, retSize);
+ StateZeroSize =
+ StateZeroSize->BindExpr(Call.getOriginExpr(), LCtx, retSize);
}
}
C.addTransition(StateZeroSize);
@@ -1771,7 +1957,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
*dstStrLengthNL, sizeTy);
}
- Optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>();
+ std::optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>();
// If we know both string lengths, we might know the final string length.
if (amountCopiedNL && dstStrLengthNL) {
@@ -1789,10 +1975,12 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
if (finalStrLength.isUnknown()) {
// Try to get a "hypothetical" string length symbol, which we can later
// set as a real value if that turns out to be the case.
- finalStrLength = getCStringLength(C, state, CE, DstVal, true);
+ finalStrLength =
+ getCStringLength(C, state, Call.getOriginExpr(), DstVal, true);
assert(!finalStrLength.isUndef());
- if (Optional<NonLoc> finalStrLengthNL = finalStrLength.getAs<NonLoc>()) {
+ if (std::optional<NonLoc> finalStrLengthNL =
+ finalStrLength.getAs<NonLoc>()) {
if (amountCopiedNL && appendK == ConcatFnKind::none) {
// we overwrite dst string with the src
// finalStrLength >= srcStrLength
@@ -1843,28 +2031,38 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// If the destination is a MemRegion, try to check for a buffer overflow and
// record the new string length.
- if (Optional<loc::MemRegionVal> dstRegVal =
- DstVal.getAs<loc::MemRegionVal>()) {
+ if (std::optional<loc::MemRegionVal> dstRegVal =
+ DstVal.getAs<loc::MemRegionVal>()) {
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 (Optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) {
+ if (std::optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) {
SVal maxLastElement =
svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *maxLastNL, ptrTy);
+ // Check if the first byte of the destination is writable.
+ state = CheckLocation(C, state, Dst, DstVal, AccessKind::write);
+ if (!state)
+ return;
+ // Check if the last byte of the destination is writable.
state = CheckLocation(C, state, Dst, maxLastElement, AccessKind::write);
if (!state)
return;
}
// Then, if the final length is known...
- if (Optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) {
+ if (std::optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) {
SVal lastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal,
*knownStrLength, ptrTy);
// ...and we haven't checked the bound, we'll check the actual copy.
if (!boundWarning) {
+ // Check if the first byte of the destination is writable.
+ state = CheckLocation(C, state, Dst, DstVal, AccessKind::write);
+ if (!state)
+ return;
+ // Check if the last byte of the destination is writable.
state = CheckLocation(C, state, Dst, lastElement, AccessKind::write);
if (!state)
return;
@@ -1882,13 +2080,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.Expression, *dstRegVal,
- /*IsSourceBuffer*/ false, nullptr);
+ state = invalidateDestinationBufferBySize(C, state, Dst.Expression,
+ *dstRegVal, amountCopied,
+ C.getASTContext().getSizeType());
// Invalidate the source (const-invalidation without const-pointer-escaping
// the address of the top-level region).
- state = InvalidateBuffer(C, state, srcExpr.Expression, srcVal,
- /*IsSourceBuffer*/ true, nullptr);
+ state = invalidateSourceBuffer(C, state, srcExpr.Expression, srcVal);
// Set the C string length of the destination, if we know it.
if (IsBounded && (appendK == ConcatFnKind::none)) {
@@ -1908,51 +2106,54 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// If this is a stpcpy-style copy, but we were unable to check for a buffer
// overflow, we still need a result. Conjure a return value.
if (ReturnEnd && Result.isUnknown()) {
- Result = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
+ Result = svalBuilder.conjureSymbolVal(nullptr, Call.getOriginExpr(), LCtx,
+ C.blockCount());
}
}
// Set the return value.
- state = state->BindExpr(CE, LCtx, Result);
+ state = state->BindExpr(Call.getOriginExpr(), LCtx, Result);
C.addTransition(state);
}
-void CStringChecker::evalStrcmp(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalStrcmp(CheckerContext &C,
+ const CallEvent &Call) const {
//int strcmp(const char *s1, const char *s2);
- evalStrcmpCommon(C, CE, /* IsBounded = */ false, /* IgnoreCase = */ false);
+ evalStrcmpCommon(C, Call, /* IsBounded = */ false, /* IgnoreCase = */ false);
}
-void CStringChecker::evalStrncmp(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalStrncmp(CheckerContext &C,
+ const CallEvent &Call) const {
//int strncmp(const char *s1, const char *s2, size_t n);
- evalStrcmpCommon(C, CE, /* IsBounded = */ true, /* IgnoreCase = */ false);
+ evalStrcmpCommon(C, Call, /* IsBounded = */ true, /* IgnoreCase = */ false);
}
void CStringChecker::evalStrcasecmp(CheckerContext &C,
- const CallExpr *CE) const {
+ const CallEvent &Call) const {
//int strcasecmp(const char *s1, const char *s2);
- evalStrcmpCommon(C, CE, /* IsBounded = */ false, /* IgnoreCase = */ true);
+ evalStrcmpCommon(C, Call, /* IsBounded = */ false, /* IgnoreCase = */ true);
}
void CStringChecker::evalStrncasecmp(CheckerContext &C,
- const CallExpr *CE) const {
+ const CallEvent &Call) const {
//int strncasecmp(const char *s1, const char *s2, size_t n);
- evalStrcmpCommon(C, CE, /* IsBounded = */ true, /* IgnoreCase = */ true);
+ evalStrcmpCommon(C, Call, /* IsBounded = */ true, /* IgnoreCase = */ true);
}
-void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
- bool IsBounded, bool IgnoreCase) const {
+void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallEvent &Call,
+ bool IsBounded, bool IgnoreCase) const {
CurrentFunctionDescription = "string comparison function";
ProgramStateRef state = C.getState();
const LocationContext *LCtx = C.getLocationContext();
// Check that the first string is non-null
- AnyArgExpr Left = {CE->getArg(0), 0};
+ AnyArgExpr Left = {Call.getArgExpr(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.
- AnyArgExpr Right = {CE->getArg(1), 1};
+ AnyArgExpr Right = {Call.getArgExpr(1), 1};
SVal RightVal = state->getSVal(Right.Expression, LCtx);
state = checkNonNull(C, state, Right, RightVal);
if (!state)
@@ -1983,8 +2184,9 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
// If the two arguments might be the same buffer, we know the result is 0,
// and we only need to check one size.
if (StSameBuf) {
- StSameBuf = StSameBuf->BindExpr(CE, LCtx,
- svalBuilder.makeZeroVal(CE->getType()));
+ StSameBuf =
+ StSameBuf->BindExpr(Call.getOriginExpr(), LCtx,
+ svalBuilder.makeZeroVal(Call.getResultType()));
C.addTransition(StSameBuf);
// If the two arguments are GUARANTEED to be the same, we're done!
@@ -2004,8 +2206,8 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
const StringLiteral *RightStrLiteral =
getCStringLiteral(C, state, Right.Expression, RightVal);
bool canComputeResult = false;
- SVal resultVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx,
- C.blockCount());
+ SVal resultVal = svalBuilder.conjureSymbolVal(nullptr, Call.getOriginExpr(),
+ LCtx, C.blockCount());
if (LeftStrLiteral && RightStrLiteral) {
StringRef LeftStrRef = LeftStrLiteral->getString();
@@ -2013,7 +2215,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
if (IsBounded) {
// Get the max number of characters to compare.
- const Expr *lenExpr = CE->getArg(2);
+ const Expr *lenExpr = Call.getArgExpr(2);
SVal lenVal = state->getSVal(lenExpr, LCtx);
// If the length is known, we can get the right substrings.
@@ -2045,13 +2247,13 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
// The strcmp function returns an integer greater than, equal to, or less
// than zero, [c11, p7.24.4.2].
if (compareRes == 0) {
- resultVal = svalBuilder.makeIntVal(compareRes, CE->getType());
+ resultVal = svalBuilder.makeIntVal(compareRes, Call.getResultType());
}
else {
- DefinedSVal zeroVal = svalBuilder.makeIntVal(0, CE->getType());
+ DefinedSVal zeroVal = svalBuilder.makeIntVal(0, Call.getResultType());
// Constrain strcmp's result range based on the result of StringRef's
// comparison methods.
- BinaryOperatorKind op = (compareRes == 1) ? BO_GT : BO_LT;
+ BinaryOperatorKind op = (compareRes > 0) ? BO_GT : BO_LT;
SVal compareWithZero =
svalBuilder.evalBinOp(state, op, resultVal, zeroVal,
svalBuilder.getConditionType());
@@ -2061,20 +2263,21 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
}
}
- state = state->BindExpr(CE, LCtx, resultVal);
+ state = state->BindExpr(Call.getOriginExpr(), LCtx, resultVal);
// Record this as a possible path.
C.addTransition(state);
}
-void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {
- //char *strsep(char **stringp, const char *delim);
- // Sanity: does the search string parameter match the return type?
- SourceArgExpr SearchStrPtr = {CE->getArg(0), 0};
+void CStringChecker::evalStrsep(CheckerContext &C,
+ const CallEvent &Call) const {
+ // char *strsep(char **stringp, const char *delim);
+ // Verify whether the search string parameter matches the return type.
+ SourceArgExpr SearchStrPtr = {{Call.getArgExpr(0), 0}};
QualType CharPtrTy = SearchStrPtr.Expression->getType()->getPointeeType();
- if (CharPtrTy.isNull() ||
- CE->getType().getUnqualifiedType() != CharPtrTy.getUnqualifiedType())
+ if (CharPtrTy.isNull() || Call.getResultType().getUnqualifiedType() !=
+ CharPtrTy.getUnqualifiedType())
return;
CurrentFunctionDescription = "strsep()";
@@ -2089,7 +2292,7 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {
return;
// Check that the delimiter string is non-null.
- AnyArgExpr DelimStr = {CE->getArg(1), 1};
+ AnyArgExpr DelimStr = {Call.getArgExpr(1), 1};
SVal DelimStrVal = State->getSVal(DelimStr.Expression, LCtx);
State = checkNonNull(C, State, DelimStr, DelimStrVal);
if (!State)
@@ -2097,48 +2300,49 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {
SValBuilder &SVB = C.getSValBuilder();
SVal Result;
- if (Optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) {
+ if (std::optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) {
// Get the current value of the search string pointer, as a char*.
Result = State->getSVal(*SearchStrLoc, CharPtrTy);
// Invalidate the search string, representing the change of one delimiter
// character to NUL.
- State = InvalidateBuffer(C, State, SearchStrPtr.Expression, Result,
- /*IsSourceBuffer*/ false, nullptr);
+ // As the replacement never overflows, do not invalidate its super region.
+ State = invalidateDestinationBufferNeverOverflows(
+ C, State, SearchStrPtr.Expression, Result);
// Overwrite the search string pointer. The new value is either an address
// further along in the same string, or NULL if there are no more tokens.
- State = State->bindLoc(*SearchStrLoc,
- SVB.conjureSymbolVal(getTag(),
- CE,
- LCtx,
- CharPtrTy,
- C.blockCount()),
- LCtx);
+ State =
+ State->bindLoc(*SearchStrLoc,
+ SVB.conjureSymbolVal(getTag(), Call.getOriginExpr(),
+ LCtx, CharPtrTy, C.blockCount()),
+ LCtx);
} else {
assert(SearchStrVal.isUnknown());
// Conjure a symbolic value. It's the best we can do.
- Result = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
+ Result = SVB.conjureSymbolVal(nullptr, Call.getOriginExpr(), LCtx,
+ C.blockCount());
}
// Set the return value, and finish.
- State = State->BindExpr(CE, LCtx, Result);
+ State = State->BindExpr(Call.getOriginExpr(), LCtx, Result);
C.addTransition(State);
}
// These should probably be moved into a C++ standard library checker.
-void CStringChecker::evalStdCopy(CheckerContext &C, const CallExpr *CE) const {
- evalStdCopyCommon(C, CE);
+void CStringChecker::evalStdCopy(CheckerContext &C,
+ const CallEvent &Call) const {
+ evalStdCopyCommon(C, Call);
}
void CStringChecker::evalStdCopyBackward(CheckerContext &C,
- const CallExpr *CE) const {
- evalStdCopyCommon(C, CE);
+ const CallEvent &Call) const {
+ evalStdCopyCommon(C, Call);
}
void CStringChecker::evalStdCopyCommon(CheckerContext &C,
- const CallExpr *CE) const {
- if (!CE->getArg(2)->getType()->isPointerType())
+ const CallEvent &Call) const {
+ if (!Call.getArgExpr(2)->getType()->isPointerType())
return;
ProgramStateRef State = C.getState();
@@ -2151,26 +2355,30 @@ void CStringChecker::evalStdCopyCommon(CheckerContext &C,
// _OutputIterator __result)
// Invalidate the destination buffer
- const Expr *Dst = CE->getArg(2);
+ const Expr *Dst = Call.getArgExpr(2);
SVal DstVal = State->getSVal(Dst, LCtx);
- State = InvalidateBuffer(C, State, Dst, DstVal, /*IsSource=*/false,
- /*Size=*/nullptr);
+ // FIXME: As we do not know how many items are copied, we also invalidate the
+ // super region containing the target location.
+ State =
+ invalidateDestinationBufferAlwaysEscapeSuperRegion(C, State, Dst, DstVal);
SValBuilder &SVB = C.getSValBuilder();
- SVal ResultVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
- State = State->BindExpr(CE, LCtx, ResultVal);
+ SVal ResultVal =
+ SVB.conjureSymbolVal(nullptr, Call.getOriginExpr(), LCtx, C.blockCount());
+ State = State->BindExpr(Call.getOriginExpr(), LCtx, ResultVal);
C.addTransition(State);
}
-void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalMemset(CheckerContext &C,
+ const CallEvent &Call) const {
// void *memset(void *s, int c, size_t n);
CurrentFunctionDescription = "memory set function";
- DestinationArgExpr Buffer = {CE->getArg(0), 0};
- AnyArgExpr CharE = {CE->getArg(1), 1};
- SizeArgExpr Size = {CE->getArg(2), 2};
+ DestinationArgExpr Buffer = {{Call.getArgExpr(0), 0}};
+ AnyArgExpr CharE = {Call.getArgExpr(1), 1};
+ SizeArgExpr Size = {{Call.getArgExpr(2), 2}};
ProgramStateRef State = C.getState();
@@ -2188,7 +2396,7 @@ void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const {
// If the size is zero, there won't be any actual memory access, so
// just bind the return value to the buffer and return.
if (ZeroSize && !NonZeroSize) {
- ZeroSize = ZeroSize->BindExpr(CE, LCtx, BufferPtrVal);
+ ZeroSize = ZeroSize->BindExpr(Call.getOriginExpr(), LCtx, BufferPtrVal);
C.addTransition(ZeroSize);
return;
}
@@ -2210,15 +2418,15 @@ void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const {
Size.Expression, C, State))
return;
- State = State->BindExpr(CE, LCtx, BufferPtrVal);
+ State = State->BindExpr(Call.getOriginExpr(), LCtx, BufferPtrVal);
C.addTransition(State);
}
-void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalBzero(CheckerContext &C, const CallEvent &Call) const {
CurrentFunctionDescription = "memory clearance function";
- DestinationArgExpr Buffer = {CE->getArg(0), 0};
- SizeArgExpr Size = {CE->getArg(1), 1};
+ DestinationArgExpr Buffer = {{Call.getArgExpr(0), 0}};
+ SizeArgExpr Size = {{Call.getArgExpr(1), 1}};
SVal Zero = C.getSValBuilder().makeZeroVal(C.getASTContext().IntTy);
ProgramStateRef State = C.getState();
@@ -2257,6 +2465,57 @@ void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const {
C.addTransition(State);
}
+void CStringChecker::evalSprintf(CheckerContext &C,
+ const CallEvent &Call) const {
+ CurrentFunctionDescription = "'sprintf'";
+ const auto *CE = cast<CallExpr>(Call.getOriginExpr());
+ bool IsBI = CE->getBuiltinCallee() == Builtin::BI__builtin___sprintf_chk;
+ evalSprintfCommon(C, Call, /* IsBounded */ false, IsBI);
+}
+
+void CStringChecker::evalSnprintf(CheckerContext &C,
+ const CallEvent &Call) const {
+ CurrentFunctionDescription = "'snprintf'";
+ const auto *CE = cast<CallExpr>(Call.getOriginExpr());
+ bool IsBI = CE->getBuiltinCallee() == Builtin::BI__builtin___snprintf_chk;
+ evalSprintfCommon(C, Call, /* IsBounded */ true, IsBI);
+}
+
+void CStringChecker::evalSprintfCommon(CheckerContext &C, const CallEvent &Call,
+ bool IsBounded, bool IsBuiltin) const {
+ ProgramStateRef State = C.getState();
+ const auto *CE = cast<CallExpr>(Call.getOriginExpr());
+ DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}};
+
+ const auto NumParams = Call.parameters().size();
+ assert(CE->getNumArgs() >= NumParams);
+
+ const auto AllArguments =
+ llvm::make_range(CE->getArgs(), CE->getArgs() + CE->getNumArgs());
+ const auto VariadicArguments = drop_begin(enumerate(AllArguments), NumParams);
+
+ for (const auto &[ArgIdx, ArgExpr] : VariadicArguments) {
+ // We consider only string buffers
+ if (const QualType type = ArgExpr->getType();
+ !type->isAnyPointerType() ||
+ !type->getPointeeType()->isAnyCharacterType())
+ continue;
+ SourceArgExpr Source = {{ArgExpr, unsigned(ArgIdx)}};
+
+ // Ensure the buffers do not overlap.
+ SizeArgExpr SrcExprAsSizeDummy = {
+ {Source.Expression, Source.ArgumentIndex}};
+ State = CheckOverlap(
+ C, State,
+ (IsBounded ? SizeArgExpr{{Call.getArgExpr(1), 1}} : SrcExprAsSizeDummy),
+ Dest, Source);
+ if (!State)
+ return;
+ }
+
+ C.addTransition(State);
+}
+
//===----------------------------------------------------------------------===//
// The driver method, and other Checker callbacks.
//===----------------------------------------------------------------------===//
@@ -2271,11 +2530,10 @@ CStringChecker::FnCheck CStringChecker::identifyCall(const CallEvent &Call,
if (!FD)
return nullptr;
- if (Call.isCalled(StdCopy)) {
+ if (StdCopy.matches(Call))
return &CStringChecker::evalStdCopy;
- } else if (Call.isCalled(StdCopyBackward)) {
+ if (StdCopyBackward.matches(Call))
return &CStringChecker::evalStdCopyBackward;
- }
// Pro-actively check that argument types are safe to do arithmetic upon.
// We do not want to crash if someone accidentally passes a structure
@@ -2302,8 +2560,8 @@ bool CStringChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
return false;
// Check and evaluate the call.
- const auto *CE = cast<CallExpr>(Call.getOriginExpr());
- (this->*Callback)(C, CE);
+ assert(isa<CallExpr>(Call.getOriginExpr()));
+ Callback(this, C, Call);
// If the evaluate call resulted in no change, chain to the next eval call
// handler.
@@ -2364,9 +2622,7 @@ CStringChecker::checkRegionChanges(ProgramStateRef state,
llvm::SmallPtrSet<const MemRegion *, 32> SuperRegions;
// First build sets for the changed regions and their super-regions.
- for (ArrayRef<const MemRegion *>::iterator
- I = Regions.begin(), E = Regions.end(); I != E; ++I) {
- const MemRegion *MR = *I;
+ for (const MemRegion *MR : Regions) {
Invalidated.insert(MR);
SuperRegions.insert(MR);
@@ -2379,10 +2635,7 @@ CStringChecker::checkRegionChanges(ProgramStateRef state,
CStringLengthTy::Factory &F = state->get_context<CStringLength>();
// Then loop over the entries in the current state.
- for (CStringLengthTy::iterator I = Entries.begin(),
- E = Entries.end(); I != E; ++I) {
- const MemRegion *MR = I.getKey();
-
+ for (const MemRegion *MR : llvm::make_first_range(Entries)) {
// Is this entry for a super-region of a changed region?
if (SuperRegions.count(MR)) {
Entries = F.remove(Entries, MR);
@@ -2408,13 +2661,9 @@ void CStringChecker::checkLiveSymbols(ProgramStateRef state,
// Mark all symbols in our string length map as valid.
CStringLengthTy Entries = state->get<CStringLength>();
- for (CStringLengthTy::iterator I = Entries.begin(), E = Entries.end();
- I != E; ++I) {
- SVal Len = I.getData();
-
- for (SymExpr::symbol_iterator si = Len.symbol_begin(),
- se = Len.symbol_end(); si != se; ++si)
- SR.markInUse(*si);
+ for (SVal Len : llvm::make_second_range(Entries)) {
+ for (SymbolRef Sym : Len.symbols())
+ SR.markInUse(Sym);
}
}
@@ -2426,12 +2675,10 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR,
return;
CStringLengthTy::Factory &F = state->get_context<CStringLength>();
- for (CStringLengthTy::iterator I = Entries.begin(), E = Entries.end();
- I != E; ++I) {
- SVal Len = I.getData();
+ for (auto [Reg, Len] : Entries) {
if (SymbolRef Sym = Len.getAsSymbol()) {
if (SR.isDead(Sym))
- Entries = F.remove(Entries, I.getKey());
+ Entries = F.remove(Entries, Reg);
}
}
@@ -2460,3 +2707,4 @@ REGISTER_CHECKER(CStringNullArg)
REGISTER_CHECKER(CStringOutOfBounds)
REGISTER_CHECKER(CStringBufferOverlap)
REGISTER_CHECKER(CStringNotNullTerm)
+REGISTER_CHECKER(CStringUninitializedRead)
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp
new file mode 100644
index 000000000000..b4dee1e300e8
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp
@@ -0,0 +1,238 @@
+//=== CXXDeleteChecker.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 the following new checkers for C++ delete expressions:
+//
+// * DeleteWithNonVirtualDtorChecker
+// Defines a checker for the OOP52-CPP CERT rule: Do not delete a
+// polymorphic object without a virtual destructor.
+//
+// Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor
+// report if an object with a virtual function but a non-virtual
+// destructor exists or is deleted, respectively.
+//
+// This check exceeds them by comparing the dynamic and static types of
+// the object at the point of destruction and only warns if it happens
+// through a pointer to a base type without a virtual destructor. The
+// check places a note at the last point where the conversion from
+// derived to base happened.
+//
+// * CXXArrayDeleteChecker
+// Defines a checker for the EXP51-CPP CERT rule: Do not delete an array
+// through a pointer of the incorrect type.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class CXXDeleteChecker : public Checker<check::PreStmt<CXXDeleteExpr>> {
+protected:
+ class PtrCastVisitor : public BugReporterVisitor {
+ public:
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int X = 0;
+ ID.AddPointer(&X);
+ }
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
+ };
+
+ virtual void
+ checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C,
+ const TypedValueRegion *BaseClassRegion,
+ const SymbolicRegion *DerivedClassRegion) const = 0;
+
+public:
+ void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
+};
+
+class DeleteWithNonVirtualDtorChecker : public CXXDeleteChecker {
+ const BugType BT{
+ this, "Destruction of a polymorphic object with no virtual destructor"};
+
+ void
+ checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C,
+ const TypedValueRegion *BaseClassRegion,
+ const SymbolicRegion *DerivedClassRegion) const override;
+};
+
+class CXXArrayDeleteChecker : public CXXDeleteChecker {
+ const BugType BT{this,
+ "Deleting an array of polymorphic objects is undefined"};
+
+ void
+ checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C,
+ const TypedValueRegion *BaseClassRegion,
+ const SymbolicRegion *DerivedClassRegion) const override;
+};
+} // namespace
+
+void CXXDeleteChecker::checkPreStmt(const CXXDeleteExpr *DE,
+ CheckerContext &C) const {
+ const Expr *DeletedObj = DE->getArgument();
+ const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion();
+ if (!MR)
+ return;
+
+ OverloadedOperatorKind DeleteKind =
+ DE->getOperatorDelete()->getOverloadedOperator();
+
+ if (DeleteKind != OO_Delete && DeleteKind != OO_Array_Delete)
+ return;
+
+ const auto *BaseClassRegion = MR->getAs<TypedValueRegion>();
+ const auto *DerivedClassRegion = MR->getBaseRegion()->getAs<SymbolicRegion>();
+ if (!BaseClassRegion || !DerivedClassRegion)
+ return;
+
+ checkTypedDeleteExpr(DE, C, BaseClassRegion, DerivedClassRegion);
+}
+
+void DeleteWithNonVirtualDtorChecker::checkTypedDeleteExpr(
+ const CXXDeleteExpr *DE, CheckerContext &C,
+ const TypedValueRegion *BaseClassRegion,
+ const SymbolicRegion *DerivedClassRegion) const {
+ const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl();
+ const auto *DerivedClass =
+ DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl();
+ if (!BaseClass || !DerivedClass)
+ return;
+
+ if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition())
+ return;
+
+ if (BaseClass->getDestructor()->isVirtual())
+ return;
+
+ if (!DerivedClass->isDerivedFrom(BaseClass))
+ return;
+
+ 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.
+ R->markInteresting(BaseClassRegion);
+ R->addVisitor<PtrCastVisitor>();
+ C.emitReport(std::move(R));
+}
+
+void CXXArrayDeleteChecker::checkTypedDeleteExpr(
+ const CXXDeleteExpr *DE, CheckerContext &C,
+ const TypedValueRegion *BaseClassRegion,
+ const SymbolicRegion *DerivedClassRegion) const {
+ const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl();
+ const auto *DerivedClass =
+ DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl();
+ if (!BaseClass || !DerivedClass)
+ return;
+
+ if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition())
+ return;
+
+ if (DE->getOperatorDelete()->getOverloadedOperator() != OO_Array_Delete)
+ return;
+
+ if (!DerivedClass->isDerivedFrom(BaseClass))
+ return;
+
+ ExplodedNode *N = C.generateNonFatalErrorNode();
+ if (!N)
+ return;
+
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream OS(Buf);
+
+ QualType SourceType = BaseClassRegion->getValueType();
+ QualType TargetType =
+ DerivedClassRegion->getSymbol()->getType()->getPointeeType();
+
+ OS << "Deleting an array of '" << TargetType.getAsString()
+ << "' objects as their base class '"
+ << SourceType.getAsString(C.getASTContext().getPrintingPolicy())
+ << "' is undefined";
+
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N);
+
+ // Mark region of problematic base class for later use in the BugVisitor.
+ R->markInteresting(BaseClassRegion);
+ R->addVisitor<PtrCastVisitor>();
+ C.emitReport(std::move(R));
+}
+
+PathDiagnosticPieceRef
+CXXDeleteChecker::PtrCastVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
+ const Stmt *S = N->getStmtForDiagnostics();
+ if (!S)
+ return nullptr;
+
+ const auto *CastE = dyn_cast<CastExpr>(S);
+ if (!CastE)
+ return nullptr;
+
+ // FIXME: This way of getting base types does not support reference types.
+ QualType SourceType = CastE->getSubExpr()->getType()->getPointeeType();
+ QualType TargetType = CastE->getType()->getPointeeType();
+
+ if (SourceType.isNull() || TargetType.isNull() || SourceType == TargetType)
+ return nullptr;
+
+ // Region associated with the current cast expression.
+ const MemRegion *M = N->getSVal(CastE).getAsRegion();
+ if (!M)
+ return nullptr;
+
+ // Check if target region was marked as problematic previously.
+ if (!BR.isInteresting(M))
+ return nullptr;
+
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream OS(Buf);
+
+ OS << "Casting from '" << SourceType.getAsString() << "' to '"
+ << TargetType.getAsString() << "' here";
+
+ PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+ N->getLocationContext());
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(),
+ /*addPosRange=*/true);
+}
+
+void ento::registerCXXArrayDeleteChecker(CheckerManager &mgr) {
+ mgr.registerChecker<CXXArrayDeleteChecker>();
+}
+
+bool ento::shouldRegisterCXXArrayDeleteChecker(const CheckerManager &mgr) {
+ return true;
+}
+
+void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) {
+ mgr.registerChecker<DeleteWithNonVirtualDtorChecker>();
+}
+
+bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker(
+ 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 3e46e2372516..f2e1f69c32cf 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -73,7 +73,7 @@ public:
CK_NumCheckKinds
};
- DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ bool ChecksEnabled[CK_NumCheckKinds] = {false};
// The original core.CallAndMessage checker name. This should rather be an
// array, as seen in MallocChecker and CStringChecker.
CheckerNameRef OriginalName;
@@ -123,11 +123,10 @@ private:
void LazyInit_BT(const char *desc, std::unique_ptr<BugType> &BT) const {
if (!BT)
- BT.reset(new BuiltinBug(OriginalName, desc));
+ BT.reset(new BugType(OriginalName, desc));
}
- bool uninitRefOrPointer(CheckerContext &C, const SVal &V,
- SourceRange ArgRange, const Expr *ArgEx,
- std::unique_ptr<BugType> &BT,
+ bool uninitRefOrPointer(CheckerContext &C, SVal V, SourceRange ArgRange,
+ const Expr *ArgEx, std::unique_ptr<BugType> &BT,
const ParmVarDecl *ParamDecl, const char *BD,
int ArgumentNumber) const;
};
@@ -185,7 +184,7 @@ static void describeUninitializedArgumentInCall(const CallEvent &Call,
}
bool CallAndMessageChecker::uninitRefOrPointer(
- CheckerContext &C, const SVal &V, SourceRange ArgRange, const Expr *ArgEx,
+ CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx,
std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, const char *BD,
int ArgumentNumber) const {
@@ -263,7 +262,7 @@ public:
if (Find(FR))
return true;
} else {
- const SVal &V = StoreMgr.getBinding(store, loc::MemRegionVal(FR));
+ SVal V = StoreMgr.getBinding(store, loc::MemRegionVal(FR));
if (V.isUndef())
return true;
}
@@ -379,7 +378,7 @@ ProgramStateRef CallAndMessageChecker::checkFunctionPointerCall(
return nullptr;
}
if (!BT_call_undef)
- BT_call_undef.reset(new BuiltinBug(
+ BT_call_undef.reset(new BugType(
OriginalName,
"Called function pointer is an uninitialized pointer value"));
emitBadCall(BT_call_undef.get(), C, Callee);
@@ -395,7 +394,7 @@ ProgramStateRef CallAndMessageChecker::checkFunctionPointerCall(
return nullptr;
}
if (!BT_call_null)
- BT_call_null.reset(new BuiltinBug(
+ BT_call_null.reset(new BugType(
OriginalName, "Called function pointer is null (null dereference)"));
emitBadCall(BT_call_null.get(), C, Callee);
return nullptr;
@@ -450,7 +449,7 @@ ProgramStateRef CallAndMessageChecker::checkCXXMethodCall(
return nullptr;
}
if (!BT_cxx_call_undef)
- BT_cxx_call_undef.reset(new BuiltinBug(
+ BT_cxx_call_undef.reset(new BugType(
OriginalName, "Called C++ object pointer is uninitialized"));
emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr());
return nullptr;
@@ -466,7 +465,7 @@ ProgramStateRef CallAndMessageChecker::checkCXXMethodCall(
}
if (!BT_cxx_call_null)
BT_cxx_call_null.reset(
- new BuiltinBug(OriginalName, "Called C++ object pointer is null"));
+ new BugType(OriginalName, "Called C++ object pointer is null"));
emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr());
return nullptr;
}
@@ -495,13 +494,13 @@ CallAndMessageChecker::checkCXXDeallocation(const CXXDeallocatorCall *DC,
return nullptr;
if (!BT_cxx_delete_undef)
BT_cxx_delete_undef.reset(
- new BuiltinBug(OriginalName, "Uninitialized argument value"));
+ new BugType(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);
+ auto R =
+ std::make_unique<PathSensitiveBugReport>(*BT_cxx_delete_undef, Desc, N);
bugreporter::trackExpressionValue(N, DE, *R);
C.emitReport(std::move(R));
return nullptr;
@@ -585,21 +584,21 @@ void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
switch (msg.getMessageKind()) {
case OCM_Message:
if (!BT_msg_undef)
- BT_msg_undef.reset(new BuiltinBug(OriginalName,
- "Receiver in message expression "
- "is an uninitialized value"));
+ BT_msg_undef.reset(new BugType(OriginalName,
+ "Receiver in message expression "
+ "is an uninitialized value"));
BT = BT_msg_undef.get();
break;
case OCM_PropertyAccess:
if (!BT_objc_prop_undef)
- BT_objc_prop_undef.reset(new BuiltinBug(
+ BT_objc_prop_undef.reset(new BugType(
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(
+ BT_objc_subscript_undef.reset(new BugType(
OriginalName,
"Subscript access on an uninitialized object pointer"));
BT = BT_objc_subscript_undef.get();
@@ -634,8 +633,8 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
}
if (!BT_msg_ret)
- BT_msg_ret.reset(new BuiltinBug(OriginalName,
- "Receiver in message expression is 'nil'"));
+ BT_msg_ret.reset(
+ new BugType(OriginalName, "Receiver in message expression is 'nil'"));
const ObjCMessageExpr *ME = msg.getOriginExpr();
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
index 2d2e14de3f2b..a50772f881f7 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
@@ -24,7 +24,7 @@ using namespace ento;
namespace {
class CastSizeChecker : public Checker< check::PreStmt<CastExpr> > {
- mutable std::unique_ptr<BuiltinBug> BT;
+ const BugType BT{this, "Cast region with wrong size."};
public:
void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
@@ -131,12 +131,10 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
return;
if (ExplodedNode *errorNode = C.generateErrorNode()) {
- if (!BT)
- BT.reset(new BuiltinBug(this, "Cast region with wrong size.",
- "Cast a region whose size is not a multiple"
- " of the destination type size."));
- auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(),
- errorNode);
+ constexpr llvm::StringLiteral Msg =
+ "Cast a region whose size is not a multiple of the destination type "
+ "size.";
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, errorNode);
R->addRange(CE->getSourceRange());
C.emitReport(std::move(R));
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
index 131c1345af99..f02d20d45678 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
@@ -20,10 +20,11 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
-#include "llvm/ADT/Optional.h"
+#include <optional>
#include <utility>
using namespace clang;
@@ -107,7 +108,7 @@ static const NoteTag *getNoteTag(CheckerContext &C,
bool CastSucceeds, bool IsKnownCast) {
std::string CastToName =
CastInfo ? CastInfo->to()->getAsCXXRecordDecl()->getNameAsString()
- : CastToTy->getPointeeCXXRecordDecl()->getNameAsString();
+ : CastToTy.getAsString();
Object = Object->IgnoreParenImpCasts();
return C.getNoteTag(
@@ -162,9 +163,9 @@ static const NoteTag *getNoteTag(CheckerContext &C,
bool First = true;
for (QualType CastToTy: CastToTyVec) {
std::string CastToName =
- CastToTy->getAsCXXRecordDecl() ?
- CastToTy->getAsCXXRecordDecl()->getNameAsString() :
- CastToTy->getPointeeCXXRecordDecl()->getNameAsString();
+ CastToTy->getAsCXXRecordDecl()
+ ? CastToTy->getAsCXXRecordDecl()->getNameAsString()
+ : CastToTy.getAsString();
Out << ' ' << ((CastToTyVec.size() == 1) ? "not" :
(First ? "neither" : "nor")) << " a '" << CastToName
<< '\'';
@@ -249,7 +250,7 @@ static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV,
CastSucceeds);
SVal V = CastSucceeds ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy)
- : C.getSValBuilder().makeNull();
+ : C.getSValBuilder().makeNullWithType(CastToTy);
C.addTransition(
State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false),
getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast));
@@ -358,7 +359,9 @@ static void evalNullParamNullReturn(const CallEvent &Call,
if (ProgramStateRef State = C.getState()->assume(DV, false))
C.addTransition(State->BindExpr(Call.getOriginExpr(),
C.getLocationContext(),
- C.getSValBuilder().makeNull(), false),
+ C.getSValBuilder().makeNullWithType(
+ Call.getOriginExpr()->getType()),
+ false),
C.getNoteTag("Assuming null pointer is passed into cast",
/*IsPrunable=*/true));
}
@@ -469,7 +472,7 @@ bool CastValueChecker::evalCall(const CallEvent &Call,
const CastCheck &Check = Lookup->first;
CallKind Kind = Lookup->second;
- Optional<DefinedOrUnknownSVal> DV;
+ std::optional<DefinedOrUnknownSVal> DV;
switch (Kind) {
case CallKind::Function: {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
index 78b3c209ad6b..978bc0bb082f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
@@ -45,6 +45,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -98,18 +99,23 @@ class ObjCDeallocChecker
check::PointerEscape,
check::PreStmt<ReturnStmt>> {
- mutable IdentifierInfo *NSObjectII, *SenTestCaseII, *XCTestCaseII,
- *Block_releaseII, *CIFilterII;
+ mutable const IdentifierInfo *NSObjectII = nullptr;
+ mutable const IdentifierInfo *SenTestCaseII = nullptr;
+ mutable const IdentifierInfo *XCTestCaseII = nullptr;
+ mutable const IdentifierInfo *Block_releaseII = nullptr;
+ mutable const IdentifierInfo *CIFilterII = nullptr;
- mutable Selector DeallocSel, ReleaseSel;
+ mutable Selector DeallocSel;
+ mutable Selector ReleaseSel;
- std::unique_ptr<BugType> MissingReleaseBugType;
- std::unique_ptr<BugType> ExtraReleaseBugType;
- std::unique_ptr<BugType> MistakenDeallocBugType;
+ const BugType MissingReleaseBugType{this, "Missing ivar release (leak)",
+ categories::MemoryRefCount};
+ const BugType ExtraReleaseBugType{this, "Extra ivar release",
+ categories::MemoryRefCount};
+ const BugType MistakenDeallocBugType{this, "Mistaken dealloc",
+ categories::MemoryRefCount};
public:
- ObjCDeallocChecker();
-
void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
BugReporter &BR) const;
void checkBeginFunction(CheckerContext &Ctx) const;
@@ -282,11 +288,11 @@ void ObjCDeallocChecker::checkBeginFunction(
continue;
SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal);
- Optional<Loc> LValLoc = LVal.getAs<Loc>();
+ std::optional<Loc> LValLoc = LVal.getAs<Loc>();
if (!LValLoc)
continue;
- SVal InitialVal = State->getSVal(LValLoc.getValue());
+ SVal InitialVal = State->getSVal(*LValLoc);
SymbolRef Symbol = InitialVal.getAsSymbol();
if (!Symbol || !isa<SymbolRegionValue>(Symbol))
continue;
@@ -320,7 +326,9 @@ ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const {
if (!IvarRegion)
return nullptr;
- return IvarRegion->getSymbolicBase()->getSymbol();
+ const SymbolicRegion *SR = IvarRegion->getSymbolicBase();
+ assert(SR && "Symbolic base should not be nullptr");
+ return SR->getSymbol();
}
/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is
@@ -576,7 +584,7 @@ void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const {
OS << " by a synthesized property but not released"
" before '[super dealloc]'";
- auto BR = std::make_unique<PathSensitiveBugReport>(*MissingReleaseBugType,
+ auto BR = std::make_unique<PathSensitiveBugReport>(MissingReleaseBugType,
OS.str(), ErrNode);
C.emitReport(std::move(BR));
}
@@ -698,7 +706,7 @@ bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue,
OS << " property but was released in 'dealloc'";
}
- auto BR = std::make_unique<PathSensitiveBugReport>(*ExtraReleaseBugType,
+ auto BR = std::make_unique<PathSensitiveBugReport>(ExtraReleaseBugType,
OS.str(), ErrNode);
BR->addRange(M.getOriginExpr()->getSourceRange());
@@ -740,7 +748,7 @@ bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue,
OS << "'" << *PropImpl->getPropertyIvarDecl()
<< "' should be released rather than deallocated";
- auto BR = std::make_unique<PathSensitiveBugReport>(*MistakenDeallocBugType,
+ auto BR = std::make_unique<PathSensitiveBugReport>(MistakenDeallocBugType,
OS.str(), ErrNode);
BR->addRange(M.getOriginExpr()->getSourceRange());
@@ -749,23 +757,6 @@ bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue,
return true;
}
-ObjCDeallocChecker::ObjCDeallocChecker()
- : NSObjectII(nullptr), SenTestCaseII(nullptr), XCTestCaseII(nullptr),
- CIFilterII(nullptr) {
-
- MissingReleaseBugType.reset(
- new BugType(this, "Missing ivar release (leak)",
- categories::MemoryRefCount));
-
- ExtraReleaseBugType.reset(
- new BugType(this, "Extra ivar release",
- categories::MemoryRefCount));
-
- MistakenDeallocBugType.reset(
- new BugType(this, "Mistaken dealloc",
- categories::MemoryRefCount));
-}
-
void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
ASTContext &Ctx) const {
if (NSObjectII)
@@ -817,8 +808,8 @@ const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl(
IdentifierInfo *ID = PropDecl->getIdentifier();
DeclContext::lookup_result R = CatDecl->getClassInterface()->lookup(ID);
- for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) {
- auto *ShadowedPropDecl = dyn_cast<ObjCPropertyDecl>(*I);
+ for (const NamedDecl *D : R) {
+ auto *ShadowedPropDecl = dyn_cast<ObjCPropertyDecl>(D);
if (!ShadowedPropDecl)
continue;
@@ -953,11 +944,11 @@ ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M,
ProgramStateRef State = C.getState();
SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
- Optional<Loc> LValLoc = LVal.getAs<Loc>();
+ std::optional<Loc> LValLoc = LVal.getAs<Loc>();
if (!LValLoc)
return nullptr;
- SVal CurrentValInIvar = State->getSVal(LValLoc.getValue());
+ SVal CurrentValInIvar = State->getSVal(*LValLoc);
return CurrentValInIvar.getAsSymbol();
}
@@ -1004,7 +995,7 @@ bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C,
return false;
}
-/// Returns true if the ID is a class in which which is known to have
+/// Returns true if the ID is a class in which is known to have
/// a separate teardown lifecycle. In this case, -dealloc warnings
/// about missing releases should be suppressed.
bool ObjCDeallocChecker::classHasSeparateTeardown(
@@ -1042,8 +1033,8 @@ bool ObjCDeallocChecker::isReleasedByCIFilterDealloc(
StringRef IvarName = PropImpl->getPropertyIvarDecl()->getName();
const char *ReleasePrefix = "input";
- if (!(PropName.startswith(ReleasePrefix) ||
- IvarName.startswith(ReleasePrefix))) {
+ if (!(PropName.starts_with(ReleasePrefix) ||
+ IvarName.starts_with(ReleasePrefix))) {
return false;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
index 175dfcef0df4..c8fe5c2ccf38 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
-// This file defines a CheckObjCInstMethSignature, a flow-insenstive check
+// This file defines a CheckObjCInstMethSignature, a flow-insensitive check
// that determines if an Objective-C class interface incorrectly redefines
// the method signature in a subclass.
//
@@ -55,13 +55,11 @@ static void CompareReturnTypes(const ObjCMethodDecl *MethDerived,
<< *MethAncestor->getClassInterface()
<< "', defines the instance method '";
MethDerived->getSelector().print(os);
- os << "' whose return type is '"
- << ResDerived.getAsString()
+ os << "' whose return type is '" << ResDerived
<< "'. A method with the same name (same selector) is also defined in "
"class '"
- << *MethAncestor->getClassInterface()
- << "' and has a return type of '"
- << ResAncestor.getAsString()
+ << *MethAncestor->getClassInterface() << "' and has a return type of '"
+ << ResAncestor
<< "'. These two types are incompatible, and may result in undefined "
"behavior for clients of these classes.";
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
index d06c87631bfb..17af1aebd6d2 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
@@ -27,7 +27,6 @@ using namespace ento;
static bool isArc4RandomAvailable(const ASTContext &Ctx) {
const llvm::Triple &T = Ctx.getTargetInfo().getTriple();
return T.getVendor() == llvm::Triple::Apple ||
- T.getOS() == llvm::Triple::CloudABI ||
T.isOSFreeBSD() ||
T.isOSNetBSD() ||
T.isOSOpenBSD() ||
@@ -36,20 +35,20 @@ static bool isArc4RandomAvailable(const ASTContext &Ctx) {
namespace {
struct ChecksFilter {
- DefaultBool check_bcmp;
- DefaultBool check_bcopy;
- DefaultBool check_bzero;
- DefaultBool check_gets;
- DefaultBool check_getpw;
- DefaultBool check_mktemp;
- DefaultBool check_mkstemp;
- DefaultBool check_strcpy;
- DefaultBool check_DeprecatedOrUnsafeBufferHandling;
- DefaultBool check_rand;
- DefaultBool check_vfork;
- DefaultBool check_FloatLoopCounter;
- DefaultBool check_UncheckedReturn;
- DefaultBool check_decodeValueOfObjCType;
+ bool check_bcmp = false;
+ bool check_bcopy = false;
+ bool check_bzero = false;
+ bool check_gets = false;
+ bool check_getpw = false;
+ bool check_mktemp = false;
+ bool check_mkstemp = false;
+ bool check_strcpy = false;
+ bool check_DeprecatedOrUnsafeBufferHandling = false;
+ bool check_rand = false;
+ bool check_vfork = false;
+ bool check_FloatLoopCounter = false;
+ bool check_UncheckedReturn = false;
+ bool check_decodeValueOfObjCType = false;
CheckerNameRef checkName_bcmp;
CheckerNameRef checkName_bcopy;
@@ -141,42 +140,42 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {
if (!II) // if no identifier, not a simple C function
return;
StringRef Name = II->getName();
- if (Name.startswith("__builtin_"))
- Name = Name.substr(10);
+ Name.consume_front("__builtin_");
// Set the evaluation function by switching on the callee name.
- FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
- .Case("bcmp", &WalkAST::checkCall_bcmp)
- .Case("bcopy", &WalkAST::checkCall_bcopy)
- .Case("bzero", &WalkAST::checkCall_bzero)
- .Case("gets", &WalkAST::checkCall_gets)
- .Case("getpw", &WalkAST::checkCall_getpw)
- .Case("mktemp", &WalkAST::checkCall_mktemp)
- .Case("mkstemp", &WalkAST::checkCall_mkstemp)
- .Case("mkdtemp", &WalkAST::checkCall_mkstemp)
- .Case("mkstemps", &WalkAST::checkCall_mkstemp)
- .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy)
- .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat)
- .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf",
- "vscanf", "vwscanf", "vfscanf", "vfwscanf",
- &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
- .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf",
- "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove",
- &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
- .Cases("strncpy", "strncat", "memset",
- &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
- .Case("drand48", &WalkAST::checkCall_rand)
- .Case("erand48", &WalkAST::checkCall_rand)
- .Case("jrand48", &WalkAST::checkCall_rand)
- .Case("lrand48", &WalkAST::checkCall_rand)
- .Case("mrand48", &WalkAST::checkCall_rand)
- .Case("nrand48", &WalkAST::checkCall_rand)
- .Case("lcong48", &WalkAST::checkCall_rand)
- .Case("rand", &WalkAST::checkCall_rand)
- .Case("rand_r", &WalkAST::checkCall_rand)
- .Case("random", &WalkAST::checkCall_random)
- .Case("vfork", &WalkAST::checkCall_vfork)
- .Default(nullptr);
+ FnCheck evalFunction =
+ llvm::StringSwitch<FnCheck>(Name)
+ .Case("bcmp", &WalkAST::checkCall_bcmp)
+ .Case("bcopy", &WalkAST::checkCall_bcopy)
+ .Case("bzero", &WalkAST::checkCall_bzero)
+ .Case("gets", &WalkAST::checkCall_gets)
+ .Case("getpw", &WalkAST::checkCall_getpw)
+ .Case("mktemp", &WalkAST::checkCall_mktemp)
+ .Case("mkstemp", &WalkAST::checkCall_mkstemp)
+ .Case("mkdtemp", &WalkAST::checkCall_mkstemp)
+ .Case("mkstemps", &WalkAST::checkCall_mkstemp)
+ .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy)
+ .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat)
+ .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf",
+ "vscanf", "vwscanf", "vfscanf", "vfwscanf",
+ &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
+ .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf",
+ "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove",
+ &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
+ .Cases("strncpy", "strncat", "memset", "fprintf",
+ &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
+ .Case("drand48", &WalkAST::checkCall_rand)
+ .Case("erand48", &WalkAST::checkCall_rand)
+ .Case("jrand48", &WalkAST::checkCall_rand)
+ .Case("lrand48", &WalkAST::checkCall_rand)
+ .Case("mrand48", &WalkAST::checkCall_rand)
+ .Case("nrand48", &WalkAST::checkCall_rand)
+ .Case("lcong48", &WalkAST::checkCall_rand)
+ .Case("rand", &WalkAST::checkCall_rand)
+ .Case("rand_r", &WalkAST::checkCall_rand)
+ .Case("random", &WalkAST::checkCall_random)
+ .Case("vfork", &WalkAST::checkCall_vfork)
+ .Default(nullptr);
// If the callee isn't defined, it is not of security concern.
// Check and evaluate the call.
@@ -219,7 +218,6 @@ void WalkAST::VisitForStmt(ForStmt *FS) {
//===----------------------------------------------------------------------===//
// Check: floating point variable used as loop counter.
-// Originally: <rdar://problem/6336718>
// Implements: CERT security coding advisory FLP-30.
//===----------------------------------------------------------------------===//
@@ -325,7 +323,7 @@ void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
llvm::raw_svector_ostream os(sbuf);
os << "Variable '" << drCond->getDecl()->getName()
- << "' with floating point type '" << drCond->getType().getAsString()
+ << "' with floating point type '" << drCond->getType()
<< "' should not be used as a loop counter";
ranges.push_back(drCond->getSourceRange());
@@ -467,8 +465,8 @@ void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) {
//===----------------------------------------------------------------------===//
-// Check: Any use of 'gets' is insecure.
-// Originally: <rdar://problem/6335715>
+// Check: Any use of 'gets' is insecure. Most man pages literally says this.
+//
// Implements (part of): 300-BSI (buildsecurityin.us-cert.gov)
// CWE-242: Use of Inherently Dangerous Function
//===----------------------------------------------------------------------===//
@@ -739,10 +737,10 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
// Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',
// 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
// 'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf',
-// 'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset'
-// is deprecated since C11.
+// 'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset',
+// 'fprintf' is deprecated since C11.
//
-// Use of 'sprintf', 'vsprintf', 'scanf', 'wscanf','fscanf',
+// Use of 'sprintf', 'fprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',
// 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
// 'swscanf', 'vsscanf', 'vswscanf' without buffer limitations
// is insecure.
@@ -764,14 +762,14 @@ void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 };
StringRef Name = FD->getIdentifier()->getName();
- if (Name.startswith("__builtin_"))
- Name = Name.substr(10);
+ Name.consume_front("__builtin_");
int ArgIndex =
llvm::StringSwitch<int>(Name)
.Cases("scanf", "wscanf", "vscanf", "vwscanf", 0)
- .Cases("sprintf", "vsprintf", "fscanf", "fwscanf", "vfscanf",
- "vfwscanf", "sscanf", "swscanf", "vsscanf", "vswscanf", 1)
+ .Cases("fscanf", "fwscanf", "vfscanf", "vfwscanf", "sscanf",
+ "swscanf", "vsscanf", "vswscanf", 1)
+ .Cases("sprintf", "vsprintf", "fprintf", 1)
.Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy",
"memmove", "memset", "strncpy", "strncat", DEPR_ONLY)
.Default(UNKNOWN_CALL);
@@ -785,9 +783,8 @@ void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
// real flow analysis.
auto FormatString =
dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts());
- if (FormatString &&
- FormatString->getString().find("%s") == StringRef::npos &&
- FormatString->getString().find("%[") == StringRef::npos)
+ if (FormatString && !FormatString->getString().contains("%s") &&
+ !FormatString->getString().contains("%["))
BoundsProvided = true;
}
@@ -848,8 +845,13 @@ bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) {
}
//===----------------------------------------------------------------------===//
-// Check: Linear congruent random number generators should not be used
-// Originally: <rdar://problem/63371000>
+// Check: Linear congruent random number generators should not be used,
+// i.e. rand(), random().
+//
+// E. Bach, "Efficient prediction of Marsaglia-Zaman random number generators,"
+// in IEEE Transactions on Information Theory, vol. 44, no. 3, pp. 1253-1257,
+// May 1998, https://doi.org/10.1109/18.669305
+//
// CWE-338: Use of cryptographically weak prng
//===----------------------------------------------------------------------===//
@@ -891,11 +893,7 @@ void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
CE->getCallee()->getSourceRange());
}
-//===----------------------------------------------------------------------===//
-// Check: 'random' should not be used
-// Originally: <rdar://problem/63371000>
-//===----------------------------------------------------------------------===//
-
+// See justification for rand().
void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {
if (!CheckRand || !filter.check_rand)
return;
@@ -975,6 +973,8 @@ void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME) {
if (VT < VersionTuple(11, 0))
return;
break;
+ case llvm::Triple::XROS:
+ break;
default:
return;
}
@@ -991,8 +991,18 @@ void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME) {
}
//===----------------------------------------------------------------------===//
-// Check: Should check whether privileges are dropped successfully.
-// Originally: <rdar://problem/6337132>
+// Check: The caller should always verify that the privileges
+// were dropped successfully.
+//
+// Some library functions, like setuid() and setgid(), should always be used
+// with a check of the return value to verify that the function completed
+// successfully. If the drop fails, the software will continue to run
+// with the raised privileges, which might provide additional access
+// to unprivileged users.
+//
+// (Note that this check predates __attribute__((warn_unused_result)).
+// Do we still need it now that we have a compiler warning for this?
+// Are these standard functions already annotated this way?)
//===----------------------------------------------------------------------===//
void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
index fd53c04f4bbf..be7be15022d3 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
@@ -14,6 +14,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
@@ -40,9 +41,9 @@ bool isRootChanged(intptr_t k) { return k == ROOT_CHANGED; }
// bug<--foo()-- JAIL_ENTERED<--foo()--
class ChrootChecker : public Checker<eval::Call, check::PreCall> {
// This bug refers to possibly break out of a chroot() jail.
- mutable std::unique_ptr<BuiltinBug> BT_BreakJail;
+ const BugType BT_BreakJail{this, "Break out of jail"};
- const CallDescription Chroot{"chroot", 1}, Chdir{"chdir", 1};
+ const CallDescription Chroot{{"chroot"}, 1}, Chdir{{"chdir"}, 1};
public:
ChrootChecker() {}
@@ -63,11 +64,11 @@ private:
} // end anonymous namespace
bool ChrootChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
- if (Call.isCalled(Chroot)) {
+ if (Chroot.matches(Call)) {
evalChroot(Call, C);
return true;
}
- if (Call.isCalled(Chdir)) {
+ if (Chdir.matches(Call)) {
evalChdir(Call, C);
return true;
}
@@ -115,7 +116,7 @@ void ChrootChecker::evalChdir(const CallEvent &Call, CheckerContext &C) const {
void ChrootChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
// Ignore chroot and chdir.
- if (Call.isCalled(Chroot) || Call.isCalled(Chdir))
+ if (matchesAny(Call, Chroot, Chdir))
return;
// If jail state is ROOT_CHANGED, generate BugReport.
@@ -123,12 +124,10 @@ void ChrootChecker::checkPreCall(const CallEvent &Call,
if (k)
if (isRootChanged((intptr_t) *k))
if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
- if (!BT_BreakJail)
- BT_BreakJail.reset(new BuiltinBug(
- this, "Break out of jail", "No call of chdir(\"/\") immediately "
- "after chroot"));
- C.emitReport(std::make_unique<PathSensitiveBugReport>(
- *BT_BreakJail, BT_BreakJail->getDescription(), N));
+ constexpr llvm::StringLiteral Msg =
+ "No call of chdir(\"/\") immediately after chroot";
+ C.emitReport(
+ std::make_unique<PathSensitiveBugReport>(BT_BreakJail, Msg, N));
}
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
index 7968aed85e1b..6692a45a09f7 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
@@ -30,12 +30,13 @@ class CloneChecker
public:
// Checker options.
int MinComplexity;
- bool ReportNormalClones;
+ bool ReportNormalClones = false;
StringRef IgnoredFilesPattern;
private:
mutable CloneDetector Detector;
- mutable std::unique_ptr<BugType> BT_Exact, BT_Suspicious;
+ const BugType BT_Exact{this, "Exact code clone", "Code clone"};
+ const BugType BT_Suspicious{this, "Suspicious code clone", "Code clone"};
public:
void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
@@ -107,15 +108,11 @@ static PathDiagnosticLocation makeLocation(const StmtSequence &S,
void CloneChecker::reportClones(
BugReporter &BR, AnalysisManager &Mgr,
std::vector<CloneDetector::CloneGroup> &CloneGroups) const {
-
- if (!BT_Exact)
- BT_Exact.reset(new BugType(this, "Exact code clone", "Code clone"));
-
for (const CloneDetector::CloneGroup &Group : CloneGroups) {
// We group the clones by printing the first as a warning and all others
// as a note.
auto R = std::make_unique<BasicBugReport>(
- *BT_Exact, "Duplicate code detected", makeLocation(Group.front(), Mgr));
+ BT_Exact, "Duplicate code detected", makeLocation(Group.front(), Mgr));
R->addRange(Group.front().getSourceRange());
for (unsigned i = 1; i < Group.size(); ++i)
@@ -154,10 +151,6 @@ void CloneChecker::reportSuspiciousClones(
}
}
- if (!BT_Suspicious)
- BT_Suspicious.reset(
- new BugType(this, "Suspicious code clone", "Code clone"));
-
ASTContext &ACtx = BR.getContext();
SourceManager &SM = ACtx.getSourceManager();
AnalysisDeclContext *ADC =
@@ -170,7 +163,7 @@ void CloneChecker::reportSuspiciousClones(
// Think how to perform more accurate suggestions?
auto R = std::make_unique<BasicBugReport>(
- *BT_Suspicious,
+ BT_Suspicious,
"Potential copy-paste error; did you really mean to use '" +
Pair.FirstCloneInfo.Variable->getNameAsString() + "' here?",
PathDiagnosticLocation::createBegin(Pair.FirstCloneInfo.Mention, SM,
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
index 1a7f0d5ab74c..65a2ec4076fd 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
@@ -10,11 +10,12 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/Driver/DriverDiagnostic.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
@@ -71,42 +72,27 @@ public:
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},
+ {{{"clear"}, 0}, &ContainerModeling::handleClear},
+ {{{"assign"}, 2}, &ContainerModeling::handleAssign},
+ {{{"push_back"}, 1}, &ContainerModeling::handlePushBack},
+ {{{"emplace_back"}, 1}, &ContainerModeling::handlePushBack},
+ {{{"pop_back"}, 0}, &ContainerModeling::handlePopBack},
+ {{{"push_front"}, 1}, &ContainerModeling::handlePushFront},
+ {{{"emplace_front"}, 1}, &ContainerModeling::handlePushFront},
+ {{{"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},
+ {{{"insert"}, 2}, &ContainerModeling::handleInsert},
+ {{{"emplace"}, 2}, &ContainerModeling::handleInsert},
+ {{{"erase"}, 1}, &ContainerModeling::handleErase},
+ {{{"erase_after"}, 1}, &ContainerModeling::handleEraseAfter},
};
-
+
CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = {
- {{0, "erase", 2},
- &ContainerModeling::handleErase},
- {{0, "erase_after", 2},
- &ContainerModeling::handleEraseAfter},
+ {{{"erase"}, 2}, &ContainerModeling::handleErase},
+ {{{"erase_after"}, 2}, &ContainerModeling::handleEraseAfter},
};
-
};
bool isBeginCall(const FunctionDecl *Func);
@@ -241,7 +227,7 @@ 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)) {
@@ -763,14 +749,14 @@ bool isBeginCall(const FunctionDecl *Func) {
const auto *IdInfo = Func->getIdentifier();
if (!IdInfo)
return false;
- return IdInfo->getName().endswith_insensitive("begin");
+ return IdInfo->getName().ends_with_insensitive("begin");
}
bool isEndCall(const FunctionDecl *Func) {
const auto *IdInfo = Func->getIdentifier();
if (!IdInfo)
return false;
- return IdInfo->getName().endswith_insensitive("end");
+ return IdInfo->getName().ends_with_insensitive("end");
}
const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
@@ -1035,7 +1021,7 @@ SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
SymbolRef NewSym) {
auto &SymMgr = SVB.getSymbolManager();
auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
- nonloc::SymbolVal(OldExpr),
+ nonloc::SymbolVal(OldExpr),
SymMgr.getType(OrigExpr));
const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
index 4216a6883119..eca8d3cc0722 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
@@ -42,22 +42,22 @@ public:
void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;
private:
- mutable std::unique_ptr<BuiltinBug> BT;
+ const BugType BT{this, "Conversion"};
bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType,
CheckerContext &C) const;
bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const;
- void reportBug(ExplodedNode *N, CheckerContext &C, const char Msg[]) const;
+ void reportBug(ExplodedNode *N, const Expr *E, CheckerContext &C,
+ const char Msg[]) const;
};
}
void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
CheckerContext &C) const {
- // TODO: For now we only warn about DeclRefExpr, to avoid noise. Warn for
- // calculations also.
- if (!isa<DeclRefExpr>(Cast->IgnoreParenImpCasts()))
+ // Don't warn for implicit conversions to bool
+ if (Cast->getType()->isBooleanType())
return;
// Don't warn for loss of sign/precision in macros.
@@ -69,6 +69,9 @@ void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
const Stmt *Parent = PM.getParent(Cast);
if (!Parent)
return;
+ // Dont warn if this is part of an explicit cast
+ if (isa<ExplicitCastExpr>(Parent))
+ return;
bool LossOfSign = false;
bool LossOfPrecision = false;
@@ -77,8 +80,10 @@ void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
if (const auto *B = dyn_cast<BinaryOperator>(Parent)) {
BinaryOperator::Opcode Opc = B->getOpcode();
if (Opc == BO_Assign) {
- LossOfSign = isLossOfSign(Cast, C);
- LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
+ if (!Cast->IgnoreParenImpCasts()->isEvaluatable(C.getASTContext())) {
+ LossOfSign = isLossOfSign(Cast, C);
+ LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
+ }
} else if (Opc == BO_AddAssign || Opc == BO_SubAssign) {
// No loss of sign.
LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
@@ -97,7 +102,12 @@ void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
} else if (B->isRelationalOp() || B->isMultiplicativeOp()) {
LossOfSign = isLossOfSign(Cast, C);
}
- } else if (isa<DeclStmt>(Parent)) {
+ } else if (isa<DeclStmt, ReturnStmt>(Parent)) {
+ if (!Cast->IgnoreParenImpCasts()->isEvaluatable(C.getASTContext())) {
+ LossOfSign = isLossOfSign(Cast, C);
+ LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
+ }
+ } else {
LossOfSign = isLossOfSign(Cast, C);
LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
}
@@ -108,20 +118,17 @@ void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
if (!N)
return;
if (LossOfSign)
- reportBug(N, C, "Loss of sign in implicit conversion");
+ reportBug(N, Cast, C, "Loss of sign in implicit conversion");
if (LossOfPrecision)
- reportBug(N, C, "Loss of precision in implicit conversion");
+ reportBug(N, Cast, C, "Loss of precision in implicit conversion");
}
}
-void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C,
- const char Msg[]) const {
- if (!BT)
- BT.reset(
- new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
-
+void ConversionChecker::reportBug(ExplodedNode *N, const Expr *E,
+ CheckerContext &C, const char Msg[]) const {
// Generate a report for this bug.
- auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
+ bugreporter::trackExpressionValue(N, E, *R);
C.emitReport(std::move(R));
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
index 8070d869f678..86f446fc411c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
@@ -38,17 +38,17 @@ public:
llvm::DenseSet<const VarDecl *> &S;
bool TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt *S) {
- SaveAndRestore<bool> inFinally(inEH, true);
+ SaveAndRestore inFinally(inEH, true);
return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S);
}
bool TraverseObjCAtCatchStmt(ObjCAtCatchStmt *S) {
- SaveAndRestore<bool> inCatch(inEH, true);
+ SaveAndRestore inCatch(inEH, true);
return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(S);
}
bool TraverseCXXCatchStmt(CXXCatchStmt *S) {
- SaveAndRestore<bool> inCatch(inEH, true);
+ SaveAndRestore inCatch(inEH, true);
return TraverseStmt(S->getHandlerBlock());
}
@@ -93,9 +93,9 @@ void ReachableCode::computeReachableBlocks() {
if (isReachable)
continue;
isReachable = true;
- for (CFGBlock::const_succ_iterator i = block->succ_begin(),
- e = block->succ_end(); i != e; ++i)
- if (const CFGBlock *succ = *i)
+
+ for (const CFGBlock *succ : block->succs())
+ if (succ)
worklist.push_back(succ);
}
}
@@ -103,15 +103,12 @@ void ReachableCode::computeReachableBlocks() {
static const Expr *
LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex) {
while (Ex) {
- const BinaryOperator *BO =
- dyn_cast<BinaryOperator>(Ex->IgnoreParenCasts());
+ Ex = Ex->IgnoreParenCasts();
+ const BinaryOperator *BO = dyn_cast<BinaryOperator>(Ex);
if (!BO)
break;
- if (BO->getOpcode() == BO_Assign) {
- Ex = BO->getRHS();
- continue;
- }
- if (BO->getOpcode() == BO_Comma) {
+ BinaryOperatorKind Op = BO->getOpcode();
+ if (Op == BO_Assign || Op == BO_Comma) {
Ex = BO->getRHS();
continue;
}
@@ -186,7 +183,7 @@ public:
// Files autogenerated by DriverKit IIG contain some dead stores that
// we don't want to report.
- if (Data.startswith("/* iig"))
+ if (Data.starts_with("/* iig"))
return true;
return false;
@@ -243,7 +240,7 @@ public:
case DeadIncrement:
BugType = "Dead increment";
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case Standard:
if (!BugType) BugType = "Dead assignment";
os << "Value stored to '" << *V << "' is never read";
@@ -334,8 +331,7 @@ public:
// Special case: check for assigning null to a pointer.
// This is a common form of defensive programming.
const Expr *RHS =
- LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS());
- RHS = RHS->IgnoreParenCasts();
+ LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS());
QualType T = VD->getType();
if (T.isVolatileQualified())
@@ -418,8 +414,7 @@ public:
if (isConstant(E))
return;
- if (const DeclRefExpr *DRE =
- dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
// Special case: check for initialization from constant
// variables.
@@ -441,7 +436,7 @@ public:
PathDiagnosticLocation Loc =
PathDiagnosticLocation::create(V, BR.getSourceManager());
- Report(V, DeadInit, Loc, E->getSourceRange());
+ Report(V, DeadInit, Loc, V->getInit()->getSourceRange());
}
}
}
@@ -453,8 +448,9 @@ private:
bool isConstant(const InitListExpr *Candidate) const {
// We consider init list to be constant if each member of the list can be
// interpreted as constant.
- return llvm::all_of(Candidate->inits(),
- [this](const Expr *Init) { return isConstant(Init); });
+ return llvm::all_of(Candidate->inits(), [this](const Expr *Init) {
+ return isConstant(Init->IgnoreParenCasts());
+ });
}
/// Return true if the given expression can be interpreted as constant
@@ -464,7 +460,7 @@ private:
return true;
// We should also allow defensive initialization of structs, i.e. { 0 }
- if (const auto *ILE = dyn_cast<InitListExpr>(E->IgnoreParenCasts())) {
+ if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
return isConstant(ILE);
}
@@ -507,7 +503,7 @@ public:
// Treat local variables captured by reference in C++ lambdas as escaped.
void findLambdaReferenceCaptures(const LambdaExpr *LE) {
const CXXRecordDecl *LambdaClass = LE->getLambdaClass();
- llvm::DenseMap<const VarDecl *, FieldDecl *> CaptureFields;
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> CaptureFields;
FieldDecl *ThisCaptureField;
LambdaClass->getCaptureFields(CaptureFields, ThisCaptureField);
@@ -515,14 +511,14 @@ public:
if (!C.capturesVariable())
continue;
- VarDecl *VD = C.getCapturedVar();
+ ValueDecl *VD = C.getCapturedVar();
const FieldDecl *FD = CaptureFields[VD];
- if (!FD)
+ if (!FD || !isa<VarDecl>(VD))
continue;
// If the capture field is a reference type, it is capture-by-reference.
if (FD->getType()->isReferenceType())
- Escaped.insert(VD);
+ Escaped.insert(cast<VarDecl>(VD));
}
}
};
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
index 7cdd78b8adfb..04bbe85473c0 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
@@ -271,9 +271,8 @@ public:
const Table &Config = mgr.options.Config;
SmallVector<const Table::MapEntryTy *, 32> Keys;
- for (Table::const_iterator I = Config.begin(), E = Config.end(); I != E;
- ++I)
- Keys.push_back(&*I);
+ for (const auto &Entry : Config)
+ Keys.push_back(&Entry);
llvm::array_pod_sort(Keys.begin(), Keys.end(), compareEntry);
llvm::errs() << "[config]\n";
@@ -302,7 +301,7 @@ class ExplodedGraphViewer : public Checker< check::EndAnalysis > {
public:
ExplodedGraphViewer() {}
void checkEndAnalysis(ExplodedGraph &G, BugReporter &B,ExprEngine &Eng) const {
- Eng.ViewGraph(0);
+ Eng.ViewGraph(false);
}
};
@@ -323,7 +322,7 @@ bool ento::shouldRegisterExplodedGraphViewer(const CheckerManager &mgr) {
namespace {
class ReportStmts : public Checker<check::PreStmt<Stmt>> {
- BuiltinBug BT_stmtLoc{this, "Statement"};
+ BugType BT_stmtLoc{this, "Statement"};
public:
void checkPreStmt(const Stmt *S, CheckerContext &C) const {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp
index 6fed999ffc80..97f769b1c451 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp
@@ -13,6 +13,7 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -27,7 +28,8 @@ namespace {
class DebugContainerModeling
: public Checker<eval::Call> {
- std::unique_ptr<BugType> DebugMsgBugType;
+ const BugType DebugMsgBugType{this, "Checking analyzer assumptions", "debug",
+ /*SuppressOnSink=*/true};
template <typename Getter>
void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C,
@@ -40,26 +42,18 @@ class DebugContainerModeling
CheckerContext &) const;
CallDescriptionMap<FnCheck> Callbacks = {
- {{0, "clang_analyzer_container_begin", 1},
- &DebugContainerModeling::analyzerContainerBegin},
- {{0, "clang_analyzer_container_end", 1},
- &DebugContainerModeling::analyzerContainerEnd},
+ {{{"clang_analyzer_container_begin"}, 1},
+ &DebugContainerModeling::analyzerContainerBegin},
+ {{{"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());
@@ -136,8 +130,8 @@ ExplodedNode *DebugContainerModeling::reportDebugMsg(llvm::StringRef Msg,
return nullptr;
auto &BR = C.getBugReporter();
- BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
- Msg, N));
+ BR.emitReport(
+ std::make_unique<PathSensitiveBugReport>(DebugMsgBugType, Msg, N));
return N;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp
index 5833eea56da8..ff479c7b0ac8 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp
@@ -13,6 +13,7 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -27,7 +28,8 @@ namespace {
class DebugIteratorModeling
: public Checker<eval::Call> {
- std::unique_ptr<BugType> DebugMsgBugType;
+ const BugType DebugMsgBugType{this, "Checking analyzer assumptions", "debug",
+ /*SuppressOnSink=*/true};
template <typename Getter>
void analyzerIteratorDataField(const CallExpr *CE, CheckerContext &C,
@@ -41,28 +43,20 @@ class DebugIteratorModeling
CheckerContext &) const;
CallDescriptionMap<FnCheck> Callbacks = {
- {{0, "clang_analyzer_iterator_position", 1},
- &DebugIteratorModeling::analyzerIteratorPosition},
- {{0, "clang_analyzer_iterator_container", 1},
- &DebugIteratorModeling::analyzerIteratorContainer},
- {{0, "clang_analyzer_iterator_validity", 1},
- &DebugIteratorModeling::analyzerIteratorValidity},
+ {{{"clang_analyzer_iterator_position"}, 1},
+ &DebugIteratorModeling::analyzerIteratorPosition},
+ {{{"clang_analyzer_iterator_container"}, 1},
+ &DebugIteratorModeling::analyzerIteratorContainer},
+ {{{"clang_analyzer_iterator_validity"}, 1},
+ &DebugIteratorModeling::analyzerIteratorValidity},
};
public:
- DebugIteratorModeling();
-
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
};
} //namespace
-DebugIteratorModeling::DebugIteratorModeling() {
- DebugMsgBugType.reset(
- new BugType(this, "Checking analyzer assumptions", "debug",
- /*SuppressOnSink=*/true));
-}
-
bool DebugIteratorModeling::evalCall(const CallEvent &Call,
CheckerContext &C) const {
const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
@@ -130,8 +124,8 @@ ExplodedNode *DebugIteratorModeling::reportDebugMsg(llvm::StringRef Msg,
return nullptr;
auto &BR = C.getBugReporter();
- BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
- Msg, N));
+ BR.emitReport(
+ std::make_unique<PathSensitiveBugReport>(DebugMsgBugType, Msg, N));
return N;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
deleted file mode 100644
index 7c5833762008..000000000000
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
+++ /dev/null
@@ -1,155 +0,0 @@
-//===-- DeleteWithNonVirtualDtorChecker.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 the OOP52-CPP CERT rule: Do not delete a polymorphic
-// object without a virtual destructor.
-//
-// Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor report if
-// an object with a virtual function but a non-virtual destructor exists or is
-// deleted, respectively.
-//
-// This check exceeds them by comparing the dynamic and static types of the
-// object at the point of destruction and only warns if it happens through a
-// pointer to a base type without a virtual destructor. The check places a note
-// at the last point where the conversion from derived to base happened.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
-#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
-#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
-#include "clang/StaticAnalyzer/Core/Checker.h"
-#include "clang/StaticAnalyzer/Core/CheckerManager.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
-
-using namespace clang;
-using namespace ento;
-
-namespace {
-class DeleteWithNonVirtualDtorChecker
- : public Checker<check::PreStmt<CXXDeleteExpr>> {
- mutable std::unique_ptr<BugType> BT;
-
- class DeleteBugVisitor : public BugReporterVisitor {
- public:
- DeleteBugVisitor() : Satisfied(false) {}
- void Profile(llvm::FoldingSetNodeID &ID) const override {
- static int X = 0;
- ID.AddPointer(&X);
- }
- PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- PathSensitiveBugReport &BR) override;
-
- private:
- bool Satisfied;
- };
-
-public:
- void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
-};
-} // end anonymous namespace
-
-void DeleteWithNonVirtualDtorChecker::checkPreStmt(const CXXDeleteExpr *DE,
- CheckerContext &C) const {
- const Expr *DeletedObj = DE->getArgument();
- const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion();
- if (!MR)
- return;
-
- const auto *BaseClassRegion = MR->getAs<TypedValueRegion>();
- const auto *DerivedClassRegion = MR->getBaseRegion()->getAs<SymbolicRegion>();
- if (!BaseClassRegion || !DerivedClassRegion)
- return;
-
- const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl();
- const auto *DerivedClass =
- DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl();
- if (!BaseClass || !DerivedClass)
- return;
-
- if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition())
- return;
-
- if (BaseClass->getDestructor()->isVirtual())
- return;
-
- if (!DerivedClass->isDerivedFrom(BaseClass))
- return;
-
- if (!BT)
- BT.reset(new BugType(this,
- "Destruction of a polymorphic object with no "
- "virtual destructor",
- "Logic error"));
-
- ExplodedNode *N = C.generateNonFatalErrorNode();
- 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.
- R->markInteresting(BaseClassRegion);
- R->addVisitor(std::make_unique<DeleteBugVisitor>());
- C.emitReport(std::move(R));
-}
-
-PathDiagnosticPieceRef
-DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode(
- const ExplodedNode *N, BugReporterContext &BRC,
- PathSensitiveBugReport &BR) {
- // Stop traversal after the first conversion was found on a path.
- if (Satisfied)
- return nullptr;
-
- const Stmt *S = N->getStmtForDiagnostics();
- if (!S)
- return nullptr;
-
- const auto *CastE = dyn_cast<CastExpr>(S);
- if (!CastE)
- return nullptr;
-
- // Only interested in DerivedToBase implicit casts.
- // Explicit casts can have different CastKinds.
- if (const auto *ImplCastE = dyn_cast<ImplicitCastExpr>(CastE)) {
- if (ImplCastE->getCastKind() != CK_DerivedToBase)
- return nullptr;
- }
-
- // Region associated with the current cast expression.
- const MemRegion *M = N->getSVal(CastE).getAsRegion();
- if (!M)
- return nullptr;
-
- // Check if target region was marked as problematic previously.
- if (!BR.isInteresting(M))
- return nullptr;
-
- // Stop traversal on this path.
- Satisfied = true;
-
- SmallString<256> Buf;
- llvm::raw_svector_ostream OS(Buf);
- OS << "Conversion from derived to base happened here";
- PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
- N->getLocationContext());
- return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
-}
-
-void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) {
- mgr.registerChecker<DeleteWithNonVirtualDtorChecker>();
-}
-
-bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker(
- 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 4a9c7ce3c66d..a678c3827e7f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
@@ -11,9 +11,10 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExprOpenMP.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"
@@ -39,6 +40,8 @@ class DereferenceChecker
void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S,
CheckerContext &C) const;
+ bool suppressReport(CheckerContext &C, const Expr *E) const;
+
public:
void checkLocation(SVal location, bool isLoad, const Stmt* S,
CheckerContext &C) const;
@@ -49,6 +52,8 @@ public:
const Expr *Ex, const ProgramState *state,
const LocationContext *LCtx,
bool loadedFrom = false);
+
+ bool SuppressAddressSpaces = false;
};
} // end anonymous namespace
@@ -109,9 +114,35 @@ static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){
return E;
}
-static bool suppressReport(const Expr *E) {
- // Do not report dereferences on memory in non-default address spaces.
- return E->getType().hasAddressSpace();
+bool DereferenceChecker::suppressReport(CheckerContext &C,
+ const Expr *E) const {
+ // Do not report dereferences on memory that use address space #256, #257,
+ // and #258. Those address spaces are used when dereferencing address spaces
+ // relative to the GS, FS, and SS segments on x86/x86-64 targets.
+ // Dereferencing a null pointer in these address spaces is not defined
+ // as an error. All other null dereferences in other address spaces
+ // are defined as an error unless explicitly defined.
+ // See https://clang.llvm.org/docs/LanguageExtensions.html, the section
+ // "X86/X86-64 Language Extensions"
+
+ QualType Ty = E->getType();
+ if (!Ty.hasAddressSpace())
+ return false;
+ if (SuppressAddressSpaces)
+ return true;
+
+ const llvm::Triple::ArchType Arch =
+ C.getASTContext().getTargetInfo().getTriple().getArch();
+
+ if ((Arch == llvm::Triple::x86) || (Arch == llvm::Triple::x86_64)) {
+ switch (toTargetAddressSpace(E->getType().getAddressSpace())) {
+ case 256:
+ case 257:
+ case 258:
+ return true;
+ }
+ }
+ return false;
}
static bool isDeclRefExprToReference(const Expr *E) {
@@ -209,7 +240,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
// Check for dereference of an undefined value.
if (l.isUndef()) {
const Expr *DerefExpr = getDereferenceExpr(S);
- if (!suppressReport(DerefExpr))
+ if (!suppressReport(C, DerefExpr))
reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C);
return;
}
@@ -217,7 +248,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
// Check for null dereferences.
- if (!location.getAs<Loc>())
+ if (!isa<Loc>(location))
return;
ProgramStateRef state = C.getState();
@@ -230,7 +261,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
// We know that 'location' can only be null. This is what
// we call an "explicit" null dereference.
const Expr *expr = getDereferenceExpr(S);
- if (!suppressReport(expr)) {
+ if (!suppressReport(C, expr)) {
reportBug(DerefKind::NullPointer, nullState, expr, C);
return;
}
@@ -272,7 +303,7 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
if (StNull) {
if (!StNonNull) {
const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
- if (!suppressReport(expr)) {
+ if (!suppressReport(C, expr)) {
reportBug(DerefKind::NullPointer, StNull, expr, C);
return;
}
@@ -308,7 +339,9 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
}
void ento::registerDereferenceChecker(CheckerManager &mgr) {
- mgr.registerChecker<DereferenceChecker>();
+ auto *Chk = mgr.registerChecker<DereferenceChecker>();
+ Chk->SuppressAddressSpaces = mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ mgr.getCurrentCheckerName(), "SuppressAddressSpaces");
}
bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
index df88b71ff063..49486ea796c2 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
@@ -44,8 +44,8 @@ static bool DefaultMethodFilter(const ObjCMethodDecl *M) {
M->getMethodFamily() == OMF_dealloc ||
M->getMethodFamily() == OMF_copy ||
M->getMethodFamily() == OMF_mutableCopy ||
- M->getSelector().getNameForSlot(0).find("init") != StringRef::npos ||
- M->getSelector().getNameForSlot(0).find("Init") != StringRef::npos;
+ M->getSelector().getNameForSlot(0).contains("init") ||
+ M->getSelector().getNameForSlot(0).contains("Init");
}
class DirectIvarAssignment :
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
index 2b3164ba4a2c..5496f087447f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
@@ -11,12 +11,14 @@
//
//===----------------------------------------------------------------------===//
-#include "Taint.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Checkers/Taint.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/CheckerContext.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -24,9 +26,13 @@ using namespace taint;
namespace {
class DivZeroChecker : public Checker< check::PreStmt<BinaryOperator> > {
- mutable std::unique_ptr<BuiltinBug> BT;
- void reportBug(const char *Msg, ProgramStateRef StateZero, CheckerContext &C,
- std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const;
+ const BugType BT{this, "Division by zero"};
+ const BugType TaintBT{this, "Division by zero", categories::TaintedData};
+ void reportBug(StringRef Msg, ProgramStateRef StateZero,
+ CheckerContext &C) const;
+ void reportTaintBug(StringRef Msg, ProgramStateRef StateZero,
+ CheckerContext &C,
+ llvm::ArrayRef<SymbolRef> TaintedSyms) const;
public:
void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
@@ -40,16 +46,23 @@ static const Expr *getDenomExpr(const ExplodedNode *N) {
return nullptr;
}
-void DivZeroChecker::reportBug(
- const char *Msg, ProgramStateRef StateZero, CheckerContext &C,
- std::unique_ptr<BugReporterVisitor> Visitor) const {
+void DivZeroChecker::reportBug(StringRef Msg, ProgramStateRef StateZero,
+ CheckerContext &C) const {
if (ExplodedNode *N = C.generateErrorNode(StateZero)) {
- if (!BT)
- BT.reset(new BuiltinBug(this, "Division by zero"));
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
+ bugreporter::trackExpressionValue(N, getDenomExpr(N), *R);
+ C.emitReport(std::move(R));
+ }
+}
- auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
- R->addVisitor(std::move(Visitor));
+void DivZeroChecker::reportTaintBug(
+ StringRef Msg, ProgramStateRef StateZero, CheckerContext &C,
+ llvm::ArrayRef<SymbolRef> TaintedSyms) const {
+ if (ExplodedNode *N = C.generateErrorNode(StateZero)) {
+ auto R = std::make_unique<PathSensitiveBugReport>(TaintBT, Msg, N);
bugreporter::trackExpressionValue(N, getDenomExpr(N), *R);
+ for (auto Sym : TaintedSyms)
+ R->markInteresting(Sym);
C.emitReport(std::move(R));
}
}
@@ -67,7 +80,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
return;
SVal Denom = C.getSVal(B->getRHS());
- Optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>();
+ std::optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>();
// Divide-by-undefined handled in the generic checking for uses of
// undefined values.
@@ -85,11 +98,13 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
return;
}
- bool TaintedD = isTainted(C.getState(), *DV);
- if ((stateNotZero && stateZero && TaintedD)) {
- reportBug("Division by a tainted value, possibly zero", stateZero, C,
- std::make_unique<taint::TaintBugVisitor>(*DV));
- return;
+ if ((stateNotZero && stateZero)) {
+ std::vector<SymbolRef> taintedSyms = getTaintedSymbols(C.getState(), *DV);
+ if (!taintedSyms.empty()) {
+ reportTaintBug("Division by a tainted value, possibly zero", stateZero, C,
+ taintedSyms);
+ return;
+ }
}
// If we get here, then the denom should not be zero. We abandon the implicit
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
index dbc930d7d37b..0ad307d3ebd5 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
@@ -30,12 +30,7 @@ using namespace ento;
namespace {
class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> {
- mutable std::unique_ptr<BugType> BT;
- void initBugType() const {
- if (!BT)
- BT.reset(
- new BugType(this, "Dynamic and static type mismatch", "Type Error"));
- }
+ const BugType BT{this, "Dynamic and static type mismatch", "Type Error"};
class DynamicTypeBugVisitor : public BugReporterVisitor {
public:
@@ -70,7 +65,6 @@ void DynamicTypeChecker::reportTypeError(QualType DynamicType,
const MemRegion *Reg,
const Stmt *ReportedNode,
CheckerContext &C) const {
- initBugType();
SmallString<192> Buf;
llvm::raw_svector_ostream OS(Buf);
OS << "Object has a dynamic type '";
@@ -81,7 +75,7 @@ void DynamicTypeChecker::reportTypeError(QualType DynamicType,
llvm::Twine());
OS << "'";
auto R = std::make_unique<PathSensitiveBugReport>(
- *BT, OS.str(), C.generateNonFatalErrorNode());
+ BT, OS.str(), C.generateNonFatalErrorNode());
R->markInteresting(Reg);
R->addVisitor(std::make_unique<DynamicTypeBugVisitor>(Reg));
R->addRange(ReportedNode->getSourceRange());
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
index 14ba5d769969..034774a252b1 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -31,6 +31,8 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "llvm/ADT/STLExtras.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -56,9 +58,6 @@ class DynamicTypePropagation:
check::PreObjCMessage,
check::PostObjCMessage > {
- const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
- CheckerContext &C) const;
-
/// Return a better dynamic type if one can be derived from the cast.
const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE,
CheckerContext &C) const;
@@ -108,7 +107,7 @@ public:
void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
/// This value is set to true, when the Generics checker is turned on.
- DefaultBool CheckGenerics;
+ bool CheckGenerics = false;
CheckerNameRef GenericCheckName;
};
@@ -235,11 +234,9 @@ void DynamicTypePropagation::checkDeadSymbols(SymbolReaper &SR,
MostSpecializedTypeArgsMapTy TyArgMap =
State->get<MostSpecializedTypeArgsMap>();
- for (MostSpecializedTypeArgsMapTy::iterator I = TyArgMap.begin(),
- E = TyArgMap.end();
- I != E; ++I) {
- if (SR.isDead(I->first)) {
- State = State->remove<MostSpecializedTypeArgsMap>(I->first);
+ for (SymbolRef Sym : llvm::make_first_range(TyArgMap)) {
+ if (SR.isDead(Sym)) {
+ State = State->remove<MostSpecializedTypeArgsMap>(Sym);
}
}
@@ -271,12 +268,12 @@ void DynamicTypePropagation::checkPreCall(const CallEvent &Call,
// a more-derived class.
switch (Ctor->getOriginExpr()->getConstructionKind()) {
- case CXXConstructExpr::CK_Complete:
- case CXXConstructExpr::CK_Delegating:
+ case CXXConstructionKind::Complete:
+ case CXXConstructionKind::Delegating:
// No additional type info necessary.
return;
- case CXXConstructExpr::CK_NonVirtualBase:
- case CXXConstructExpr::CK_VirtualBase:
+ case CXXConstructionKind::NonVirtualBase:
+ case CXXConstructionKind::VirtualBase:
if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion())
recordFixedType(Target, Ctor->getDecl(), C);
return;
@@ -363,16 +360,16 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call,
if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
// We may need to undo the effects of our pre-call check.
switch (Ctor->getOriginExpr()->getConstructionKind()) {
- case CXXConstructExpr::CK_Complete:
- case CXXConstructExpr::CK_Delegating:
+ case CXXConstructionKind::Complete:
+ case CXXConstructionKind::Delegating:
// No additional work necessary.
// Note: This will leave behind the actual type of the object for
// complete constructors, but arguably that's a good thing, since it
// means the dynamic type info will be correct even for objects
// constructed with operator new.
return;
- case CXXConstructExpr::CK_NonVirtualBase:
- case CXXConstructExpr::CK_VirtualBase:
+ case CXXConstructionKind::NonVirtualBase:
+ case CXXConstructionKind::VirtualBase:
if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) {
// We just finished a base constructor. Now we can use the subclass's
// type when resolving virtual calls.
@@ -384,7 +381,7 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call,
// FIXME: Instead of relying on the ParentMap, we should have the
// trigger-statement (InitListExpr in this case) available in this
// callback, ideally as part of CallEvent.
- if (dyn_cast_or_null<InitListExpr>(
+ if (isa_and_nonnull<InitListExpr>(
LCtx->getParentMap().getParent(Ctor->getOriginExpr())))
return;
@@ -716,7 +713,7 @@ static bool isObjCTypeParamDependent(QualType Type) {
class IsObjCTypeParamDependentTypeVisitor
: public RecursiveASTVisitor<IsObjCTypeParamDependentTypeVisitor> {
public:
- IsObjCTypeParamDependentTypeVisitor() : Result(false) {}
+ IsObjCTypeParamDependentTypeVisitor() = default;
bool VisitObjCTypeParamType(const ObjCTypeParamType *Type) {
if (isa<ObjCTypeParamDecl>(Type->getDecl())) {
Result = true;
@@ -725,7 +722,7 @@ static bool isObjCTypeParamDependent(QualType Type) {
return true;
}
- bool Result;
+ bool Result = false;
};
IsObjCTypeParamDependentTypeVisitor Visitor;
@@ -744,8 +741,6 @@ findMethodDecl(const ObjCMessageExpr *MessageExpr,
const ObjCMethodDecl *Method = nullptr;
QualType ReceiverType = MessageExpr->getReceiverType();
- const auto *ReceiverObjectPtrType =
- ReceiverType->getAs<ObjCObjectPointerType>();
// Do this "devirtualization" on instance and class methods only. Trust the
// static type on super and super class calls.
@@ -755,7 +750,8 @@ findMethodDecl(const ObjCMessageExpr *MessageExpr,
// type, look up the method in the tracked type, not in the receiver type.
// This way we preserve more information.
if (ReceiverType->isObjCIdType() || ReceiverType->isObjCClassType() ||
- ASTCtxt.canAssignObjCInterfaces(ReceiverObjectPtrType, TrackedType)) {
+ ASTCtxt.canAssignObjCInterfaces(
+ ReceiverType->castAs<ObjCObjectPointerType>(), TrackedType)) {
const ObjCInterfaceDecl *InterfaceDecl = TrackedType->getInterfaceDecl();
// The method might not be found.
Selector Sel = MessageExpr->getSelector();
@@ -849,7 +845,7 @@ void DynamicTypePropagation::checkPreObjCMessage(const ObjCMethodCall &M,
return;
}
- Optional<ArrayRef<QualType>> TypeArgs =
+ std::optional<ArrayRef<QualType>> TypeArgs =
(*TrackedType)->getObjCSubstitutions(Method->getDeclContext());
// This case might happen when there is an unspecialized override of a
// specialized method.
@@ -982,7 +978,7 @@ void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M,
if (!Method)
return;
- Optional<ArrayRef<QualType>> TypeArgs =
+ std::optional<ArrayRef<QualType>> TypeArgs =
(*TrackedType)->getObjCSubstitutions(Method->getDeclContext());
if (!TypeArgs)
return;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
index 0e94b915a468..0fa20428c1b5 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
@@ -22,9 +22,12 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/Support/FormatVariadic.h"
+#include <optional>
using namespace clang;
using namespace ento;
+using llvm::formatv;
namespace {
// This evaluator checks two SVals for equality. The first SVal is provided via
@@ -57,8 +60,9 @@ public:
// Being conservative, it does not warn if there is slight possibility the
// value can be matching.
class EnumCastOutOfRangeChecker : public Checker<check::PreStmt<CastExpr>> {
- mutable std::unique_ptr<BuiltinBug> EnumValueCastOutOfRange;
- void reportWarning(CheckerContext &C) const;
+ const BugType EnumValueCastOutOfRange{this, "Enum cast out of range"};
+ void reportWarning(CheckerContext &C, const CastExpr *CE,
+ const EnumDecl *E) const;
public:
void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
@@ -71,21 +75,39 @@ EnumValueVector getDeclValuesForEnum(const EnumDecl *ED) {
EnumValueVector DeclValues(
std::distance(ED->enumerator_begin(), ED->enumerator_end()));
llvm::transform(ED->enumerators(), DeclValues.begin(),
- [](const EnumConstantDecl *D) { return D->getInitVal(); });
+ [](const EnumConstantDecl *D) { return D->getInitVal(); });
return DeclValues;
}
} // namespace
-void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C) const {
+void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C,
+ const CastExpr *CE,
+ const EnumDecl *E) const {
+ assert(E && "valid EnumDecl* is expected");
if (const ExplodedNode *N = C.generateNonFatalErrorNode()) {
- if (!EnumValueCastOutOfRange)
- EnumValueCastOutOfRange.reset(
- new BuiltinBug(this, "Enum cast out of range",
- "The value provided to the cast expression is not in "
- "the valid range of values for the enum"));
- C.emitReport(std::make_unique<PathSensitiveBugReport>(
- *EnumValueCastOutOfRange, EnumValueCastOutOfRange->getDescription(),
- N));
+ std::string ValueStr = "", NameStr = "the enum";
+
+ // Try to add details to the message:
+ const auto ConcreteValue =
+ C.getSVal(CE->getSubExpr()).getAs<nonloc::ConcreteInt>();
+ if (ConcreteValue) {
+ ValueStr = formatv(" '{0}'", ConcreteValue->getValue());
+ }
+ if (StringRef EnumName{E->getName()}; !EnumName.empty()) {
+ NameStr = formatv("'{0}'", EnumName);
+ }
+
+ std::string Msg = formatv("The value{0} provided to the cast expression is "
+ "not in the valid range of values for {1}",
+ ValueStr, NameStr);
+
+ auto BR = std::make_unique<PathSensitiveBugReport>(EnumValueCastOutOfRange,
+ Msg, N);
+ bugreporter::trackExpressionValue(N, CE->getSubExpr(), *BR);
+ BR->addNote("enum declared here",
+ PathDiagnosticLocation::create(E, C.getSourceManager()),
+ {E->getSourceRange()});
+ C.emitReport(std::move(BR));
}
}
@@ -94,10 +116,10 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
// Only perform enum range check on casts where such checks are valid. For
// all other cast kinds (where enum range checks are unnecessary or invalid),
- // just return immediately. TODO: The set of casts whitelisted for enum
- // range checking may be incomplete. Better to add a missing cast kind to
- // enable a missing check than to generate false negatives and have to remove
- // those later.
+ // just return immediately. TODO: The set of casts allowed for enum range
+ // checking may be incomplete. Better to add a missing cast kind to enable a
+ // missing check than to generate false negatives and have to remove those
+ // later.
switch (CE->getCastKind()) {
case CK_IntegralCast:
break;
@@ -108,7 +130,7 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
}
// Get the value of the expression to cast.
- const llvm::Optional<DefinedOrUnknownSVal> ValueToCast =
+ const std::optional<DefinedOrUnknownSVal> ValueToCast =
C.getSVal(CE->getSubExpr()).getAs<DefinedOrUnknownSVal>();
// If the value cannot be reasoned about (not even a DefinedOrUnknownSVal),
@@ -128,14 +150,25 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
const EnumDecl *ED = T->castAs<EnumType>()->getDecl();
EnumValueVector DeclValues = getDeclValuesForEnum(ED);
+
+ // If the declarator list is empty, bail out.
+ // Every initialization an enum with a fixed underlying type but without any
+ // enumerators would produce a warning if we were to continue at this point.
+ // The most notable example is std::byte in the C++17 standard library.
+ // TODO: Create heuristics to bail out when the enum type is intended to be
+ // used to store combinations of flag values (to mitigate the limitation
+ // described in the docs).
+ if (DeclValues.size() == 0)
+ return;
+
// Check if any of the enum values possibly match.
- bool PossibleValueMatch = llvm::any_of(
- DeclValues, ConstraintBasedEQEvaluator(C, *ValueToCast));
+ bool PossibleValueMatch =
+ llvm::any_of(DeclValues, ConstraintBasedEQEvaluator(C, *ValueToCast));
// If there is no value that can possibly match any of the enum values, then
// warn.
if (!PossibleValueMatch)
- reportWarning(C);
+ reportWarning(C, CE, ED);
}
void ento::registerEnumCastOutOfRangeChecker(CheckerManager &mgr) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp
new file mode 100644
index 000000000000..265185e64107
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp
@@ -0,0 +1,250 @@
+//=== ErrnoChecker.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 defines an "errno checker" that can detect some invalid use of the
+// system-defined value 'errno'. This checker works together with the
+// ErrnoModeling checker and other checkers like StdCLibraryFunctions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ErrnoModeling.h"
+#include "clang/AST/ParentMapContext.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "llvm/ADT/STLExtras.h"
+#include <optional>
+
+using namespace clang;
+using namespace ento;
+using namespace errno_modeling;
+
+namespace {
+
+class ErrnoChecker
+ : public Checker<check::Location, check::PreCall, check::RegionChanges> {
+public:
+ void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
+ CheckerContext &) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ ProgramStateRef
+ checkRegionChanges(ProgramStateRef State,
+ const InvalidatedSymbols *Invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx, const CallEvent *Call) const;
+ void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const;
+
+ /// Indicates if a read (load) of \c errno is allowed in a non-condition part
+ /// of \c if, \c switch, loop and conditional statements when the errno
+ /// value may be undefined.
+ bool AllowErrnoReadOutsideConditions = true;
+
+private:
+ void generateErrnoNotCheckedBug(CheckerContext &C, ProgramStateRef State,
+ const MemRegion *ErrnoRegion,
+ const CallEvent *CallMayChangeErrno) const;
+
+ BugType BT_InvalidErrnoRead{this, "Value of 'errno' could be undefined",
+ "Error handling"};
+ BugType BT_ErrnoNotChecked{this, "Value of 'errno' was not checked",
+ "Error handling"};
+};
+
+} // namespace
+
+static ProgramStateRef setErrnoStateIrrelevant(ProgramStateRef State) {
+ return setErrnoState(State, Irrelevant);
+}
+
+/// Check if a statement (expression) or an ancestor of it is in a condition
+/// part of a (conditional, loop, switch) statement.
+static bool isInCondition(const Stmt *S, CheckerContext &C) {
+ ParentMapContext &ParentCtx = C.getASTContext().getParentMapContext();
+ bool CondFound = false;
+ while (S && !CondFound) {
+ const DynTypedNodeList Parents = ParentCtx.getParents(*S);
+ if (Parents.empty())
+ break;
+ const auto *ParentS = Parents[0].get<Stmt>();
+ if (!ParentS || isa<CallExpr>(ParentS))
+ break;
+ switch (ParentS->getStmtClass()) {
+ case Expr::IfStmtClass:
+ CondFound = (S == cast<IfStmt>(ParentS)->getCond());
+ break;
+ case Expr::ForStmtClass:
+ CondFound = (S == cast<ForStmt>(ParentS)->getCond());
+ break;
+ case Expr::DoStmtClass:
+ CondFound = (S == cast<DoStmt>(ParentS)->getCond());
+ break;
+ case Expr::WhileStmtClass:
+ CondFound = (S == cast<WhileStmt>(ParentS)->getCond());
+ break;
+ case Expr::SwitchStmtClass:
+ CondFound = (S == cast<SwitchStmt>(ParentS)->getCond());
+ break;
+ case Expr::ConditionalOperatorClass:
+ CondFound = (S == cast<ConditionalOperator>(ParentS)->getCond());
+ break;
+ case Expr::BinaryConditionalOperatorClass:
+ CondFound = (S == cast<BinaryConditionalOperator>(ParentS)->getCommon());
+ break;
+ default:
+ break;
+ }
+ S = ParentS;
+ }
+ return CondFound;
+}
+
+void ErrnoChecker::generateErrnoNotCheckedBug(
+ CheckerContext &C, ProgramStateRef State, const MemRegion *ErrnoRegion,
+ const CallEvent *CallMayChangeErrno) const {
+ if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
+ SmallString<100> StrBuf;
+ llvm::raw_svector_ostream OS(StrBuf);
+ if (CallMayChangeErrno) {
+ OS << "Value of 'errno' was not checked and may be overwritten by "
+ "function '";
+ const auto *CallD =
+ dyn_cast_or_null<FunctionDecl>(CallMayChangeErrno->getDecl());
+ assert(CallD && CallD->getIdentifier());
+ OS << CallD->getIdentifier()->getName() << "'";
+ } else {
+ OS << "Value of 'errno' was not checked and is overwritten here";
+ }
+ auto BR = std::make_unique<PathSensitiveBugReport>(BT_ErrnoNotChecked,
+ OS.str(), N);
+ BR->markInteresting(ErrnoRegion);
+ C.emitReport(std::move(BR));
+ }
+}
+
+void ErrnoChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
+ CheckerContext &C) const {
+ std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
+ if (!ErrnoLoc)
+ return;
+
+ auto L = Loc.getAs<ento::Loc>();
+ if (!L || *ErrnoLoc != *L)
+ return;
+
+ ProgramStateRef State = C.getState();
+ ErrnoCheckState EState = getErrnoState(State);
+
+ if (IsLoad) {
+ switch (EState) {
+ case MustNotBeChecked:
+ // Read of 'errno' when it may have undefined value.
+ if (!AllowErrnoReadOutsideConditions || isInCondition(S, C)) {
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ auto BR = std::make_unique<PathSensitiveBugReport>(
+ BT_InvalidErrnoRead,
+ "An undefined value may be read from 'errno'", N);
+ BR->markInteresting(ErrnoLoc->getAsRegion());
+ C.emitReport(std::move(BR));
+ }
+ }
+ break;
+ case MustBeChecked:
+ // 'errno' has to be checked. A load is required for this, with no more
+ // information we can assume that it is checked somehow.
+ // After this place 'errno' is allowed to be read and written.
+ State = setErrnoStateIrrelevant(State);
+ C.addTransition(State);
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (EState) {
+ case MustBeChecked:
+ // 'errno' is overwritten without a read before but it should have been
+ // checked.
+ generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(State),
+ ErrnoLoc->getAsRegion(), nullptr);
+ break;
+ case MustNotBeChecked:
+ // Write to 'errno' when it is not allowed to be read.
+ // After this place 'errno' is allowed to be read and written.
+ State = setErrnoStateIrrelevant(State);
+ C.addTransition(State);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void ErrnoChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *CallF = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!CallF)
+ return;
+
+ CallF = CallF->getCanonicalDecl();
+ // If 'errno' must be checked, it should be done as soon as possible, and
+ // before any other call to a system function (something in a system header).
+ // To avoid use of a long list of functions that may change 'errno'
+ // (which may be different with standard library versions) assume that any
+ // function can change it.
+ // A list of special functions can be used that are allowed here without
+ // generation of diagnostic. For now the only such case is 'errno' itself.
+ // Probably 'strerror'?
+ if (CallF->isExternC() && CallF->isGlobal() &&
+ C.getSourceManager().isInSystemHeader(CallF->getLocation()) &&
+ !isErrno(CallF)) {
+ if (getErrnoState(C.getState()) == MustBeChecked) {
+ std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
+ assert(ErrnoLoc && "ErrnoLoc should exist if an errno state is set.");
+ generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(C.getState()),
+ ErrnoLoc->getAsRegion(), &Call);
+ }
+ }
+}
+
+ProgramStateRef ErrnoChecker::checkRegionChanges(
+ ProgramStateRef State, const InvalidatedSymbols *Invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
+ const CallEvent *Call) const {
+ std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(State);
+ if (!ErrnoLoc)
+ return State;
+ const MemRegion *ErrnoRegion = ErrnoLoc->getAsRegion();
+
+ // If 'errno' is invalidated we can not know if it is checked or written into,
+ // allow read and write without bug reports.
+ if (llvm::is_contained(Regions, ErrnoRegion))
+ return clearErrnoState(State);
+
+ // Always reset errno state when the system memory space is invalidated.
+ // The ErrnoRegion is not always found in the list in this case.
+ if (llvm::is_contained(Regions, ErrnoRegion->getMemorySpace()))
+ return clearErrnoState(State);
+
+ return State;
+}
+
+void ento::registerErrnoChecker(CheckerManager &mgr) {
+ const AnalyzerOptions &Opts = mgr.getAnalyzerOptions();
+ auto *Checker = mgr.registerChecker<ErrnoChecker>();
+ Checker->AllowErrnoReadOutsideConditions = Opts.getCheckerBooleanOption(
+ Checker, "AllowErrnoReadOutsideConditionExpressions");
+}
+
+bool ento::shouldRegisterErrnoChecker(const CheckerManager &mgr) {
+ return true;
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
new file mode 100644
index 000000000000..1b34ea0e056e
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
@@ -0,0 +1,325 @@
+//=== ErrnoModeling.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 defines a checker `ErrnoModeling`, which is used to make the system
+// value 'errno' available to other checkers.
+// The 'errno' value is stored at a special memory region that is accessible
+// through the `errno_modeling` namespace. The memory region is either the
+// region of `errno` itself if it is a variable, otherwise an artifically
+// created region (in the system memory space). If `errno` is defined by using
+// a function which returns the address of it (this is always the case if it is
+// not a variable) this function is recognized and evaluated. In this way
+// `errno` becomes visible to the analysis and checkers can change its value.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ErrnoModeling.h"
+#include "clang/AST/ParentMapContext.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/FormatVariadic.h"
+#include <optional>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+// Name of the "errno" variable.
+// FIXME: Is there a system where it is not called "errno" but is a variable?
+const char *ErrnoVarName = "errno";
+// Names of functions that return a location of the "errno" value.
+// FIXME: Are there other similar function names?
+const char *ErrnoLocationFuncNames[] = {"__errno_location", "___errno",
+ "__errno", "_errno", "__error"};
+
+class ErrnoModeling
+ : public Checker<check::ASTDecl<TranslationUnitDecl>, check::BeginFunction,
+ check::LiveSymbols, eval::Call> {
+public:
+ void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &Mgr,
+ BugReporter &BR) const;
+ void checkBeginFunction(CheckerContext &C) const;
+ void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const;
+
+ // The declaration of an "errno" variable or "errno location" function.
+ mutable const Decl *ErrnoDecl = nullptr;
+
+private:
+ // FIXME: Names from `ErrnoLocationFuncNames` are used to build this set.
+ CallDescriptionSet ErrnoLocationCalls{{{"__errno_location"}, 0, 0},
+ {{"___errno"}, 0, 0},
+ {{"__errno"}, 0, 0},
+ {{"_errno"}, 0, 0},
+ {{"__error"}, 0, 0}};
+};
+
+} // namespace
+
+/// Store a MemRegion that contains the 'errno' integer value.
+/// The value is null if the 'errno' value was not recognized in the AST.
+REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion, const MemRegion *)
+
+REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoState, errno_modeling::ErrnoCheckState)
+
+/// Search for a variable called "errno" in the AST.
+/// Return nullptr if not found.
+static const VarDecl *getErrnoVar(ASTContext &ACtx) {
+ IdentifierInfo &II = ACtx.Idents.get(ErrnoVarName);
+ auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
+ auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) {
+ if (auto *VD = dyn_cast<VarDecl>(D))
+ return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) &&
+ VD->hasExternalStorage() &&
+ VD->getType().getCanonicalType() == ACtx.IntTy;
+ return false;
+ });
+ if (Found == LookupRes.end())
+ return nullptr;
+
+ return cast<VarDecl>(*Found);
+}
+
+/// Search for a function with a specific name that is used to return a pointer
+/// to "errno".
+/// Return nullptr if no such function was found.
+static const FunctionDecl *getErrnoFunc(ASTContext &ACtx) {
+ SmallVector<const Decl *> LookupRes;
+ for (StringRef ErrnoName : ErrnoLocationFuncNames) {
+ IdentifierInfo &II = ACtx.Idents.get(ErrnoName);
+ llvm::append_range(LookupRes, ACtx.getTranslationUnitDecl()->lookup(&II));
+ }
+
+ auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) {
+ if (auto *FD = dyn_cast<FunctionDecl>(D))
+ return ACtx.getSourceManager().isInSystemHeader(FD->getLocation()) &&
+ FD->isExternC() && FD->getNumParams() == 0 &&
+ FD->getReturnType().getCanonicalType() ==
+ ACtx.getPointerType(ACtx.IntTy);
+ return false;
+ });
+ if (Found == LookupRes.end())
+ return nullptr;
+
+ return cast<FunctionDecl>(*Found);
+}
+
+void ErrnoModeling::checkASTDecl(const TranslationUnitDecl *D,
+ AnalysisManager &Mgr, BugReporter &BR) const {
+ // Try to find an usable `errno` value.
+ // It can be an external variable called "errno" or a function that returns a
+ // pointer to the "errno" value. This function can have different names.
+ // The actual case is dependent on the C library implementation, we
+ // can only search for a match in one of these variations.
+ // We assume that exactly one of these cases might be true.
+ ErrnoDecl = getErrnoVar(Mgr.getASTContext());
+ if (!ErrnoDecl)
+ ErrnoDecl = getErrnoFunc(Mgr.getASTContext());
+}
+
+void ErrnoModeling::checkBeginFunction(CheckerContext &C) const {
+ if (!C.inTopFrame())
+ return;
+
+ ASTContext &ACtx = C.getASTContext();
+ ProgramStateRef State = C.getState();
+
+ if (const auto *ErrnoVar = dyn_cast_or_null<VarDecl>(ErrnoDecl)) {
+ // There is an external 'errno' variable.
+ // Use its memory region.
+ // The memory region for an 'errno'-like variable is allocated in system
+ // space by MemRegionManager.
+ const MemRegion *ErrnoR =
+ State->getRegion(ErrnoVar, C.getLocationContext());
+ assert(ErrnoR && "Memory region should exist for the 'errno' variable.");
+ State = State->set<ErrnoRegion>(ErrnoR);
+ State =
+ errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant);
+ C.addTransition(State);
+ } else if (ErrnoDecl) {
+ assert(isa<FunctionDecl>(ErrnoDecl) && "Invalid errno location function.");
+ // There is a function that returns the location of 'errno'.
+ // We must create a memory region for it in system space.
+ // Currently a symbolic region is used with an artifical symbol.
+ // FIXME: It is better to have a custom (new) kind of MemRegion for such
+ // cases.
+ SValBuilder &SVB = C.getSValBuilder();
+ MemRegionManager &RMgr = C.getStateManager().getRegionManager();
+
+ const MemSpaceRegion *GlobalSystemSpace =
+ RMgr.getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);
+
+ // Create an artifical symbol for the region.
+ // It is not possible to associate a statement or expression in this case.
+ const SymbolConjured *Sym = SVB.conjureSymbol(
+ nullptr, C.getLocationContext(),
+ ACtx.getLValueReferenceType(ACtx.IntTy), C.blockCount(), &ErrnoDecl);
+
+ // The symbolic region is untyped, create a typed sub-region in it.
+ // The ElementRegion is used to make the errno region a typed region.
+ const MemRegion *ErrnoR = RMgr.getElementRegion(
+ ACtx.IntTy, SVB.makeZeroArrayIndex(),
+ RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), C.getASTContext());
+ State = State->set<ErrnoRegion>(ErrnoR);
+ State =
+ errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant);
+ C.addTransition(State);
+ }
+}
+
+bool ErrnoModeling::evalCall(const CallEvent &Call, CheckerContext &C) const {
+ // Return location of "errno" at a call to an "errno address returning"
+ // function.
+ if (ErrnoLocationCalls.contains(Call)) {
+ ProgramStateRef State = C.getState();
+
+ const MemRegion *ErrnoR = State->get<ErrnoRegion>();
+ if (!ErrnoR)
+ return false;
+
+ State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
+ loc::MemRegionVal{ErrnoR});
+ C.addTransition(State);
+ return true;
+ }
+
+ return false;
+}
+
+void ErrnoModeling::checkLiveSymbols(ProgramStateRef State,
+ SymbolReaper &SR) const {
+ // The special errno region should never garbage collected.
+ if (const auto *ErrnoR = State->get<ErrnoRegion>())
+ SR.markLive(ErrnoR);
+}
+
+namespace clang {
+namespace ento {
+namespace errno_modeling {
+
+std::optional<SVal> getErrnoValue(ProgramStateRef State) {
+ const MemRegion *ErrnoR = State->get<ErrnoRegion>();
+ if (!ErrnoR)
+ return {};
+ QualType IntTy = State->getAnalysisManager().getASTContext().IntTy;
+ return State->getSVal(ErrnoR, IntTy);
+}
+
+ProgramStateRef setErrnoValue(ProgramStateRef State,
+ const LocationContext *LCtx, SVal Value,
+ ErrnoCheckState EState) {
+ const MemRegion *ErrnoR = State->get<ErrnoRegion>();
+ if (!ErrnoR)
+ return State;
+ // First set the errno value, the old state is still available at 'checkBind'
+ // or 'checkLocation' for errno value.
+ State = State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx);
+ return State->set<ErrnoState>(EState);
+}
+
+ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C,
+ uint64_t Value, ErrnoCheckState EState) {
+ const MemRegion *ErrnoR = State->get<ErrnoRegion>();
+ if (!ErrnoR)
+ return State;
+ State = State->bindLoc(
+ loc::MemRegionVal{ErrnoR},
+ C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy),
+ C.getLocationContext());
+ return State->set<ErrnoState>(EState);
+}
+
+std::optional<Loc> getErrnoLoc(ProgramStateRef State) {
+ const MemRegion *ErrnoR = State->get<ErrnoRegion>();
+ if (!ErrnoR)
+ return {};
+ return loc::MemRegionVal{ErrnoR};
+}
+
+ErrnoCheckState getErrnoState(ProgramStateRef State) {
+ return State->get<ErrnoState>();
+}
+
+ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) {
+ return State->set<ErrnoState>(EState);
+}
+
+ProgramStateRef clearErrnoState(ProgramStateRef State) {
+ return setErrnoState(State, Irrelevant);
+}
+
+bool isErrno(const Decl *D) {
+ if (const auto *VD = dyn_cast_or_null<VarDecl>(D))
+ if (const IdentifierInfo *II = VD->getIdentifier())
+ return II->getName() == ErrnoVarName;
+ if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D))
+ if (const IdentifierInfo *II = FD->getIdentifier())
+ return llvm::is_contained(ErrnoLocationFuncNames, II->getName());
+ return false;
+}
+
+const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) {
+ return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string {
+ const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>();
+ if (ErrnoR && BR.isInteresting(ErrnoR)) {
+ BR.markNotInteresting(ErrnoR);
+ return Message;
+ }
+ return "";
+ });
+}
+
+ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State,
+ CheckerContext &C) {
+ return setErrnoState(State, MustNotBeChecked);
+}
+
+ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C,
+ NonLoc ErrnoSym) {
+ SValBuilder &SVB = C.getSValBuilder();
+ NonLoc ZeroVal = SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>();
+ DefinedOrUnknownSVal Cond =
+ SVB.evalBinOp(State, BO_NE, ErrnoSym, ZeroVal, SVB.getConditionType())
+ .castAs<DefinedOrUnknownSVal>();
+ State = State->assume(Cond, true);
+ if (!State)
+ return nullptr;
+ return setErrnoValue(State, C.getLocationContext(), ErrnoSym, Irrelevant);
+}
+
+ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State,
+ CheckerContext &C,
+ const Expr *InvalE) {
+ const MemRegion *ErrnoR = State->get<ErrnoRegion>();
+ if (!ErrnoR)
+ return State;
+ State = State->invalidateRegions(ErrnoR, InvalE, C.blockCount(),
+ C.getLocationContext(), false);
+ if (!State)
+ return nullptr;
+ return setErrnoState(State, MustBeChecked);
+}
+
+} // namespace errno_modeling
+} // namespace ento
+} // namespace clang
+
+void ento::registerErrnoModeling(CheckerManager &mgr) {
+ mgr.registerChecker<ErrnoModeling>();
+}
+
+bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) {
+ return true;
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
new file mode 100644
index 000000000000..6b53572fe5e2
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
@@ -0,0 +1,110 @@
+//=== ErrnoModeling.h - Tracking value of 'errno'. -----------------*- 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 using the system value 'errno'.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ERRNOMODELING_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ERRNOMODELING_H
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include <optional>
+
+namespace clang {
+namespace ento {
+namespace errno_modeling {
+
+/// Describe how reads and writes of \c errno are handled by the checker.
+enum ErrnoCheckState : unsigned {
+ /// We do not know anything about 'errno'.
+ /// Read and write is always allowed.
+ Irrelevant = 0,
+
+ /// Value of 'errno' should be checked to find out if a previous function call
+ /// has failed.
+ /// When this state is set \c errno must be read by the program before a next
+ /// standard function call or other overwrite of \c errno follows, otherwise
+ /// a bug report is emitted.
+ MustBeChecked = 1,
+
+ /// Value of 'errno' is not allowed to be read, it can contain an unspecified
+ /// value.
+ /// When this state is set \c errno is not allowed to be read by the program
+ /// until it is overwritten or invalidated.
+ MustNotBeChecked = 2
+};
+
+/// Returns the value of 'errno', if 'errno' was found in the AST.
+std::optional<SVal> getErrnoValue(ProgramStateRef State);
+
+/// Returns the errno check state, \c Errno_Irrelevant if 'errno' was not found
+/// (this is not the only case for that value).
+ErrnoCheckState getErrnoState(ProgramStateRef State);
+
+/// Returns the location that points to the \c MemoryRegion where the 'errno'
+/// value is stored. Returns \c std::nullopt if 'errno' was not found. Otherwise
+/// it always returns a valid memory region in the system global memory space.
+std::optional<Loc> getErrnoLoc(ProgramStateRef State);
+
+/// Set value of 'errno' to any SVal, if possible.
+/// The errno check state is set always when the 'errno' value is set.
+ProgramStateRef setErrnoValue(ProgramStateRef State,
+ const LocationContext *LCtx, SVal Value,
+ ErrnoCheckState EState);
+
+/// Set value of 'errno' to a concrete (signed) integer, if possible.
+/// The errno check state is set always when the 'errno' value is set.
+ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C,
+ uint64_t Value, ErrnoCheckState EState);
+
+/// Set the errno check state, do not modify the errno value.
+ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState);
+
+/// Clear state of errno (make it irrelevant).
+ProgramStateRef clearErrnoState(ProgramStateRef State);
+
+/// Determine if a `Decl` node related to 'errno'.
+/// This is true if the declaration is the errno variable or a function
+/// that returns a pointer to the 'errno' value (usually the 'errno' macro is
+/// defined with this function). \p D is not required to be a canonical
+/// declaration.
+bool isErrno(const Decl *D);
+
+/// Create a NoteTag that displays the message if the 'errno' memory region is
+/// marked as interesting, and resets the interestingness.
+const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message);
+
+/// Set errno state for the common case when a standard function is successful.
+/// Set \c ErrnoCheckState to \c MustNotBeChecked (the \c errno value is not
+/// affected).
+ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State, CheckerContext &C);
+
+/// Set errno state for the common case when a standard function fails.
+/// Set \c errno value to be not equal to zero and \c ErrnoCheckState to
+/// \c Irrelevant . The irrelevant errno state ensures that no related bug
+/// report is emitted later and no note tag is needed.
+/// \arg \c ErrnoSym Value to be used for \c errno and constrained to be
+/// non-zero.
+ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C,
+ NonLoc ErrnoSym);
+
+/// Set errno state for the common case when a standard function indicates
+/// failure only by \c errno. Sets \c ErrnoCheckState to \c MustBeChecked, and
+/// invalidates the errno region (clear of previous value).
+/// \arg \c InvalE Expression that causes invalidation of \c errno.
+ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State,
+ CheckerContext &C, const Expr *InvalE);
+
+} // namespace errno_modeling
+} // namespace ento
+} // namespace clang
+
+#endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ERRNOMODELING_H
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
new file mode 100644
index 000000000000..c46ebee0c94f
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
@@ -0,0 +1,185 @@
+//=== ErrnoTesterChecker.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 defines ErrnoTesterChecker, which is used to test functionality of the
+// errno_check API.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ErrnoModeling.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include <optional>
+
+using namespace clang;
+using namespace ento;
+using namespace errno_modeling;
+
+namespace {
+
+class ErrnoTesterChecker : public Checker<eval::Call> {
+public:
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const;
+
+private:
+ /// Evaluate function \code void ErrnoTesterChecker_setErrno(int) \endcode.
+ /// Set value of \c errno to the argument.
+ static void evalSetErrno(CheckerContext &C, const CallEvent &Call);
+ /// Evaluate function \code int ErrnoTesterChecker_getErrno() \endcode.
+ /// Return the value of \c errno.
+ static void evalGetErrno(CheckerContext &C, const CallEvent &Call);
+ /// Evaluate function \code int ErrnoTesterChecker_setErrnoIfError() \endcode.
+ /// Simulate a standard library function tha returns 0 on success and 1 on
+ /// failure. On the success case \c errno is not allowed to be used (may be
+ /// undefined). On the failure case \c errno is set to a fixed value 11 and
+ /// is not needed to be checked.
+ static void evalSetErrnoIfError(CheckerContext &C, const CallEvent &Call);
+ /// Evaluate function \code int ErrnoTesterChecker_setErrnoIfErrorRange()
+ /// \endcode. Same as \c ErrnoTesterChecker_setErrnoIfError but \c errno is
+ /// set to a range (to be nonzero) at the failure case.
+ static void evalSetErrnoIfErrorRange(CheckerContext &C,
+ const CallEvent &Call);
+ /// Evaluate function \code int ErrnoTesterChecker_setErrnoCheckState()
+ /// \endcode. This function simulates the following:
+ /// - Return 0 and leave \c errno with undefined value.
+ /// This is the case of a successful standard function call.
+ /// For example if \c ftell returns not -1.
+ /// - Return 1 and sets \c errno to a specific error code (1).
+ /// This is the case of a failed standard function call.
+ /// The function indicates the failure by a special return value
+ /// that is returned only at failure.
+ /// \c errno can be checked but it is not required.
+ /// For example if \c ftell returns -1.
+ /// - Return 2 and may set errno to a value (actually it does not set it).
+ /// This is the case of a standard function call where the failure can only
+ /// be checked by reading from \c errno. The value of \c errno is changed by
+ /// the function only at failure, the user should set \c errno to 0 before
+ /// the call (\c ErrnoChecker does not check for this rule).
+ /// \c strtol is an example of this case, if it returns \c LONG_MIN (or
+ /// \c LONG_MAX). This case applies only if \c LONG_MIN or \c LONG_MAX is
+ /// returned, otherwise the first case in this list applies.
+ static void evalSetErrnoCheckState(CheckerContext &C, const CallEvent &Call);
+
+ using EvalFn = std::function<void(CheckerContext &, const CallEvent &)>;
+ const CallDescriptionMap<EvalFn> TestCalls{
+ {{{"ErrnoTesterChecker_setErrno"}, 1}, &ErrnoTesterChecker::evalSetErrno},
+ {{{"ErrnoTesterChecker_getErrno"}, 0}, &ErrnoTesterChecker::evalGetErrno},
+ {{{"ErrnoTesterChecker_setErrnoIfError"}, 0},
+ &ErrnoTesterChecker::evalSetErrnoIfError},
+ {{{"ErrnoTesterChecker_setErrnoIfErrorRange"}, 0},
+ &ErrnoTesterChecker::evalSetErrnoIfErrorRange},
+ {{{"ErrnoTesterChecker_setErrnoCheckState"}, 0},
+ &ErrnoTesterChecker::evalSetErrnoCheckState}};
+};
+
+} // namespace
+
+void ErrnoTesterChecker::evalSetErrno(CheckerContext &C,
+ const CallEvent &Call) {
+ C.addTransition(setErrnoValue(C.getState(), C.getLocationContext(),
+ Call.getArgSVal(0), Irrelevant));
+}
+
+void ErrnoTesterChecker::evalGetErrno(CheckerContext &C,
+ const CallEvent &Call) {
+ ProgramStateRef State = C.getState();
+
+ std::optional<SVal> ErrnoVal = getErrnoValue(State);
+ assert(ErrnoVal && "Errno value should be available.");
+ State =
+ State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal);
+
+ C.addTransition(State);
+}
+
+void ErrnoTesterChecker::evalSetErrnoIfError(CheckerContext &C,
+ const CallEvent &Call) {
+ ProgramStateRef State = C.getState();
+ SValBuilder &SVB = C.getSValBuilder();
+
+ ProgramStateRef StateSuccess = State->BindExpr(
+ Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
+ StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
+
+ ProgramStateRef StateFailure = State->BindExpr(
+ Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
+ StateFailure = setErrnoValue(StateFailure, C, 11, Irrelevant);
+
+ C.addTransition(StateSuccess);
+ C.addTransition(StateFailure);
+}
+
+void ErrnoTesterChecker::evalSetErrnoIfErrorRange(CheckerContext &C,
+ const CallEvent &Call) {
+ ProgramStateRef State = C.getState();
+ SValBuilder &SVB = C.getSValBuilder();
+
+ ProgramStateRef StateSuccess = State->BindExpr(
+ Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
+ StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
+
+ ProgramStateRef StateFailure = State->BindExpr(
+ Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
+ DefinedOrUnknownSVal ErrnoVal = SVB.conjureSymbolVal(
+ nullptr, Call.getOriginExpr(), C.getLocationContext(), C.blockCount());
+ StateFailure = StateFailure->assume(ErrnoVal, true);
+ assert(StateFailure && "Failed to assume on an initial value.");
+ StateFailure =
+ setErrnoValue(StateFailure, C.getLocationContext(), ErrnoVal, Irrelevant);
+
+ C.addTransition(StateSuccess);
+ C.addTransition(StateFailure);
+}
+
+void ErrnoTesterChecker::evalSetErrnoCheckState(CheckerContext &C,
+ const CallEvent &Call) {
+ ProgramStateRef State = C.getState();
+ SValBuilder &SVB = C.getSValBuilder();
+
+ ProgramStateRef StateSuccess = State->BindExpr(
+ Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true));
+ StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked);
+
+ ProgramStateRef StateFailure1 = State->BindExpr(
+ Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true));
+ StateFailure1 = setErrnoValue(StateFailure1, C, 1, Irrelevant);
+
+ ProgramStateRef StateFailure2 = State->BindExpr(
+ Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(2, true));
+ StateFailure2 = setErrnoValue(StateFailure2, C, 2, MustBeChecked);
+
+ C.addTransition(StateSuccess,
+ getErrnoNoteTag(C, "Assuming that this function succeeds but "
+ "sets 'errno' to an unspecified value."));
+ C.addTransition(StateFailure1);
+ C.addTransition(
+ StateFailure2,
+ getErrnoNoteTag(C, "Assuming that this function returns 2. 'errno' "
+ "should be checked to test for failure."));
+}
+
+bool ErrnoTesterChecker::evalCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const EvalFn *Fn = TestCalls.lookup(Call);
+ if (Fn) {
+ (*Fn)(C, Call);
+ return C.isDifferent();
+ }
+ return false;
+}
+
+void ento::registerErrnoTesterChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<ErrnoTesterChecker>();
+}
+
+bool ento::shouldRegisterErrnoTesterChecker(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 2ce1bef6d228..3096999e9fd1 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -6,10 +6,10 @@
//
//===----------------------------------------------------------------------===//
-#include "Taint.h"
#include "clang/Analysis/IssueHash.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Checkers/SValExplainer.h"
+#include "clang/StaticAnalyzer/Checkers/Taint.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/ScopedPrinter.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -24,7 +25,7 @@ using namespace ento;
namespace {
class ExprInspectionChecker
: public Checker<eval::Call, check::DeadSymbols, check::EndAnalysis> {
- mutable std::unique_ptr<BugType> BT;
+ const BugType BT{this, "Checking analyzer assumptions", "debug"};
// These stats are per-analysis, not per-branch, hence they shouldn't
// stay inside the program state.
@@ -40,6 +41,8 @@ class ExprInspectionChecker
void analyzerNumTimesReached(const CallExpr *CE, CheckerContext &C) const;
void analyzerCrash(const CallExpr *CE, CheckerContext &C) const;
void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerValue(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerDumpSValType(const CallExpr *CE, CheckerContext &C) const;
void analyzerDump(const CallExpr *CE, CheckerContext &C) const;
void analyzerExplain(const CallExpr *CE, CheckerContext &C) const;
void analyzerPrintState(const CallExpr *CE, CheckerContext &C) const;
@@ -56,9 +59,10 @@ class ExprInspectionChecker
// Optional parameter `ExprVal` for expression value to be marked interesting.
ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C,
- Optional<SVal> ExprVal = None) const;
+ std::optional<SVal> ExprVal = std::nullopt) const;
ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, ExplodedNode *N,
- Optional<SVal> ExprVal = None) const;
+ std::optional<SVal> ExprVal = std::nullopt) const;
+ template <typename T> void printAndReport(CheckerContext &C, T What) const;
const Expr *getArgExpr(const CallExpr *CE, CheckerContext &C) const;
const MemRegion *getArgRegion(const CallExpr *CE, CheckerContext &C) const;
@@ -98,6 +102,9 @@ bool ExprInspectionChecker::evalCall(const CallEvent &Call,
&ExprInspectionChecker::analyzerDumpExtent)
.Case("clang_analyzer_dumpElementCount",
&ExprInspectionChecker::analyzerDumpElementCount)
+ .Case("clang_analyzer_value", &ExprInspectionChecker::analyzerValue)
+ .StartsWith("clang_analyzer_dumpSvalType",
+ &ExprInspectionChecker::analyzerDumpSValType)
.StartsWith("clang_analyzer_dump",
&ExprInspectionChecker::analyzerDump)
.Case("clang_analyzer_getExtent",
@@ -109,7 +116,8 @@ bool ExprInspectionChecker::evalCall(const CallEvent &Call,
.Case("clang_analyzer_hashDump",
&ExprInspectionChecker::analyzerHashDump)
.Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote)
- .Case("clang_analyzer_express",
+ .Case("clang_analyzer_express", // This also marks the argument as
+ // interesting.
&ExprInspectionChecker::analyzerExpress)
.StartsWith("clang_analyzer_isTainted",
&ExprInspectionChecker::analyzerIsTainted)
@@ -154,24 +162,21 @@ static const char *getArgumentValueString(const CallExpr *CE,
}
}
-ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
- CheckerContext &C,
- Optional<SVal> ExprVal) const {
+ExplodedNode *
+ExprInspectionChecker::reportBug(llvm::StringRef Msg, CheckerContext &C,
+ std::optional<SVal> ExprVal) const {
ExplodedNode *N = C.generateNonFatalErrorNode();
reportBug(Msg, C.getBugReporter(), N, ExprVal);
return N;
}
-ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
- BugReporter &BR, ExplodedNode *N,
- Optional<SVal> ExprVal) const {
+ExplodedNode *
+ExprInspectionChecker::reportBug(llvm::StringRef Msg, BugReporter &BR,
+ ExplodedNode *N,
+ std::optional<SVal> ExprVal) const {
if (!N)
return nullptr;
-
- if (!BT)
- BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
-
- auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
if (ExprVal) {
R->markInteresting(*ExprVal);
}
@@ -255,6 +260,55 @@ void ExprInspectionChecker::analyzerExplain(const CallExpr *CE,
reportBug(Ex.Visit(V), C);
}
+static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C,
+ const llvm::APSInt &I) {
+ Out << I.getBitWidth() << (I.isUnsigned() ? "u:" : "s:");
+ Out << I;
+}
+
+static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C,
+ SymbolRef Sym) {
+ C.getConstraintManager().printValue(Out, C.getState(), Sym);
+}
+
+static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C,
+ SVal V) {
+ Out << V;
+}
+
+template <typename T>
+void ExprInspectionChecker::printAndReport(CheckerContext &C, T What) const {
+ llvm::SmallString<64> Str;
+ llvm::raw_svector_ostream OS(Str);
+ printHelper(OS, C, What);
+ reportBug(OS.str(), C);
+}
+
+void ExprInspectionChecker::analyzerValue(const CallExpr *CE,
+ CheckerContext &C) const {
+ const Expr *Arg = getArgExpr(CE, C);
+ if (!Arg)
+ return;
+
+ SVal V = C.getSVal(Arg);
+ if (const SymbolRef Sym = V.getAsSymbol())
+ printAndReport(C, Sym);
+ else if (const llvm::APSInt *I = V.getAsInteger())
+ printAndReport(C, *I);
+ else
+ reportBug("n/a", C);
+}
+
+void ExprInspectionChecker::analyzerDumpSValType(const CallExpr *CE,
+ CheckerContext &C) const {
+ const Expr *Arg = getArgExpr(CE, C);
+ if (!Arg)
+ return;
+
+ QualType Ty = C.getSVal(Arg).getType(C.getASTContext());
+ reportBug(Ty.getAsString(), C);
+}
+
void ExprInspectionChecker::analyzerDump(const CallExpr *CE,
CheckerContext &C) const {
const Expr *Arg = getArgExpr(CE, C);
@@ -262,21 +316,17 @@ void ExprInspectionChecker::analyzerDump(const CallExpr *CE,
return;
SVal V = C.getSVal(Arg);
-
- llvm::SmallString<32> Str;
- llvm::raw_svector_ostream OS(Str);
- V.dumpToStream(OS);
- reportBug(OS.str(), C);
+ printAndReport(C, V);
}
void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE,
CheckerContext &C) const {
- const MemRegion *MR = getArgRegion(CE, C);
- if (!MR)
+ const Expr *Arg = getArgExpr(CE, C);
+ if (!Arg)
return;
ProgramStateRef State = C.getState();
- DefinedOrUnknownSVal Size = getDynamicExtent(State, MR, C.getSValBuilder());
+ SVal Size = getDynamicExtentWithOffset(State, C.getSVal(Arg));
State = State->BindExpr(CE, C.getLocationContext(), Size);
C.addTransition(State);
@@ -284,17 +334,13 @@ void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE,
void ExprInspectionChecker::analyzerDumpExtent(const CallExpr *CE,
CheckerContext &C) const {
- const MemRegion *MR = getArgRegion(CE, C);
- if (!MR)
+ const Expr *Arg = getArgExpr(CE, C);
+ if (!Arg)
return;
- DefinedOrUnknownSVal Size =
- getDynamicExtent(C.getState(), MR, C.getSValBuilder());
-
- SmallString<64> Msg;
- llvm::raw_svector_ostream Out(Msg);
- Out << Size;
- reportBug(Out.str(), C);
+ ProgramStateRef State = C.getState();
+ SVal Size = getDynamicExtentWithOffset(State, C.getSVal(Arg));
+ printAndReport(C, Size);
}
void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE,
@@ -307,19 +353,14 @@ void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE,
if (const auto *TVR = MR->getAs<TypedValueRegion>()) {
ElementTy = TVR->getValueType();
} else {
- ElementTy =
- MR->castAs<SymbolicRegion>()->getSymbol()->getType()->getPointeeType();
+ ElementTy = MR->castAs<SymbolicRegion>()->getPointeeStaticType();
}
assert(!ElementTy->isPointerType());
- DefinedOrUnknownSVal ElementCount =
- getDynamicElementCount(C.getState(), MR, C.getSValBuilder(), ElementTy);
-
- SmallString<128> Msg;
- llvm::raw_svector_ostream Out(Msg);
- Out << ElementCount;
- reportBug(Out.str(), C);
+ DefinedOrUnknownSVal ElementCount = getDynamicElementCountWithOffset(
+ C.getState(), C.getSVal(getArgExpr(CE, C)), ElementTy);
+ printAndReport(C, ElementCount);
}
void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE,
@@ -348,8 +389,7 @@ void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper,
ProgramStateRef State = C.getState();
const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>();
ExplodedNode *N = C.getPredecessor();
- for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) {
- SymbolRef Sym = *I;
+ for (SymbolRef Sym : Syms) {
if (!SymReaper.isDead(Sym))
continue;
@@ -423,50 +463,60 @@ void ExprInspectionChecker::analyzerDenote(const CallExpr *CE,
namespace {
class SymbolExpressor
- : public SymExprVisitor<SymbolExpressor, Optional<std::string>> {
+ : public SymExprVisitor<SymbolExpressor, std::optional<std::string>> {
ProgramStateRef State;
public:
SymbolExpressor(ProgramStateRef State) : State(State) {}
- Optional<std::string> lookup(const SymExpr *S) {
+ std::optional<std::string> lookup(const SymExpr *S) {
if (const StringLiteral *const *SLPtr = State->get<DenotedSymbols>(S)) {
const StringLiteral *SL = *SLPtr;
return std::string(SL->getBytes());
}
- return None;
+ return std::nullopt;
}
- Optional<std::string> VisitSymExpr(const SymExpr *S) { return lookup(S); }
+ std::optional<std::string> VisitSymExpr(const SymExpr *S) {
+ return lookup(S);
+ }
- Optional<std::string> VisitSymIntExpr(const SymIntExpr *S) {
- if (Optional<std::string> Str = lookup(S))
+ std::optional<std::string> VisitSymIntExpr(const SymIntExpr *S) {
+ if (std::optional<std::string> Str = lookup(S))
return Str;
- if (Optional<std::string> Str = Visit(S->getLHS()))
+ if (std::optional<std::string> Str = Visit(S->getLHS()))
return (*Str + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " +
std::to_string(S->getRHS().getLimitedValue()) +
(S->getRHS().isUnsigned() ? "U" : ""))
.str();
- return None;
+ return std::nullopt;
}
- Optional<std::string> VisitSymSymExpr(const SymSymExpr *S) {
- if (Optional<std::string> Str = lookup(S))
+ std::optional<std::string> VisitSymSymExpr(const SymSymExpr *S) {
+ if (std::optional<std::string> Str = lookup(S))
return Str;
- if (Optional<std::string> Str1 = Visit(S->getLHS()))
- if (Optional<std::string> Str2 = Visit(S->getRHS()))
+ if (std::optional<std::string> Str1 = Visit(S->getLHS()))
+ if (std::optional<std::string> Str2 = Visit(S->getRHS()))
return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) +
" " + *Str2)
.str();
- return None;
+ return std::nullopt;
}
- Optional<std::string> VisitSymbolCast(const SymbolCast *S) {
- if (Optional<std::string> Str = lookup(S))
+ std::optional<std::string> VisitUnarySymExpr(const UnarySymExpr *S) {
+ if (std::optional<std::string> Str = lookup(S))
return Str;
- if (Optional<std::string> Str = Visit(S->getOperand()))
+ if (std::optional<std::string> Str = Visit(S->getOperand()))
+ return (UnaryOperator::getOpcodeStr(S->getOpcode()) + *Str).str();
+ return std::nullopt;
+ }
+
+ std::optional<std::string> VisitSymbolCast(const SymbolCast *S) {
+ if (std::optional<std::string> Str = lookup(S))
+ return Str;
+ if (std::optional<std::string> Str = Visit(S->getOperand()))
return (Twine("(") + S->getType().getAsString() + ")" + *Str).str();
- return None;
+ return std::nullopt;
}
};
} // namespace
@@ -480,14 +530,14 @@ void ExprInspectionChecker::analyzerExpress(const CallExpr *CE,
SVal ArgVal = C.getSVal(CE->getArg(0));
SymbolRef Sym = ArgVal.getAsSymbol();
if (!Sym) {
- reportBug("Not a symbol", C);
+ reportBug("Not a symbol", C, ArgVal);
return;
}
SymbolExpressor V(C.getState());
auto Str = V.Visit(Sym);
if (!Str) {
- reportBug("Unable to express", C);
+ reportBug("Unable to express", C, ArgVal);
return;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
index 6275e49e51ae..7aefcdc6d358 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
@@ -24,7 +24,7 @@ using namespace ento;
namespace {
class FixedAddressChecker
: public Checker< check::PreStmt<BinaryOperator> > {
- mutable std::unique_ptr<BuiltinBug> BT;
+ const BugType BT{this, "Use fixed address"};
public:
void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
@@ -49,14 +49,11 @@ void FixedAddressChecker::checkPreStmt(const BinaryOperator *B,
return;
if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
- if (!BT)
- BT.reset(
- new BuiltinBug(this, "Use fixed address",
- "Using a fixed address is not portable because that "
- "address will probably not be valid in all "
- "environments or platforms."));
- auto R =
- std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
+ // FIXME: improve grammar in the following strings:
+ constexpr llvm::StringLiteral Msg =
+ "Using a fixed address is not portable because that address will "
+ "probably not be valid in all environments or platforms.";
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
R->addRange(B->getRHS()->getSourceRange());
C.emitReport(std::move(R));
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
index e3f4be0726c8..079bc61a87d9 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
@@ -101,6 +101,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "llvm/ADT/StringExtras.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -254,9 +255,6 @@ static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym,
namespace {
class FuchsiaHandleSymbolVisitor final : public SymbolVisitor {
public:
- FuchsiaHandleSymbolVisitor(ProgramStateRef State) : State(std::move(State)) {}
- ProgramStateRef getState() const { return State; }
-
bool VisitSymbol(SymbolRef S) override {
if (const auto *HandleType = S->getType()->getAs<TypedefType>())
if (HandleType->getDecl()->getName() == HandleTypeName)
@@ -268,7 +266,6 @@ public:
private:
SmallVector<SymbolRef, 1024> Symbols;
- ProgramStateRef State;
};
} // end anonymous namespace
@@ -284,7 +281,7 @@ getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) {
if (QT->isStructureType()) {
// If we see a structure, see if there is any handle referenced by the
// structure.
- FuchsiaHandleSymbolVisitor Visitor(State);
+ FuchsiaHandleSymbolVisitor Visitor;
State->scanReachableSymbols(Arg, Visitor);
return Visitor.GetSymbols();
}
@@ -304,7 +301,7 @@ getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) {
}
} else {
assert(PtrToHandleLevel == 1);
- if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) {
+ if (std::optional<Loc> ArgLoc = Arg.getAs<Loc>()) {
SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol();
if (Sym) {
return {Sym};
@@ -384,12 +381,12 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
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)) {
+ if (PathBR->getInterestingnessKind(RetSym)) {
std::string SBuf;
llvm::raw_string_ostream OS(SBuf);
OS << "Function '" << FuncDecl->getDeclName()
<< "' returns an open handle";
- return OS.str();
+ return SBuf;
} else
return "";
});
@@ -400,12 +397,12 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
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)) {
+ if (PathBR->getInterestingnessKind(RetSym)) {
std::string SBuf;
llvm::raw_string_ostream OS(SBuf);
OS << "Function '" << FuncDecl->getDeclName()
<< "' returns an unowned handle";
- return OS.str();
+ return SBuf;
} else
return "";
});
@@ -434,12 +431,12 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
} else {
Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
- if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
+ if (PathBR->getInterestingnessKind(Handle)) {
std::string SBuf;
llvm::raw_string_ostream OS(SBuf);
OS << "Handle released through " << ParamDiagIdx
<< llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
- return OS.str();
+ return SBuf;
} else
return "";
});
@@ -448,12 +445,12 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
} else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
- if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
+ if (PathBR->getInterestingnessKind(Handle)) {
std::string SBuf;
llvm::raw_string_ostream OS(SBuf);
OS << "Handle allocated through " << ParamDiagIdx
<< llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
- return OS.str();
+ return SBuf;
} else
return "";
});
@@ -462,12 +459,12 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
} else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(PVD)) {
Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
- if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
+ if (PathBR->getInterestingnessKind(Handle)) {
std::string SBuf;
llvm::raw_string_ostream OS(SBuf);
OS << "Unowned handle allocated through " << ParamDiagIdx
<< llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
- return OS.str();
+ return SBuf;
} else
return "";
});
@@ -656,10 +653,11 @@ void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode,
if (Type.isSuppressOnSink()) {
const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C);
if (AcquireNode) {
+ const Stmt *S = AcquireNode->getStmtForDiagnostics();
+ assert(S && "Statement cannot be null.");
PathDiagnosticLocation LocUsedForUniqueing =
PathDiagnosticLocation::createBegin(
- AcquireNode->getStmtForDiagnostics(), C.getSourceManager(),
- AcquireNode->getLocationContext());
+ S, C.getSourceManager(), AcquireNode->getLocationContext());
R = std::make_unique<PathSensitiveBugReport>(
Type, Msg, ErrorNode, LocUsedForUniqueing,
@@ -689,11 +687,10 @@ void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State,
if (!StateMap.isEmpty()) {
Out << Sep << "FuchsiaHandleChecker :" << NL;
- for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E;
- ++I) {
- I.getKey()->dumpToStream(Out);
+ for (const auto &[Sym, HandleState] : StateMap) {
+ Sym->dumpToStream(Out);
Out << " : ";
- I.getData().dump(Out);
+ HandleState.dump(Out);
Out << NL;
}
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp
index 8e02ef74c668..5637941a58f0 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp
@@ -73,7 +73,7 @@ decltype(auto) bindAssignmentToDecl(const char *DeclName) {
static bool isTest(const Decl *D) {
if (const auto* ND = dyn_cast<NamedDecl>(D)) {
std::string DeclName = ND->getNameAsString();
- if (StringRef(DeclName).startswith("test"))
+ if (StringRef(DeclName).starts_with("test"))
return true;
}
if (const auto *OD = dyn_cast<ObjCMethodDecl>(D)) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
index 8d9afbe88aa8..6c32a8dec844 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
@@ -20,6 +20,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -91,11 +92,11 @@ using namespace ento;
namespace {
class GTestChecker : public Checker<check::PostCall> {
- mutable IdentifierInfo *AssertionResultII;
- mutable IdentifierInfo *SuccessII;
+ mutable IdentifierInfo *AssertionResultII = nullptr;
+ mutable IdentifierInfo *SuccessII = nullptr;
public:
- GTestChecker();
+ GTestChecker() = default;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
@@ -119,8 +120,6 @@ private:
};
} // End anonymous namespace.
-GTestChecker::GTestChecker() : AssertionResultII(nullptr), SuccessII(nullptr) {}
-
/// Model a call to an un-inlined AssertionResult(bool) or
/// AssertionResult(bool &, ...).
/// To do so, constrain the value of the newly-constructed instance's 'success_'
@@ -135,7 +134,7 @@ void GTestChecker::modelAssertionResultBoolConstructor(
SVal BooleanArgVal = Call->getArgSVal(0);
if (IsRef) {
// The argument is a reference, so load from it to get the boolean value.
- if (!BooleanArgVal.getAs<Loc>())
+ if (!isa<Loc>(BooleanArgVal))
return;
BooleanArgVal = C.getState()->getSVal(BooleanArgVal.castAs<Loc>());
}
@@ -258,9 +257,9 @@ SVal GTestChecker::getAssertionResultSuccessFieldValue(
if (!SuccessField)
return UnknownVal();
- Optional<Loc> FieldLoc =
+ std::optional<Loc> FieldLoc =
State->getLValue(SuccessField, Instance).getAs<Loc>();
- if (!FieldLoc.hasValue())
+ if (!FieldLoc)
return UnknownVal();
return State->getSVal(*FieldLoc);
@@ -270,20 +269,17 @@ SVal GTestChecker::getAssertionResultSuccessFieldValue(
ProgramStateRef GTestChecker::assumeValuesEqual(SVal Val1, SVal Val2,
ProgramStateRef State,
CheckerContext &C) {
- if (!Val1.getAs<DefinedOrUnknownSVal>() ||
- !Val2.getAs<DefinedOrUnknownSVal>())
+ auto DVal1 = Val1.getAs<DefinedOrUnknownSVal>();
+ auto DVal2 = Val2.getAs<DefinedOrUnknownSVal>();
+ if (!DVal1 || !DVal2)
return State;
auto ValuesEqual =
- C.getSValBuilder().evalEQ(State, Val1.castAs<DefinedOrUnknownSVal>(),
- Val2.castAs<DefinedOrUnknownSVal>());
-
- if (!ValuesEqual.getAs<DefinedSVal>())
+ C.getSValBuilder().evalEQ(State, *DVal1, *DVal2).getAs<DefinedSVal>();
+ if (!ValuesEqual)
return State;
- State = C.getConstraintManager().assume(
- State, ValuesEqual.castAs<DefinedSVal>(), true);
-
+ State = C.getConstraintManager().assume(State, *ValuesEqual, true);
return State;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
index 42c777eb2c52..4ceaf933d0bf 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -14,838 +14,1065 @@
//
//===----------------------------------------------------------------------===//
-#include "Taint.h"
#include "Yaml.h"
#include "clang/AST/Attr.h"
#include "clang/Basic/Builtins.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Checkers/Taint.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/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/YAMLTraits.h"
-#include <algorithm>
#include <limits>
#include <memory>
-#include <unordered_map>
+#include <optional>
#include <utility>
+#include <vector>
+
+#define DEBUG_TYPE "taint-checker"
using namespace clang;
using namespace ento;
using namespace taint;
+using llvm::ImmutableSet;
+
namespace {
-class GenericTaintChecker : public Checker<check::PreCall, check::PostCall> {
-public:
- static void *getTag() {
- static int Tag;
- return &Tag;
+
+class GenericTaintChecker;
+
+/// Check for CWE-134: Uncontrolled Format String.
+constexpr llvm::StringLiteral MsgUncontrolledFormatString =
+ "Untrusted data is used as a format string "
+ "(CWE-134: Uncontrolled Format String)";
+
+/// Check for:
+/// CERT/STR02-C. "Sanitize data passed to complex subsystems"
+/// CWE-78, "Failure to Sanitize Data into an OS Command"
+constexpr llvm::StringLiteral MsgSanitizeSystemArgs =
+ "Untrusted data is passed to a system call "
+ "(CERT/STR02-C. Sanitize data passed to complex subsystems)";
+
+/// Check if tainted data is used as a buffer size in strn.. functions,
+/// and allocators.
+constexpr llvm::StringLiteral MsgTaintedBufferSize =
+ "Untrusted data is used to specify the buffer size "
+ "(CERT/STR31-C. Guarantee that storage for strings has sufficient space "
+ "for character data and the null terminator)";
+
+/// Check if tainted data is used as a custom sink's parameter.
+constexpr llvm::StringLiteral MsgCustomSink =
+ "Untrusted data is passed to a user-defined sink";
+
+using ArgIdxTy = int;
+using ArgVecTy = llvm::SmallVector<ArgIdxTy, 2>;
+
+/// Denotes the return value.
+constexpr ArgIdxTy ReturnValueIndex{-1};
+
+static ArgIdxTy fromArgumentCount(unsigned Count) {
+ assert(Count <=
+ static_cast<std::size_t>(std::numeric_limits<ArgIdxTy>::max()) &&
+ "ArgIdxTy is not large enough to represent the number of arguments.");
+ return Count;
+}
+
+/// Check if the region the expression evaluates to is the standard input,
+/// and thus, is tainted.
+/// FIXME: Move this to Taint.cpp.
+bool isStdin(SVal Val, const ASTContext &ACtx) {
+ // FIXME: What if Val is NonParamVarRegion?
+
+ // The region should be symbolic, we do not know it's value.
+ const auto *SymReg = dyn_cast_or_null<SymbolicRegion>(Val.getAsRegion());
+ if (!SymReg)
+ return false;
+
+ // Get it's symbol and find the declaration region it's pointing to.
+ const auto *DeclReg =
+ dyn_cast_or_null<DeclRegion>(SymReg->getSymbol()->getOriginRegion());
+ if (!DeclReg)
+ return false;
+
+ // This region corresponds to a declaration, find out if it's a global/extern
+ // variable named stdin with the proper type.
+ if (const auto *D = dyn_cast_or_null<VarDecl>(DeclReg->getDecl())) {
+ D = D->getCanonicalDecl();
+ if (D->getName() == "stdin" && D->hasExternalStorage() && D->isExternC()) {
+ const QualType FILETy = ACtx.getFILEType().getCanonicalType();
+ const QualType Ty = D->getType().getCanonicalType();
+
+ if (Ty->isPointerType())
+ return Ty->getPointeeType() == FILETy;
+ }
}
+ return false;
+}
- void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
- void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+SVal getPointeeOf(ProgramStateRef State, Loc LValue) {
+ const QualType ArgTy = LValue.getType(State->getStateManager().getContext());
+ if (!ArgTy->isPointerType() || !ArgTy->getPointeeType()->isVoidType())
+ return State->getSVal(LValue);
- void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
- const char *Sep) const override;
+ // Do not dereference void pointers. Treat them as byte pointers instead.
+ // FIXME: we might want to consider more than just the first byte.
+ return State->getSVal(LValue, State->getStateManager().getContext().CharTy);
+}
- using ArgVector = SmallVector<unsigned, 2>;
- using SignedArgVector = SmallVector<int, 2>;
+/// Given a pointer/reference argument, return the value it refers to.
+std::optional<SVal> getPointeeOf(ProgramStateRef State, SVal Arg) {
+ if (auto LValue = Arg.getAs<Loc>())
+ return getPointeeOf(State, *LValue);
+ return std::nullopt;
+}
- enum class VariadicType { None, Src, Dst };
+/// Given a pointer, return the SVal of its pointee or if it is tainted,
+/// otherwise return the pointer's SVal if tainted.
+/// Also considers stdin as a taint source.
+std::optional<SVal> getTaintedPointeeOrPointer(ProgramStateRef State,
+ SVal Arg) {
+ if (auto Pointee = getPointeeOf(State, Arg))
+ if (isTainted(State, *Pointee)) // FIXME: isTainted(...) ? Pointee : None;
+ return Pointee;
+
+ if (isTainted(State, Arg))
+ return Arg;
+ return std::nullopt;
+}
- /// Used to parse the configuration file.
- struct TaintConfiguration {
- using NameScopeArgs = std::tuple<std::string, std::string, ArgVector>;
-
- struct Propagation {
- std::string Name;
- std::string Scope;
- ArgVector SrcArgs;
- SignedArgVector DstArgs;
- VariadicType VarType;
- unsigned VarIndex;
- };
-
- std::vector<Propagation> Propagations;
- std::vector<NameScopeArgs> Filters;
- std::vector<NameScopeArgs> Sinks;
-
- TaintConfiguration() = default;
- TaintConfiguration(const TaintConfiguration &) = default;
- TaintConfiguration(TaintConfiguration &&) = default;
- TaintConfiguration &operator=(const TaintConfiguration &) = default;
- TaintConfiguration &operator=(TaintConfiguration &&) = default;
- };
+bool isTaintedOrPointsToTainted(ProgramStateRef State, SVal ExprSVal) {
+ return getTaintedPointeeOrPointer(State, ExprSVal).has_value();
+}
- /// Convert SignedArgVector to ArgVector.
- ArgVector convertToArgVector(CheckerManager &Mgr, const std::string &Option,
- const SignedArgVector &Args);
+/// Helps in printing taint diagnostics.
+/// Marks the incoming parameters of a function interesting (to be printed)
+/// when the return value, or the outgoing parameters are tainted.
+const NoteTag *taintOriginTrackerTag(CheckerContext &C,
+ std::vector<SymbolRef> TaintedSymbols,
+ std::vector<ArgIdxTy> TaintedArgs,
+ const LocationContext *CallLocation) {
+ return C.getNoteTag([TaintedSymbols = std::move(TaintedSymbols),
+ TaintedArgs = std::move(TaintedArgs), CallLocation](
+ PathSensitiveBugReport &BR) -> std::string {
+ SmallString<256> Msg;
+ // We give diagnostics only for taint related reports
+ if (!BR.isInteresting(CallLocation) ||
+ BR.getBugType().getCategory() != categories::TaintedData) {
+ return "";
+ }
+ if (TaintedSymbols.empty())
+ return "Taint originated here";
- /// Parse the config.
- void parseConfiguration(CheckerManager &Mgr, const std::string &Option,
- TaintConfiguration &&Config);
+ for (auto Sym : TaintedSymbols) {
+ BR.markInteresting(Sym);
+ }
+ LLVM_DEBUG(for (auto Arg
+ : TaintedArgs) {
+ llvm::dbgs() << "Taint Propagated from argument " << Arg + 1 << "\n";
+ });
+ return "";
+ });
+}
+
+/// Helps in printing taint diagnostics.
+/// Marks the function interesting (to be printed)
+/// when the return value, or the outgoing parameters are tainted.
+const NoteTag *taintPropagationExplainerTag(
+ CheckerContext &C, std::vector<SymbolRef> TaintedSymbols,
+ std::vector<ArgIdxTy> TaintedArgs, const LocationContext *CallLocation) {
+ assert(TaintedSymbols.size() == TaintedArgs.size());
+ return C.getNoteTag([TaintedSymbols = std::move(TaintedSymbols),
+ TaintedArgs = std::move(TaintedArgs), CallLocation](
+ PathSensitiveBugReport &BR) -> std::string {
+ SmallString<256> Msg;
+ llvm::raw_svector_ostream Out(Msg);
+ // We give diagnostics only for taint related reports
+ if (TaintedSymbols.empty() ||
+ BR.getBugType().getCategory() != categories::TaintedData) {
+ return "";
+ }
+ int nofTaintedArgs = 0;
+ for (auto [Idx, Sym] : llvm::enumerate(TaintedSymbols)) {
+ if (BR.isInteresting(Sym)) {
+ BR.markInteresting(CallLocation);
+ if (TaintedArgs[Idx] != ReturnValueIndex) {
+ LLVM_DEBUG(llvm::dbgs() << "Taint Propagated to argument "
+ << TaintedArgs[Idx] + 1 << "\n");
+ if (nofTaintedArgs == 0)
+ Out << "Taint propagated to the ";
+ else
+ Out << ", ";
+ Out << TaintedArgs[Idx] + 1
+ << llvm::getOrdinalSuffix(TaintedArgs[Idx] + 1) << " argument";
+ nofTaintedArgs++;
+ } else {
+ LLVM_DEBUG(llvm::dbgs() << "Taint Propagated to return value.\n");
+ Out << "Taint propagated to the return value";
+ }
+ }
+ }
+ return std::string(Out.str());
+ });
+}
+
+/// ArgSet is used to describe arguments relevant for taint detection or
+/// taint application. A discrete set of argument indexes and a variadic
+/// argument list signified by a starting index are supported.
+class ArgSet {
+public:
+ ArgSet() = default;
+ ArgSet(ArgVecTy &&DiscreteArgs,
+ std::optional<ArgIdxTy> VariadicIndex = std::nullopt)
+ : DiscreteArgs(std::move(DiscreteArgs)),
+ VariadicIndex(std::move(VariadicIndex)) {}
+
+ bool contains(ArgIdxTy ArgIdx) const {
+ if (llvm::is_contained(DiscreteArgs, ArgIdx))
+ return true;
+
+ return VariadicIndex && ArgIdx >= *VariadicIndex;
+ }
- static const unsigned InvalidArgIndex{std::numeric_limits<unsigned>::max()};
- /// Denotes the return vale.
- static const unsigned ReturnValueIndex{std::numeric_limits<unsigned>::max() -
- 1};
+ bool isEmpty() const { return DiscreteArgs.empty() && !VariadicIndex; }
private:
- mutable std::unique_ptr<BugType> BT;
- void initBugType() const {
- if (!BT)
- BT = std::make_unique<BugType>(this, "Use of Untrusted Data",
- "Untrusted Data");
+ ArgVecTy DiscreteArgs;
+ std::optional<ArgIdxTy> VariadicIndex;
+};
+
+/// A struct used to specify taint propagation rules for a function.
+///
+/// If any of the possible taint source arguments is tainted, all of the
+/// destination arguments should also be tainted. If ReturnValueIndex is added
+/// to the dst list, the return value will be tainted.
+class GenericTaintRule {
+ /// Arguments which are taints sinks and should be checked, and a report
+ /// should be emitted if taint reaches these.
+ ArgSet SinkArgs;
+ /// Arguments which should be sanitized on function return.
+ ArgSet FilterArgs;
+ /// Arguments which can participate in taint propagation. If any of the
+ /// arguments in PropSrcArgs is tainted, all arguments in PropDstArgs should
+ /// be tainted.
+ ArgSet PropSrcArgs;
+ ArgSet PropDstArgs;
+
+ /// A message that explains why the call is sensitive to taint.
+ std::optional<StringRef> SinkMsg;
+
+ GenericTaintRule() = default;
+
+ GenericTaintRule(ArgSet &&Sink, ArgSet &&Filter, ArgSet &&Src, ArgSet &&Dst,
+ std::optional<StringRef> SinkMsg = std::nullopt)
+ : SinkArgs(std::move(Sink)), FilterArgs(std::move(Filter)),
+ PropSrcArgs(std::move(Src)), PropDstArgs(std::move(Dst)),
+ SinkMsg(SinkMsg) {}
+
+public:
+ /// Make a rule that reports a warning if taint reaches any of \p FilterArgs
+ /// arguments.
+ static GenericTaintRule Sink(ArgSet &&SinkArgs,
+ std::optional<StringRef> Msg = std::nullopt) {
+ return {std::move(SinkArgs), {}, {}, {}, Msg};
}
- struct FunctionData {
- FunctionData() = delete;
- FunctionData(const FunctionDecl *FDecl, StringRef Name,
- std::string FullName)
- : FDecl(FDecl), Name(Name), FullName(std::move(FullName)) {}
- FunctionData(const FunctionData &) = default;
- FunctionData(FunctionData &&) = default;
- FunctionData &operator=(const FunctionData &) = delete;
- FunctionData &operator=(FunctionData &&) = delete;
-
- static Optional<FunctionData> create(const CallEvent &Call,
- const CheckerContext &C) {
- if (!Call.getDecl())
- return None;
-
- const FunctionDecl *FDecl = Call.getDecl()->getAsFunction();
- if (!FDecl || (FDecl->getKind() != Decl::Function &&
- FDecl->getKind() != Decl::CXXMethod))
- return None;
-
- StringRef Name = C.getCalleeName(FDecl);
- std::string FullName = FDecl->getQualifiedNameAsString();
- if (Name.empty() || FullName.empty())
- return None;
-
- return FunctionData{FDecl, Name, std::move(FullName)};
- }
+ /// Make a rule that sanitizes all FilterArgs arguments.
+ static GenericTaintRule Filter(ArgSet &&FilterArgs) {
+ return {{}, std::move(FilterArgs), {}, {}};
+ }
- bool isInScope(StringRef Scope) const {
- return StringRef(FullName).startswith(Scope);
- }
+ /// Make a rule that unconditionally taints all Args.
+ /// If Func is provided, it must also return true for taint to propagate.
+ static GenericTaintRule Source(ArgSet &&SourceArgs) {
+ return {{}, {}, {}, std::move(SourceArgs)};
+ }
- const FunctionDecl *const FDecl;
- const StringRef Name;
- const std::string FullName;
- };
+ /// Make a rule that taints all PropDstArgs if any of PropSrcArgs is tainted.
+ static GenericTaintRule Prop(ArgSet &&SrcArgs, ArgSet &&DstArgs) {
+ return {{}, {}, std::move(SrcArgs), std::move(DstArgs)};
+ }
- /// Catch taint related bugs. Check if tainted data is passed to a
- /// system call etc. Returns true on matching.
- bool checkPre(const CallEvent &Call, const FunctionData &FData,
- CheckerContext &C) const;
+ /// Make a rule that taints all PropDstArgs if any of PropSrcArgs is tainted.
+ static GenericTaintRule
+ SinkProp(ArgSet &&SinkArgs, ArgSet &&SrcArgs, ArgSet &&DstArgs,
+ std::optional<StringRef> Msg = std::nullopt) {
+ return {
+ std::move(SinkArgs), {}, std::move(SrcArgs), std::move(DstArgs), Msg};
+ }
- /// Add taint sources on a pre-visit. Returns true on matching.
- bool addSourcesPre(const CallEvent &Call, const FunctionData &FData,
- CheckerContext &C) const;
+ /// Process a function which could either be a taint source, a taint sink, a
+ /// taint filter or a taint propagator.
+ void process(const GenericTaintChecker &Checker, const CallEvent &Call,
+ CheckerContext &C) const;
- /// Mark filter's arguments not tainted on a pre-visit. Returns true on
- /// matching.
- bool addFiltersPre(const CallEvent &Call, const FunctionData &FData,
- CheckerContext &C) const;
+ /// Handles the resolution of indexes of type ArgIdxTy to Expr*-s.
+ static const Expr *GetArgExpr(ArgIdxTy ArgIdx, const CallEvent &Call) {
+ return ArgIdx == ReturnValueIndex ? Call.getOriginExpr()
+ : Call.getArgExpr(ArgIdx);
+ };
- /// Propagate taint generated at pre-visit. Returns true on matching.
- static bool propagateFromPre(const CallEvent &Call, CheckerContext &C);
+ /// Functions for custom taintedness propagation.
+ static bool UntrustedEnv(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);
+using RuleLookupTy = CallDescriptionMap<GenericTaintRule>;
- /// Given a pointer argument, return the value it points to.
- static Optional<SVal> getPointeeOf(CheckerContext &C, const Expr *Arg);
+/// Used to parse the configuration file.
+struct TaintConfiguration {
+ using NameScopeArgs = std::tuple<std::string, std::string, ArgVecTy>;
+ enum class VariadicType { None, Src, Dst };
- /// 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 CallEvent &Call,
- CheckerContext &C) const;
+ struct Common {
+ std::string Name;
+ std::string Scope;
+ };
- /// Check for:
- /// CERT/STR02-C. "Sanitize data passed to complex subsystems"
- /// CWE-78, "Failure to Sanitize Data into an OS Command"
- 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 CallEvent &Call, StringRef Name,
- CheckerContext &C) const;
-
- /// Check if tainted data is used as a buffer size ins strn.. functions,
- /// and allocators.
- static constexpr llvm::StringLiteral MsgTaintedBufferSize =
- "Untrusted data is used to specify the buffer size "
- "(CERT/STR31-C. Guarantee that storage for strings has sufficient space "
- "for character data and the null terminator)";
- 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 CallEvent &Call, const FunctionData &FData,
- CheckerContext &C) const;
+ struct Sink : Common {
+ ArgVecTy SinkArgs;
+ };
- /// Generate a report if the expression is tainted or points to tainted data.
- bool generateReportIfTainted(const Expr *E, StringRef Msg,
- CheckerContext &C) const;
+ struct Filter : Common {
+ ArgVecTy FilterArgs;
+ };
- struct TaintPropagationRule;
- template <typename T>
- using ConfigDataMap =
- std::unordered_multimap<std::string, std::pair<std::string, T>>;
- using NameRuleMap = ConfigDataMap<TaintPropagationRule>;
- using NameArgMap = ConfigDataMap<ArgVector>;
-
- /// Find a function with the given name and scope. Returns the first match
- /// or the end of the map.
- template <typename T>
- static auto findFunctionInConfig(const ConfigDataMap<T> &Map,
- const FunctionData &FData);
-
- /// A struct used to specify taint propagation rules for a function.
- ///
- /// If any of the possible taint source arguments is tainted, all of the
- /// destination arguments should also be tainted. Use InvalidArgIndex in the
- /// src list to specify that all of the arguments can introduce taint. Use
- /// InvalidArgIndex in the dst arguments to signify that all the non-const
- /// pointer and reference arguments might be tainted on return. If
- /// ReturnValueIndex is added to the dst list, the return value will be
- /// tainted.
- struct TaintPropagationRule {
- using PropagationFuncType = bool (*)(bool IsTainted, const CallEvent &Call,
- CheckerContext &C);
-
- /// List of arguments which can be taint sources and should be checked.
- ArgVector SrcArgs;
- /// List of arguments which should be tainted on function return.
- ArgVector DstArgs;
- /// Index for the first variadic parameter if exist.
- unsigned VariadicIndex;
- /// Show when a function has variadic parameters. If it has, it marks all
- /// of them as source or destination.
+ struct Propagation : Common {
+ ArgVecTy SrcArgs;
+ ArgVecTy DstArgs;
VariadicType VarType;
- /// Special function for tainted source determination. If defined, it can
- /// override the default behavior.
- PropagationFuncType PropagationFunc;
-
- TaintPropagationRule()
- : VariadicIndex(InvalidArgIndex), VarType(VariadicType::None),
- PropagationFunc(nullptr) {}
-
- TaintPropagationRule(ArgVector &&Src, ArgVector &&Dst,
- VariadicType Var = VariadicType::None,
- unsigned VarIndex = InvalidArgIndex,
- PropagationFuncType Func = nullptr)
- : SrcArgs(std::move(Src)), DstArgs(std::move(Dst)),
- VariadicIndex(VarIndex), VarType(Var), PropagationFunc(Func) {}
-
- /// Get the propagation rule for a given function.
- static TaintPropagationRule
- getTaintPropagationRule(const NameRuleMap &CustomPropagations,
- const FunctionData &FData, CheckerContext &C);
-
- void addSrcArg(unsigned A) { SrcArgs.push_back(A); }
- void addDstArg(unsigned A) { DstArgs.push_back(A); }
-
- bool isNull() const {
- return SrcArgs.empty() && DstArgs.empty() &&
- VariadicType::None == VarType;
- }
+ ArgIdxTy VarIndex;
+ };
- bool isDestinationArgument(unsigned ArgNum) const {
- return (llvm::find(DstArgs, ArgNum) != DstArgs.end());
- }
+ std::vector<Propagation> Propagations;
+ std::vector<Filter> Filters;
+ std::vector<Sink> Sinks;
- static bool isTaintedOrPointsToTainted(const Expr *E,
- const ProgramStateRef &State,
- CheckerContext &C) {
- if (isTainted(State, E, C.getLocationContext()) || isStdin(E, C))
- return true;
+ TaintConfiguration() = default;
+ TaintConfiguration(const TaintConfiguration &) = default;
+ TaintConfiguration(TaintConfiguration &&) = default;
+ TaintConfiguration &operator=(const TaintConfiguration &) = default;
+ TaintConfiguration &operator=(TaintConfiguration &&) = default;
+};
- if (!E->getType().getTypePtr()->isPointerType())
- return false;
+struct GenericTaintRuleParser {
+ GenericTaintRuleParser(CheckerManager &Mgr) : Mgr(Mgr) {}
+ /// Container type used to gather call identification objects grouped into
+ /// pairs with their corresponding taint rules. It is temporary as it is used
+ /// to finally initialize RuleLookupTy, which is considered to be immutable.
+ using RulesContTy = std::vector<std::pair<CallDescription, GenericTaintRule>>;
+ RulesContTy parseConfiguration(const std::string &Option,
+ TaintConfiguration &&Config) const;
- Optional<SVal> V = getPointeeOf(C, E);
- return (V && isTainted(State, *V));
- }
+private:
+ using NamePartsTy = llvm::SmallVector<StringRef, 2>;
- /// Pre-process a function which propagates taint according to the
- /// taint rule.
- ProgramStateRef process(const CallEvent &Call, CheckerContext &C) const;
+ /// Validate part of the configuration, which contains a list of argument
+ /// indexes.
+ void validateArgVector(const std::string &Option, const ArgVecTy &Args) const;
- // Functions for custom taintedness propagation.
- static bool postSocket(bool IsTainted, const CallEvent &Call,
- CheckerContext &C);
- };
+ template <typename Config> static NamePartsTy parseNameParts(const Config &C);
- /// Defines a map between the propagation function's name, scope
- /// and TaintPropagationRule.
- NameRuleMap CustomPropagations;
+ // Takes the config and creates a CallDescription for it and associates a Rule
+ // with that.
+ template <typename Config>
+ static void consumeRulesFromConfig(const Config &C, GenericTaintRule &&Rule,
+ RulesContTy &Rules);
- /// Defines a map between the filter function's name, scope and filtering
- /// args.
- NameArgMap CustomFilters;
+ void parseConfig(const std::string &Option, TaintConfiguration::Sink &&P,
+ RulesContTy &Rules) const;
+ void parseConfig(const std::string &Option, TaintConfiguration::Filter &&P,
+ RulesContTy &Rules) const;
+ void parseConfig(const std::string &Option,
+ TaintConfiguration::Propagation &&P,
+ RulesContTy &Rules) const;
- /// Defines a map between the sink function's name, scope and sinking args.
- NameArgMap CustomSinks;
+ CheckerManager &Mgr;
};
-const unsigned GenericTaintChecker::ReturnValueIndex;
-const unsigned GenericTaintChecker::InvalidArgIndex;
+class GenericTaintChecker : public Checker<check::PreCall, check::PostCall> {
+public:
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
-// FIXME: these lines can be removed in C++17
-constexpr llvm::StringLiteral GenericTaintChecker::MsgUncontrolledFormatString;
-constexpr llvm::StringLiteral GenericTaintChecker::MsgSanitizeSystemArgs;
-constexpr llvm::StringLiteral GenericTaintChecker::MsgTaintedBufferSize;
-constexpr llvm::StringLiteral GenericTaintChecker::MsgCustomSink;
-} // end of anonymous namespace
+ void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+ const char *Sep) const override;
-using TaintConfig = GenericTaintChecker::TaintConfiguration;
+ /// Generate a report if the expression is tainted or points to tainted data.
+ bool generateReportIfTainted(const Expr *E, StringRef Msg,
+ CheckerContext &C) const;
-LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfig::Propagation)
-LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfig::NameScopeArgs)
+private:
+ const BugType BT{this, "Use of Untrusted Data", categories::TaintedData};
+
+ bool checkUncontrolledFormatString(const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void taintUnsafeSocketProtocol(const CallEvent &Call,
+ CheckerContext &C) const;
+
+ /// Default taint rules are initalized with the help of a CheckerContext to
+ /// access the names of built-in functions like memcpy.
+ void initTaintRules(CheckerContext &C) const;
+
+ /// CallDescription currently cannot restrict matches to the global namespace
+ /// only, which is why multiple CallDescriptionMaps are used, as we want to
+ /// disambiguate global C functions from functions inside user-defined
+ /// namespaces.
+ // TODO: Remove separation to simplify matching logic once CallDescriptions
+ // are more expressive.
+
+ mutable std::optional<RuleLookupTy> StaticTaintRules;
+ mutable std::optional<RuleLookupTy> DynamicTaintRules;
+};
+} // end of anonymous namespace
+
+/// YAML serialization mapping.
+LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfiguration::Sink)
+LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfiguration::Filter)
+LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfiguration::Propagation)
namespace llvm {
namespace yaml {
-template <> struct MappingTraits<TaintConfig> {
- static void mapping(IO &IO, TaintConfig &Config) {
+template <> struct MappingTraits<TaintConfiguration> {
+ static void mapping(IO &IO, TaintConfiguration &Config) {
IO.mapOptional("Propagations", Config.Propagations);
IO.mapOptional("Filters", Config.Filters);
IO.mapOptional("Sinks", Config.Sinks);
}
};
-template <> struct MappingTraits<TaintConfig::Propagation> {
- static void mapping(IO &IO, TaintConfig::Propagation &Propagation) {
+template <> struct MappingTraits<TaintConfiguration::Sink> {
+ static void mapping(IO &IO, TaintConfiguration::Sink &Sink) {
+ IO.mapRequired("Name", Sink.Name);
+ IO.mapOptional("Scope", Sink.Scope);
+ IO.mapRequired("Args", Sink.SinkArgs);
+ }
+};
+
+template <> struct MappingTraits<TaintConfiguration::Filter> {
+ static void mapping(IO &IO, TaintConfiguration::Filter &Filter) {
+ IO.mapRequired("Name", Filter.Name);
+ IO.mapOptional("Scope", Filter.Scope);
+ IO.mapRequired("Args", Filter.FilterArgs);
+ }
+};
+
+template <> struct MappingTraits<TaintConfiguration::Propagation> {
+ static void mapping(IO &IO, TaintConfiguration::Propagation &Propagation) {
IO.mapRequired("Name", Propagation.Name);
IO.mapOptional("Scope", Propagation.Scope);
IO.mapOptional("SrcArgs", Propagation.SrcArgs);
IO.mapOptional("DstArgs", Propagation.DstArgs);
- IO.mapOptional("VariadicType", Propagation.VarType,
- GenericTaintChecker::VariadicType::None);
- IO.mapOptional("VariadicIndex", Propagation.VarIndex,
- GenericTaintChecker::InvalidArgIndex);
+ IO.mapOptional("VariadicType", Propagation.VarType);
+ IO.mapOptional("VariadicIndex", Propagation.VarIndex);
}
};
-template <> struct ScalarEnumerationTraits<GenericTaintChecker::VariadicType> {
- static void enumeration(IO &IO, GenericTaintChecker::VariadicType &Value) {
- IO.enumCase(Value, "None", GenericTaintChecker::VariadicType::None);
- IO.enumCase(Value, "Src", GenericTaintChecker::VariadicType::Src);
- IO.enumCase(Value, "Dst", GenericTaintChecker::VariadicType::Dst);
- }
-};
-
-template <> struct MappingTraits<TaintConfig::NameScopeArgs> {
- static void mapping(IO &IO, TaintConfig::NameScopeArgs &NSA) {
- IO.mapRequired("Name", std::get<0>(NSA));
- IO.mapOptional("Scope", std::get<1>(NSA));
- IO.mapRequired("Args", std::get<2>(NSA));
+template <> struct ScalarEnumerationTraits<TaintConfiguration::VariadicType> {
+ static void enumeration(IO &IO, TaintConfiguration::VariadicType &Value) {
+ IO.enumCase(Value, "None", TaintConfiguration::VariadicType::None);
+ IO.enumCase(Value, "Src", TaintConfiguration::VariadicType::Src);
+ IO.enumCase(Value, "Dst", TaintConfiguration::VariadicType::Dst);
}
};
} // namespace yaml
} // namespace llvm
/// A set which is used to pass information from call pre-visit instruction
-/// to the call post-visit. The values are unsigned integers, which are either
+/// to the call post-visit. The values are signed integers, which are either
/// ReturnValueIndex, or indexes of the pointer/reference argument, which
/// points to data, which should be tainted on return.
-REGISTER_SET_WITH_PROGRAMSTATE(TaintArgsOnPostVisit, unsigned)
-
-GenericTaintChecker::ArgVector
-GenericTaintChecker::convertToArgVector(CheckerManager &Mgr,
- const std::string &Option,
- const SignedArgVector &Args) {
- ArgVector Result;
- for (int Arg : Args) {
- if (Arg == -1)
- Result.push_back(ReturnValueIndex);
- else if (Arg < -1) {
- Result.push_back(InvalidArgIndex);
+REGISTER_MAP_WITH_PROGRAMSTATE(TaintArgsOnPostVisit, const LocationContext *,
+ ImmutableSet<ArgIdxTy>)
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(ArgIdxFactory, ArgIdxTy)
+
+void GenericTaintRuleParser::validateArgVector(const std::string &Option,
+ const ArgVecTy &Args) const {
+ for (ArgIdxTy Arg : Args) {
+ if (Arg < ReturnValueIndex) {
Mgr.reportInvalidCheckerOptionValue(
- this, Option,
+ Mgr.getChecker<GenericTaintChecker>(), Option,
"an argument number for propagation rules greater or equal to -1");
- } else
- Result.push_back(static_cast<unsigned>(Arg));
+ }
}
- return Result;
}
-void GenericTaintChecker::parseConfiguration(CheckerManager &Mgr,
- const std::string &Option,
- TaintConfiguration &&Config) {
- for (auto &P : Config.Propagations) {
- GenericTaintChecker::CustomPropagations.emplace(
- P.Name,
- std::make_pair(P.Scope, TaintPropagationRule{
- std::move(P.SrcArgs),
- convertToArgVector(Mgr, Option, P.DstArgs),
- P.VarType, P.VarIndex}));
+template <typename Config>
+GenericTaintRuleParser::NamePartsTy
+GenericTaintRuleParser::parseNameParts(const Config &C) {
+ NamePartsTy NameParts;
+ if (!C.Scope.empty()) {
+ // If the Scope argument contains multiple "::" parts, those are considered
+ // namespace identifiers.
+ StringRef{C.Scope}.split(NameParts, "::", /*MaxSplit*/ -1,
+ /*KeepEmpty*/ false);
}
+ NameParts.emplace_back(C.Name);
+ return NameParts;
+}
- for (auto &F : Config.Filters) {
- GenericTaintChecker::CustomFilters.emplace(
- std::get<0>(F),
- std::make_pair(std::move(std::get<1>(F)), std::move(std::get<2>(F))));
- }
+template <typename Config>
+void GenericTaintRuleParser::consumeRulesFromConfig(const Config &C,
+ GenericTaintRule &&Rule,
+ RulesContTy &Rules) {
+ NamePartsTy NameParts = parseNameParts(C);
+ Rules.emplace_back(CallDescription(NameParts), std::move(Rule));
+}
- for (auto &S : Config.Sinks) {
- GenericTaintChecker::CustomSinks.emplace(
- std::get<0>(S),
- std::make_pair(std::move(std::get<1>(S)), std::move(std::get<2>(S))));
- }
+void GenericTaintRuleParser::parseConfig(const std::string &Option,
+ TaintConfiguration::Sink &&S,
+ RulesContTy &Rules) const {
+ validateArgVector(Option, S.SinkArgs);
+ consumeRulesFromConfig(S, GenericTaintRule::Sink(std::move(S.SinkArgs)),
+ Rules);
}
-template <typename T>
-auto GenericTaintChecker::findFunctionInConfig(const ConfigDataMap<T> &Map,
- const FunctionData &FData) {
- 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;
- StringRef Scope = Value.first;
- return Scope.empty() || FData.isInScope(Scope);
- });
- return It != Range.second ? It : Map.end();
+void GenericTaintRuleParser::parseConfig(const std::string &Option,
+ TaintConfiguration::Filter &&S,
+ RulesContTy &Rules) const {
+ validateArgVector(Option, S.FilterArgs);
+ consumeRulesFromConfig(S, GenericTaintRule::Filter(std::move(S.FilterArgs)),
+ Rules);
}
-GenericTaintChecker::TaintPropagationRule
-GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
- const NameRuleMap &CustomPropagations, const FunctionData &FData,
- CheckerContext &C) {
- // TODO: Currently, we might lose precision here: we always mark a return
- // value as tainted even if it's just a pointer, pointing to tainted data.
+void GenericTaintRuleParser::parseConfig(const std::string &Option,
+ TaintConfiguration::Propagation &&P,
+ RulesContTy &Rules) const {
+ validateArgVector(Option, P.SrcArgs);
+ validateArgVector(Option, P.DstArgs);
+ bool IsSrcVariadic = P.VarType == TaintConfiguration::VariadicType::Src;
+ bool IsDstVariadic = P.VarType == TaintConfiguration::VariadicType::Dst;
+ std::optional<ArgIdxTy> JustVarIndex = P.VarIndex;
+
+ ArgSet SrcDesc(std::move(P.SrcArgs),
+ IsSrcVariadic ? JustVarIndex : std::nullopt);
+ ArgSet DstDesc(std::move(P.DstArgs),
+ IsDstVariadic ? JustVarIndex : std::nullopt);
+
+ consumeRulesFromConfig(
+ P, GenericTaintRule::Prop(std::move(SrcDesc), std::move(DstDesc)), Rules);
+}
+
+GenericTaintRuleParser::RulesContTy
+GenericTaintRuleParser::parseConfiguration(const std::string &Option,
+ TaintConfiguration &&Config) const {
+
+ RulesContTy Rules;
+
+ for (auto &F : Config.Filters)
+ parseConfig(Option, std::move(F), Rules);
+
+ for (auto &S : Config.Sinks)
+ parseConfig(Option, std::move(S), Rules);
+ for (auto &P : Config.Propagations)
+ parseConfig(Option, std::move(P), Rules);
+
+ return Rules;
+}
+
+void GenericTaintChecker::initTaintRules(CheckerContext &C) const {
// Check for exact name match for functions without builtin substitutes.
// Use qualified name, because these are C functions without namespace.
- TaintPropagationRule Rule =
- llvm::StringSwitch<TaintPropagationRule>(FData.FullName)
- // Source functions
- // TODO: Add support for vfscanf & family.
- .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", {{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())) {
- switch (BId) {
- case Builtin::BImemcpy:
- case Builtin::BImemmove:
- case Builtin::BIstrncpy:
- case Builtin::BIstrncat:
- return {{1, 2}, {0, ReturnValueIndex}};
- case Builtin::BIstrlcpy:
- case Builtin::BIstrlcat:
- return {{1, 2}, {0}};
- case Builtin::BIstrndup:
- return {{0, 1}, {ReturnValueIndex}};
-
- default:
- break;
- }
+
+ if (StaticTaintRules || DynamicTaintRules)
+ return;
+
+ using RulesConstructionTy =
+ std::vector<std::pair<CallDescription, GenericTaintRule>>;
+ using TR = GenericTaintRule;
+
+ const Builtin::Context &BI = C.getASTContext().BuiltinInfo;
+
+ RulesConstructionTy GlobalCRules{
+ // Sources
+ {{{"fdopen"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"fopen"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"freopen"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"getch"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"getchar"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"getchar_unlocked"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"gets"}}, TR::Source({{0}, ReturnValueIndex})},
+ {{{"gets_s"}}, TR::Source({{0}, ReturnValueIndex})},
+ {{{"scanf"}}, TR::Source({{}, 1})},
+ {{{"scanf_s"}}, TR::Source({{}, {1}})},
+ {{{"wgetch"}}, TR::Source({{}, ReturnValueIndex})},
+ // Sometimes the line between taint sources and propagators is blurry.
+ // _IO_getc is choosen to be a source, but could also be a propagator.
+ // This way it is simpler, as modeling it as a propagator would require
+ // to model the possible sources of _IO_FILE * values, which the _IO_getc
+ // function takes as parameters.
+ {{{"_IO_getc"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"getcwd"}}, TR::Source({{0, ReturnValueIndex}})},
+ {{{"getwd"}}, TR::Source({{0, ReturnValueIndex}})},
+ {{{"readlink"}}, TR::Source({{1, ReturnValueIndex}})},
+ {{{"readlinkat"}}, TR::Source({{2, ReturnValueIndex}})},
+ {{{"get_current_dir_name"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"gethostname"}}, TR::Source({{0}})},
+ {{{"getnameinfo"}}, TR::Source({{2, 4}})},
+ {{{"getseuserbyname"}}, TR::Source({{1, 2}})},
+ {{{"getgroups"}}, TR::Source({{1, ReturnValueIndex}})},
+ {{{"getlogin"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"getlogin_r"}}, TR::Source({{0}})},
+
+ // Props
+ {{{"accept"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"atoi"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"atol"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"atoll"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"fgetc"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"fgetln"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"fgets"}}, TR::Prop({{2}}, {{0, ReturnValueIndex}})},
+ {{{"fgetws"}}, TR::Prop({{2}}, {{0, ReturnValueIndex}})},
+ {{{"fscanf"}}, TR::Prop({{0}}, {{}, 2})},
+ {{{"fscanf_s"}}, TR::Prop({{0}}, {{}, {2}})},
+ {{{"sscanf"}}, TR::Prop({{0}}, {{}, 2})},
+
+ {{{"getc"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"getc_unlocked"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"getdelim"}}, TR::Prop({{3}}, {{0}})},
+ {{{"getline"}}, TR::Prop({{2}}, {{0}})},
+ {{{"getw"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"pread"}}, TR::Prop({{0, 1, 2, 3}}, {{1, ReturnValueIndex}})},
+ {{{"read"}}, TR::Prop({{0, 2}}, {{1, ReturnValueIndex}})},
+ {{{"strchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"strrchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"tolower"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"toupper"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"fread"}}, TR::Prop({{3}}, {{0, ReturnValueIndex}})},
+ {{{"recv"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
+ {{{"recvfrom"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
+
+ {{{"ttyname"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"ttyname_r"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
+
+ {{{"basename"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"dirname"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"fnmatch"}}, TR::Prop({{1}}, {{ReturnValueIndex}})},
+ {{{"memchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"memrchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"rawmemchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+
+ {{{"mbtowc"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})},
+ {{{"wctomb"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})},
+ {{{"wcwidth"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+
+ {{{"memcmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})},
+ {{{"memcpy"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})},
+ {{{"memmove"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})},
+ // If memmem was called with a tainted needle and the search was
+ // successful, that would mean that the value pointed by the return value
+ // has the same content as the needle. If we choose to go by the policy of
+ // content equivalence implies taintedness equivalence, that would mean
+ // haystack should be considered a propagation source argument.
+ {{{"memmem"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+
+ // The comment for memmem above also applies to strstr.
+ {{{"strstr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"strcasestr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+
+ {{{"strchrnul"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+
+ {{{"index"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"rindex"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+
+ // FIXME: In case of arrays, only the first element of the array gets
+ // tainted.
+ {{{"qsort"}}, TR::Prop({{0}}, {{0}})},
+ {{{"qsort_r"}}, TR::Prop({{0}}, {{0}})},
+
+ {{{"strcmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})},
+ {{{"strcasecmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})},
+ {{{"strncmp"}}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})},
+ {{{"strncasecmp"}}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})},
+ {{{"strspn"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})},
+ {{{"strcspn"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})},
+ {{{"strpbrk"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"strndup"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"strndupa"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+
+ // strlen, wcslen, strnlen and alike intentionally don't propagate taint.
+ // See the details here: https://github.com/llvm/llvm-project/pull/66086
+
+ {{{"strtol"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
+ {{{"strtoll"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
+ {{{"strtoul"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
+ {{{"strtoull"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
+
+ {{{"isalnum"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isalpha"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isascii"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isblank"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"iscntrl"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isdigit"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isgraph"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"islower"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isprint"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"ispunct"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isspace"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isupper"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isxdigit"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+
+ {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrncat)}},
+ TR::Prop({{1, 2}}, {{0, ReturnValueIndex}})},
+ {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrlcpy)}},
+ TR::Prop({{1, 2}}, {{0}})},
+ {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrlcat)}},
+ TR::Prop({{1, 2}}, {{0}})},
+ {{CDF_MaybeBuiltin, {{"snprintf"}}},
+ TR::Prop({{1}, 3}, {{0, ReturnValueIndex}})},
+ {{CDF_MaybeBuiltin, {{"sprintf"}}},
+ TR::Prop({{1}, 2}, {{0, ReturnValueIndex}})},
+ {{CDF_MaybeBuiltin, {{"strcpy"}}},
+ TR::Prop({{1}}, {{0, ReturnValueIndex}})},
+ {{CDF_MaybeBuiltin, {{"stpcpy"}}},
+ TR::Prop({{1}}, {{0, ReturnValueIndex}})},
+ {{CDF_MaybeBuiltin, {{"strcat"}}},
+ TR::Prop({{1}}, {{0, ReturnValueIndex}})},
+ {{CDF_MaybeBuiltin, {{"wcsncat"}}},
+ TR::Prop({{1}}, {{0, ReturnValueIndex}})},
+ {{CDF_MaybeBuiltin, {{"strdup"}}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{CDF_MaybeBuiltin, {{"strdupa"}}},
+ TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{CDF_MaybeBuiltin, {{"wcsdup"}}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+
+ // Sinks
+ {{{"system"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+ {{{"popen"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+ {{{"execl"}}, TR::Sink({{}, {0}}, MsgSanitizeSystemArgs)},
+ {{{"execle"}}, TR::Sink({{}, {0}}, MsgSanitizeSystemArgs)},
+ {{{"execlp"}}, TR::Sink({{}, {0}}, MsgSanitizeSystemArgs)},
+ {{{"execv"}}, TR::Sink({{0, 1}}, MsgSanitizeSystemArgs)},
+ {{{"execve"}}, TR::Sink({{0, 1, 2}}, MsgSanitizeSystemArgs)},
+ {{{"fexecve"}}, TR::Sink({{0, 1, 2}}, MsgSanitizeSystemArgs)},
+ {{{"execvp"}}, TR::Sink({{0, 1}}, MsgSanitizeSystemArgs)},
+ {{{"execvpe"}}, TR::Sink({{0, 1, 2}}, MsgSanitizeSystemArgs)},
+ {{{"dlopen"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+ {{CDF_MaybeBuiltin, {{"malloc"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)},
+ {{CDF_MaybeBuiltin, {{"calloc"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)},
+ {{CDF_MaybeBuiltin, {{"alloca"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)},
+ {{CDF_MaybeBuiltin, {{"memccpy"}}},
+ TR::Sink({{3}}, MsgTaintedBufferSize)},
+ {{CDF_MaybeBuiltin, {{"realloc"}}},
+ TR::Sink({{1}}, MsgTaintedBufferSize)},
+ {{{{"setproctitle"}}}, TR::Sink({{0}, 1}, MsgUncontrolledFormatString)},
+ {{{{"setproctitle_fast"}}},
+ TR::Sink({{0}, 1}, MsgUncontrolledFormatString)},
+
+ // SinkProps
+ {{CDF_MaybeBuiltin, BI.getName(Builtin::BImemcpy)},
+ TR::SinkProp({{2}}, {{1, 2}}, {{0, ReturnValueIndex}},
+ MsgTaintedBufferSize)},
+ {{CDF_MaybeBuiltin, {BI.getName(Builtin::BImemmove)}},
+ TR::SinkProp({{2}}, {{1, 2}}, {{0, ReturnValueIndex}},
+ MsgTaintedBufferSize)},
+ {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrncpy)}},
+ TR::SinkProp({{2}}, {{1, 2}}, {{0, ReturnValueIndex}},
+ MsgTaintedBufferSize)},
+ {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrndup)}},
+ TR::SinkProp({{1}}, {{0, 1}}, {{ReturnValueIndex}},
+ MsgTaintedBufferSize)},
+ {{CDF_MaybeBuiltin, {{"bcopy"}}},
+ TR::SinkProp({{2}}, {{0, 2}}, {{1}}, MsgTaintedBufferSize)}};
+
+ // `getenv` returns taint only in untrusted environments.
+ if (TR::UntrustedEnv(C)) {
+ // void setproctitle_init(int argc, char *argv[], char *envp[])
+ GlobalCRules.push_back(
+ {{{"setproctitle_init"}}, TR::Sink({{1, 2}}, MsgCustomSink)});
+ GlobalCRules.push_back({{{"getenv"}}, TR::Source({{ReturnValueIndex}})});
}
- // Process all other functions which could be defined as builtins.
- if (Rule.isNull()) {
- 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}};
+ StaticTaintRules.emplace(std::make_move_iterator(GlobalCRules.begin()),
+ std::make_move_iterator(GlobalCRules.end()));
+
+ // User-provided taint configuration.
+ CheckerManager *Mgr = C.getAnalysisManager().getCheckerManager();
+ assert(Mgr);
+ GenericTaintRuleParser ConfigParser{*Mgr};
+ std::string Option{"Config"};
+ StringRef ConfigFile =
+ Mgr->getAnalyzerOptions().getCheckerStringOption(this, Option);
+ std::optional<TaintConfiguration> Config =
+ getConfiguration<TaintConfiguration>(*Mgr, this, Option, ConfigFile);
+ if (!Config) {
+ // We don't have external taint config, no parsing required.
+ DynamicTaintRules = RuleLookupTy{};
+ return;
}
- // Skipping the following functions, since they might be used for cleansing or
- // smart memory copy:
- // - memccpy - copying until hitting a special character.
+ GenericTaintRuleParser::RulesContTy Rules{
+ ConfigParser.parseConfiguration(Option, std::move(*Config))};
- auto It = findFunctionInConfig(CustomPropagations, FData);
- if (It != CustomPropagations.end())
- return It->second.second;
- return {};
+ DynamicTaintRules.emplace(std::make_move_iterator(Rules.begin()),
+ std::make_move_iterator(Rules.end()));
}
void GenericTaintChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
- 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(Call, *FData, C))
- return;
-
- // Marks the function's arguments and/or return value tainted if it present in
- // the list.
- if (addSourcesPre(Call, *FData, C))
- return;
-
- addFiltersPre(Call, *FData, C);
+ initTaintRules(C);
+
+ // FIXME: this should be much simpler.
+ if (const auto *Rule =
+ Call.isGlobalCFunction() ? StaticTaintRules->lookup(Call) : nullptr)
+ Rule->process(*this, Call, C);
+ else if (const auto *Rule = DynamicTaintRules->lookup(Call))
+ Rule->process(*this, Call, C);
+
+ // FIXME: These edge cases are to be eliminated from here eventually.
+ //
+ // Additional check that is not supported by CallDescription.
+ // TODO: Make CallDescription be able to match attributes such as printf-like
+ // arguments.
+ checkUncontrolledFormatString(Call, C);
+
+ // TODO: Modeling sockets should be done in a specific checker.
+ // Socket is a source, which taints the return value.
+ taintUnsafeSocketProtocol(Call, C);
}
void GenericTaintChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
// Set the marked values as tainted. The return value only accessible from
// checkPostStmt.
- propagateFromPre(Call, C);
-}
-
-void GenericTaintChecker::printState(raw_ostream &Out, ProgramStateRef State,
- const char *NL, const char *Sep) const {
- printTaint(State, Out, NL, Sep);
-}
-
-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(Call, C);
- if (State) {
- C.addTransition(State);
- return true;
- }
- }
- return false;
-}
-
-bool GenericTaintChecker::addFiltersPre(const CallEvent &Call,
- const FunctionData &FData,
- CheckerContext &C) const {
- auto It = findFunctionInConfig(CustomFilters, FData);
- if (It == CustomFilters.end())
- return false;
-
- ProgramStateRef State = C.getState();
- const auto &Value = It->second;
- const ArgVector &Args = Value.second;
- for (unsigned ArgNum : Args) {
- if (ArgNum >= Call.getNumArgs())
- continue;
-
- const Expr *Arg = Call.getArgExpr(ArgNum);
- Optional<SVal> V = getPointeeOf(C, Arg);
- if (V)
- State = removeTaint(State, *V);
- }
-
- if (State != C.getState()) {
- C.addTransition(State);
- return true;
- }
- return false;
-}
-
-bool GenericTaintChecker::propagateFromPre(const CallEvent &Call,
- CheckerContext &C) {
ProgramStateRef State = C.getState();
+ const StackFrameContext *CurrentFrame = C.getStackFrame();
// Depending on what was tainted at pre-visit, we determined a set of
// arguments which should be tainted after the function returns. These are
// stored in the state as TaintArgsOnPostVisit set.
- TaintArgsOnPostVisitTy TaintArgs = State->get<TaintArgsOnPostVisit>();
- if (TaintArgs.isEmpty())
- return false;
+ TaintArgsOnPostVisitTy TaintArgsMap = State->get<TaintArgsOnPostVisit>();
- for (unsigned ArgNum : TaintArgs) {
+ const ImmutableSet<ArgIdxTy> *TaintArgs = TaintArgsMap.lookup(CurrentFrame);
+ if (!TaintArgs)
+ return;
+ assert(!TaintArgs->isEmpty());
+
+ LLVM_DEBUG(for (ArgIdxTy I
+ : *TaintArgs) {
+ llvm::dbgs() << "PostCall<";
+ Call.dump(llvm::dbgs());
+ llvm::dbgs() << "> actually wants to taint arg index: " << I << '\n';
+ });
+
+ const NoteTag *InjectionTag = nullptr;
+ std::vector<SymbolRef> TaintedSymbols;
+ std::vector<ArgIdxTy> TaintedIndexes;
+ for (ArgIdxTy ArgNum : *TaintArgs) {
// Special handling for the tainted return value.
if (ArgNum == ReturnValueIndex) {
State = addTaint(State, Call.getReturnValue());
+ std::vector<SymbolRef> TaintedSyms =
+ getTaintedSymbols(State, Call.getReturnValue());
+ if (!TaintedSyms.empty()) {
+ TaintedSymbols.push_back(TaintedSyms[0]);
+ TaintedIndexes.push_back(ArgNum);
+ }
continue;
}
-
// The arguments are pointer arguments. The data they are pointing at is
// tainted after the call.
- if (Call.getNumArgs() < (ArgNum + 1))
- return false;
- const Expr *Arg = Call.getArgExpr(ArgNum);
- Optional<SVal> V = getPointeeOf(C, Arg);
- if (V)
+ if (auto V = getPointeeOf(State, Call.getArgSVal(ArgNum))) {
State = addTaint(State, *V);
+ std::vector<SymbolRef> TaintedSyms = getTaintedSymbols(State, *V);
+ if (!TaintedSyms.empty()) {
+ TaintedSymbols.push_back(TaintedSyms[0]);
+ TaintedIndexes.push_back(ArgNum);
+ }
+ }
}
-
+ // Create a NoteTag callback, which prints to the user where the taintedness
+ // was propagated to.
+ InjectionTag = taintPropagationExplainerTag(C, TaintedSymbols, TaintedIndexes,
+ Call.getCalleeStackFrame(0));
// Clear up the taint info from the state.
- State = State->remove<TaintArgsOnPostVisit>();
-
- if (State != C.getState()) {
- C.addTransition(State);
- return true;
- }
- return false;
-}
-
-bool GenericTaintChecker::checkPre(const CallEvent &Call,
- const FunctionData &FData,
- CheckerContext &C) const {
- if (checkUncontrolledFormatString(Call, C))
- return true;
-
- if (checkSystemCall(Call, FData.Name, C))
- return true;
-
- if (checkTaintedBufferSize(Call, C))
- return true;
-
- return checkCustomSinks(Call, FData, C);
+ State = State->remove<TaintArgsOnPostVisit>(CurrentFrame);
+ C.addTransition(State, InjectionTag);
}
-Optional<SVal> GenericTaintChecker::getPointeeOf(CheckerContext &C,
- const Expr *Arg) {
- ProgramStateRef State = C.getState();
- SVal AddrVal = C.getSVal(Arg->IgnoreParens());
- if (AddrVal.isUnknownOrUndef())
- return None;
-
- Optional<Loc> AddrLoc = AddrVal.getAs<Loc>();
- if (!AddrLoc)
- return None;
-
- QualType ArgTy = Arg->getType().getCanonicalType();
- if (!ArgTy->isPointerType())
- return State->getSVal(*AddrLoc);
-
- QualType ValTy = ArgTy->getPointeeType();
-
- // Do not dereference void pointers. Treat them as byte pointers instead.
- // FIXME: we might want to consider more than just the first byte.
- if (ValTy->isVoidType())
- ValTy = C.getASTContext().CharTy;
-
- return State->getSVal(*AddrLoc, ValTy);
+void GenericTaintChecker::printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const {
+ printTaint(State, Out, NL, Sep);
}
-ProgramStateRef
-GenericTaintChecker::TaintPropagationRule::process(const CallEvent &Call,
- CheckerContext &C) const {
+void GenericTaintRule::process(const GenericTaintChecker &Checker,
+ const CallEvent &Call, CheckerContext &C) const {
ProgramStateRef State = C.getState();
+ const ArgIdxTy CallNumArgs = fromArgumentCount(Call.getNumArgs());
- // Check for taint in arguments.
- bool IsTainted = true;
- for (unsigned ArgNum : SrcArgs) {
- if (ArgNum >= Call.getNumArgs())
- continue;
-
- 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 < Call.getNumArgs(); ++i) {
- if ((IsTainted =
- isTaintedOrPointsToTainted(Call.getArgExpr(i), State, C)))
- break;
+ /// Iterate every call argument, and get their corresponding Expr and SVal.
+ const auto ForEachCallArg = [&C, &Call, CallNumArgs](auto &&Fun) {
+ for (ArgIdxTy I = ReturnValueIndex; I < CallNumArgs; ++I) {
+ const Expr *E = GetArgExpr(I, Call);
+ Fun(I, E, C.getSVal(E));
}
- }
+ };
- if (PropagationFunc)
- IsTainted = PropagationFunc(IsTainted, Call, C);
+ /// Check for taint sinks.
+ ForEachCallArg([this, &Checker, &C, &State](ArgIdxTy I, const Expr *E, SVal) {
+ // Add taintedness to stdin parameters
+ if (isStdin(C.getSVal(E), C.getASTContext())) {
+ State = addTaint(State, C.getSVal(E));
+ }
+ if (SinkArgs.contains(I) && isTaintedOrPointsToTainted(State, C.getSVal(E)))
+ Checker.generateReportIfTainted(E, SinkMsg.value_or(MsgCustomSink), C);
+ });
+
+ /// Check for taint filters.
+ ForEachCallArg([this, &State](ArgIdxTy I, const Expr *E, SVal S) {
+ if (FilterArgs.contains(I)) {
+ State = removeTaint(State, S);
+ if (auto P = getPointeeOf(State, S))
+ State = removeTaint(State, *P);
+ }
+ });
+
+ /// Check for taint propagation sources.
+ /// A rule will make the destination variables tainted if PropSrcArgs
+ /// is empty (taints the destination
+ /// arguments unconditionally), or if any of its signified
+ /// args are tainted in context of the current CallEvent.
+ bool IsMatching = PropSrcArgs.isEmpty();
+ std::vector<SymbolRef> TaintedSymbols;
+ std::vector<ArgIdxTy> TaintedIndexes;
+ ForEachCallArg([this, &C, &IsMatching, &State, &TaintedSymbols,
+ &TaintedIndexes](ArgIdxTy I, const Expr *E, SVal) {
+ std::optional<SVal> TaintedSVal =
+ getTaintedPointeeOrPointer(State, C.getSVal(E));
+ IsMatching =
+ IsMatching || (PropSrcArgs.contains(I) && TaintedSVal.has_value());
+
+ // We track back tainted arguments except for stdin
+ if (TaintedSVal && !isStdin(*TaintedSVal, C.getASTContext())) {
+ std::vector<SymbolRef> TaintedArgSyms =
+ getTaintedSymbols(State, *TaintedSVal);
+ if (!TaintedArgSyms.empty()) {
+ llvm::append_range(TaintedSymbols, TaintedArgSyms);
+ TaintedIndexes.push_back(I);
+ }
+ }
+ });
- if (!IsTainted)
- return State;
+ // Early return for propagation rules which dont match.
+ // Matching propagations, Sinks and Filters will pass this point.
+ if (!IsMatching)
+ return;
- // Mark the arguments which should be tainted after the function returns.
- for (unsigned ArgNum : DstArgs) {
- // Should mark the return value?
- if (ArgNum == ReturnValueIndex) {
- State = State->add<TaintArgsOnPostVisit>(ReturnValueIndex);
- continue;
- }
+ const auto WouldEscape = [](SVal V, QualType Ty) -> bool {
+ if (!isa<Loc>(V))
+ return false;
- if (ArgNum >= Call.getNumArgs())
- continue;
+ const bool IsNonConstRef = Ty->isReferenceType() && !Ty.isConstQualified();
+ const bool IsNonConstPtr =
+ Ty->isPointerType() && !Ty->getPointeeType().isConstQualified();
- // Mark the given argument.
- State = State->add<TaintArgsOnPostVisit>(ArgNum);
- }
+ return IsNonConstRef || IsNonConstPtr;
+ };
- // Mark all variadic arguments tainted if present.
- if (VariadicType::Dst == VarType) {
- // For all pointer and references that were passed in:
- // If they are not pointing to const data, mark data as tainted.
- // TODO: So far we are just going one level down; ideally we'd need to
- // recurse here.
- for (unsigned 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())) {
- State = State->add<TaintArgsOnPostVisit>(i);
- }
- }
- }
+ /// Propagate taint where it is necessary.
+ auto &F = State->getStateManager().get_context<ArgIdxFactory>();
+ ImmutableSet<ArgIdxTy> Result = F.getEmptySet();
+ ForEachCallArg(
+ [&](ArgIdxTy I, const Expr *E, SVal V) {
+ if (PropDstArgs.contains(I)) {
+ LLVM_DEBUG(llvm::dbgs() << "PreCall<"; Call.dump(llvm::dbgs());
+ llvm::dbgs()
+ << "> prepares tainting arg index: " << I << '\n';);
+ Result = F.add(Result, I);
+ }
+
+ // Taint property gets lost if the variable is passed as a
+ // non-const pointer or reference to a function which is
+ // not inlined. For matching rules we want to preserve the taintedness.
+ // TODO: We should traverse all reachable memory regions via the
+ // escaping parameter. Instead of doing that we simply mark only the
+ // referred memory region as tainted.
+ if (WouldEscape(V, E->getType()) && getTaintedPointeeOrPointer(State, V)) {
+ LLVM_DEBUG(if (!Result.contains(I)) {
+ llvm::dbgs() << "PreCall<";
+ Call.dump(llvm::dbgs());
+ llvm::dbgs() << "> prepares tainting arg index: " << I << '\n';
+ });
+ Result = F.add(Result, I);
+ }
+ });
- return State;
+ if (!Result.isEmpty())
+ State = State->set<TaintArgsOnPostVisit>(C.getStackFrame(), Result);
+ const NoteTag *InjectionTag = taintOriginTrackerTag(
+ C, std::move(TaintedSymbols), std::move(TaintedIndexes),
+ Call.getCalleeStackFrame(0));
+ C.addTransition(State, InjectionTag);
}
-// If argument 0(protocol domain) is network, the return value should get taint.
-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;
+bool GenericTaintRule::UntrustedEnv(CheckerContext &C) {
+ return !C.getAnalysisManager()
+ .getAnalyzerOptions()
+ .ShouldAssumeControlledEnvironment;
}
-bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) {
- ProgramStateRef State = C.getState();
- SVal Val = C.getSVal(E);
-
- // stdin is a pointer, so it would be a region.
- const MemRegion *MemReg = Val.getAsRegion();
-
- // The region should be symbolic, we do not know it's value.
- const auto *SymReg = dyn_cast_or_null<SymbolicRegion>(MemReg);
- if (!SymReg)
- return false;
+bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg,
+ CheckerContext &C) const {
+ assert(E);
+ std::optional<SVal> TaintedSVal =
+ getTaintedPointeeOrPointer(C.getState(), C.getSVal(E));
- // Get it's symbol and find the declaration region it's pointing to.
- const auto *Sm = dyn_cast<SymbolRegionValue>(SymReg->getSymbol());
- if (!Sm)
- return false;
- const auto *DeclReg = dyn_cast_or_null<DeclRegion>(Sm->getRegion());
- if (!DeclReg)
+ if (!TaintedSVal)
return false;
- // This region corresponds to a declaration, find out if it's a global/extern
- // variable named stdin with the proper type.
- if (const auto *D = dyn_cast_or_null<VarDecl>(DeclReg->getDecl())) {
- D = D->getCanonicalDecl();
- if ((D->getName().find("stdin") != StringRef::npos) && D->isExternC()) {
- const auto *PtrTy = dyn_cast<PointerType>(D->getType().getTypePtr());
- if (PtrTy && PtrTy->getPointeeType().getCanonicalType() ==
- C.getASTContext().getFILEType().getCanonicalType())
- return true;
+ // Generate diagnostic.
+ if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+ auto report = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
+ report->addRange(E->getSourceRange());
+ for (auto TaintedSym : getTaintedSymbols(C.getState(), *TaintedSVal)) {
+ report->markInteresting(TaintedSym);
}
+
+ C.emitReport(std::move(report));
+ return true;
}
return false;
}
+/// TODO: remove checking for printf format attributes and socket whitelisting
+/// from GenericTaintChecker, and that means the following functions:
+/// getPrintfFormatArgumentNum,
+/// GenericTaintChecker::checkUncontrolledFormatString,
+/// GenericTaintChecker::taintUnsafeSocketProtocol
+
static bool getPrintfFormatArgumentNum(const CallEvent &Call,
const CheckerContext &C,
- unsigned &ArgNum) {
+ ArgIdxTy &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 = Call.getDecl()->getAsFunction();
+ const Decl *CallDecl = Call.getDecl();
+ if (!CallDecl)
+ return false;
+ const FunctionDecl *FDecl = CallDecl->getAsFunction();
if (!FDecl)
return false;
+
+ const ArgIdxTy CallNumArgs = fromArgumentCount(Call.getNumArgs());
+
for (const auto *Format : FDecl->specific_attrs<FormatAttr>()) {
ArgNum = Format->getFormatIdx() - 1;
- if ((Format->getType()->getName() == "printf") &&
- Call.getNumArgs() > ArgNum)
+ if ((Format->getType()->getName() == "printf") && CallNumArgs > ArgNum)
return true;
}
- // Or if a function is named setproctitle (this is a heuristic).
- if (C.getCalleeName(FDecl).find("setproctitle") != StringRef::npos) {
- ArgNum = 0;
- return true;
- }
-
- return false;
-}
-
-bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg,
- CheckerContext &C) const {
- assert(E);
-
- // Check for taint.
- ProgramStateRef State = C.getState();
- Optional<SVal> PointedToSVal = getPointeeOf(C, E);
- SVal TaintedSVal;
- if (PointedToSVal && isTainted(State, *PointedToSVal))
- TaintedSVal = *PointedToSVal;
- else if (isTainted(State, E, C.getLocationContext()))
- TaintedSVal = C.getSVal(E);
- else
- return false;
-
- // Generate diagnostic.
- if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
- initBugType();
- auto report = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
- report->addRange(E->getSourceRange());
- report->addVisitor(std::make_unique<TaintBugVisitor>(TaintedSVal));
- C.emitReport(std::move(report));
- return true;
- }
return false;
}
bool GenericTaintChecker::checkUncontrolledFormatString(
const CallEvent &Call, CheckerContext &C) const {
// Check if the function contains a format string argument.
- unsigned ArgNum = 0;
+ ArgIdxTy ArgNum = 0;
if (!getPrintfFormatArgumentNum(Call, C, ArgNum))
return false;
@@ -855,102 +1082,35 @@ bool GenericTaintChecker::checkUncontrolledFormatString(
MsgUncontrolledFormatString, C);
}
-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
- // need to know if the user was reset before these calls(seteuid).
- unsigned ArgNum = llvm::StringSwitch<unsigned>(Name)
- .Case("system", 0)
- .Case("popen", 0)
- .Case("execl", 0)
- .Case("execle", 0)
- .Case("execlp", 0)
- .Case("execv", 0)
- .Case("execvp", 0)
- .Case("execvP", 0)
- .Case("execve", 0)
- .Case("dlopen", 0)
- .Default(InvalidArgIndex);
-
- if (ArgNum == InvalidArgIndex || Call.getNumArgs() < (ArgNum + 1))
- return false;
-
- 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 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())) {
- switch (BId) {
- case Builtin::BImemcpy:
- case Builtin::BImemmove:
- case Builtin::BIstrncpy:
- ArgNum = 2;
- break;
- case Builtin::BIstrndup:
- ArgNum = 1;
- break;
- default:
- break;
- }
- }
-
- if (ArgNum == InvalidArgIndex) {
- using CCtx = CheckerContext;
- if (CCtx::isCLibraryFunction(FDecl, "malloc") ||
- CCtx::isCLibraryFunction(FDecl, "calloc") ||
- CCtx::isCLibraryFunction(FDecl, "alloca"))
- ArgNum = 0;
- else if (CCtx::isCLibraryFunction(FDecl, "memccpy"))
- ArgNum = 3;
- else if (CCtx::isCLibraryFunction(FDecl, "realloc"))
- ArgNum = 1;
- else if (CCtx::isCLibraryFunction(FDecl, "bcopy"))
- ArgNum = 2;
- }
-
- return ArgNum != InvalidArgIndex && Call.getNumArgs() > ArgNum &&
- generateReportIfTainted(Call.getArgExpr(ArgNum), MsgTaintedBufferSize,
- C);
-}
-
-bool GenericTaintChecker::checkCustomSinks(const CallEvent &Call,
- const FunctionData &FData,
- CheckerContext &C) const {
- auto It = findFunctionInConfig(CustomSinks, FData);
- if (It == CustomSinks.end())
- return false;
-
- const auto &Value = It->second;
- const GenericTaintChecker::ArgVector &Args = Value.second;
- for (unsigned ArgNum : Args) {
- if (ArgNum >= Call.getNumArgs())
- continue;
+void GenericTaintChecker::taintUnsafeSocketProtocol(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (Call.getNumArgs() < 1)
+ return;
+ const IdentifierInfo *ID = Call.getCalleeIdentifier();
+ if (!ID)
+ return;
+ if (!ID->getName().equals("socket"))
+ return;
- if (generateReportIfTainted(Call.getArgExpr(ArgNum), MsgCustomSink, C))
- return true;
- }
+ SourceLocation DomLoc = Call.getArgExpr(0)->getExprLoc();
+ StringRef DomName = C.getMacroNameOrSpelling(DomLoc);
+ // Allow internal communication protocols.
+ bool SafeProtocol = DomName.equals("AF_SYSTEM") ||
+ DomName.equals("AF_LOCAL") || DomName.equals("AF_UNIX") ||
+ DomName.equals("AF_RESERVED_36");
+ if (SafeProtocol)
+ return;
- return false;
+ ProgramStateRef State = C.getState();
+ auto &F = State->getStateManager().get_context<ArgIdxFactory>();
+ ImmutableSet<ArgIdxTy> Result = F.add(F.getEmptySet(), ReturnValueIndex);
+ State = State->set<TaintArgsOnPostVisit>(C.getStackFrame(), Result);
+ C.addTransition(State);
}
+/// Checker registration
void ento::registerGenericTaintChecker(CheckerManager &Mgr) {
- auto *Checker = Mgr.registerChecker<GenericTaintChecker>();
- std::string Option{"Config"};
- StringRef ConfigFile =
- Mgr.getAnalyzerOptions().getCheckerStringOption(Checker, Option);
- llvm::Optional<TaintConfig> Config =
- getConfiguration<TaintConfig>(Mgr, Checker, Option, ConfigFile);
- if (Config)
- Checker->parseConfiguration(Mgr, Option, std::move(Config.getValue()));
+ Mgr.registerChecker<GenericTaintChecker>();
}
bool ento::shouldRegisterGenericTaintChecker(const CheckerManager &mgr) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
index bcae73378028..b673b51c4623 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -13,11 +13,12 @@
//===----------------------------------------------------------------------===//
#include "AllocationState.h"
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "InterCheckerAPI.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/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -34,9 +35,9 @@ namespace {
class InnerPointerChecker
: public Checker<check::DeadSymbols, check::PostCall> {
- CallDescription AppendFn, AssignFn, AddressofFn, ClearFn, CStrFn, DataFn,
- DataMemberFn, EraseFn, InsertFn, PopBackFn, PushBackFn, ReplaceFn,
- ReserveFn, ResizeFn, ShrinkToFitFn, SwapFn;
+ CallDescription AppendFn, AssignFn, AddressofFn, AddressofFn_, ClearFn,
+ CStrFn, DataFn, DataMemberFn, EraseFn, InsertFn, PopBackFn, PushBackFn,
+ ReplaceFn, ReserveFn, ResizeFn, ShrinkToFitFn, SwapFn;
public:
class InnerPointerBRVisitor : public BugReporterVisitor {
@@ -54,9 +55,9 @@ public:
ID.AddPointer(getTag());
}
- virtual PathDiagnosticPieceRef
- VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
- PathSensitiveBugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
// FIXME: Scan the map once in the visitor's constructor and do a direct
// lookup by region.
@@ -73,7 +74,7 @@ public:
InnerPointerChecker()
: AppendFn({"std", "basic_string", "append"}),
AssignFn({"std", "basic_string", "assign"}),
- AddressofFn({"std", "addressof"}),
+ AddressofFn({"std", "addressof"}), AddressofFn_({"std", "__addressof"}),
ClearFn({"std", "basic_string", "clear"}),
CStrFn({"std", "basic_string", "c_str"}), DataFn({"std", "data"}, 1),
DataMemberFn({"std", "basic_string", "data"}),
@@ -125,19 +126,15 @@ bool InnerPointerChecker::isInvalidatingMemberFunction(
return true;
return false;
}
- return (isa<CXXDestructorCall>(Call) || Call.isCalled(AppendFn) ||
- Call.isCalled(AssignFn) || Call.isCalled(ClearFn) ||
- Call.isCalled(EraseFn) || Call.isCalled(InsertFn) ||
- Call.isCalled(PopBackFn) || Call.isCalled(PushBackFn) ||
- Call.isCalled(ReplaceFn) || Call.isCalled(ReserveFn) ||
- Call.isCalled(ResizeFn) || Call.isCalled(ShrinkToFitFn) ||
- Call.isCalled(SwapFn));
+ return isa<CXXDestructorCall>(Call) ||
+ matchesAny(Call, AppendFn, AssignFn, ClearFn, EraseFn, InsertFn,
+ PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn,
+ ShrinkToFitFn, SwapFn);
}
bool InnerPointerChecker::isInnerPointerAccessFunction(
const CallEvent &Call) const {
- return (Call.isCalled(CStrFn) || Call.isCalled(DataFn) ||
- Call.isCalled(DataMemberFn));
+ return matchesAny(Call, CStrFn, DataFn, DataMemberFn);
}
void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call,
@@ -182,9 +179,9 @@ void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call,
if (!ArgRegion)
continue;
- // std::addressof function accepts a non-const reference as an argument,
+ // std::addressof functions accepts a non-const reference as an argument,
// but doesn't modify it.
- if (Call.isCalled(AddressofFn))
+ if (matchesAny(Call, AddressofFn, AddressofFn_))
continue;
markPtrSymbolsReleased(Call, State, ArgRegion, C);
@@ -323,8 +320,7 @@ PathDiagnosticPieceRef InnerPointerChecker::InnerPointerBRVisitor::VisitNode(
SmallString<256> Buf;
llvm::raw_svector_ostream OS(Buf);
- OS << "Pointer to inner buffer of '" << ObjTy.getAsString()
- << "' obtained here";
+ OS << "Pointer to inner buffer of '" << ObjTy << "' obtained here";
PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
N->getLocationContext());
return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp
index 6955ba11a28f..3f5856a3efbe 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp
@@ -31,14 +31,14 @@ class InvalidatedIteratorChecker
check::PreStmt<ArraySubscriptExpr>,
check::PreStmt<MemberExpr>> {
- std::unique_ptr<BugType> InvalidatedBugType;
+ const BugType InvalidatedBugType{this, "Iterator invalidated",
+ "Misuse of STL APIs"};
- void verifyAccess(CheckerContext &C, const SVal &Val) const;
- void reportBug(const StringRef &Message, const SVal &Val,
- CheckerContext &C, ExplodedNode *ErrNode) const;
-public:
- InvalidatedIteratorChecker();
+ void verifyAccess(CheckerContext &C, SVal Val) const;
+ void reportBug(StringRef Message, SVal Val, CheckerContext &C,
+ ExplodedNode *ErrNode) const;
+public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPreStmt(const UnaryOperator *UO, CheckerContext &C) const;
void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const;
@@ -49,11 +49,6 @@ public:
} //namespace
-InvalidatedIteratorChecker::InvalidatedIteratorChecker() {
- InvalidatedBugType.reset(
- new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
-}
-
void InvalidatedIteratorChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
// Check for access of invalidated position
@@ -114,7 +109,8 @@ void InvalidatedIteratorChecker::checkPreStmt(const MemberExpr *ME,
verifyAccess(C, BaseVal);
}
-void InvalidatedIteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const {
+void InvalidatedIteratorChecker::verifyAccess(CheckerContext &C,
+ SVal Val) const {
auto State = C.getState();
const auto *Pos = getIteratorPosition(State, Val);
if (Pos && !Pos->isValid()) {
@@ -126,11 +122,11 @@ void InvalidatedIteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val
}
}
-void InvalidatedIteratorChecker::reportBug(const StringRef &Message,
- const SVal &Val, CheckerContext &C,
+void InvalidatedIteratorChecker::reportBug(StringRef Message, SVal Val,
+ CheckerContext &C,
ExplodedNode *ErrNode) const {
- auto R = std::make_unique<PathSensitiveBugReport>(*InvalidatedBugType,
- Message, ErrNode);
+ auto R = std::make_unique<PathSensitiveBugReport>(InvalidatedBugType, Message,
+ ErrNode);
R->markInteresting(Val);
C.emitReport(std::move(R));
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
index 496190149991..e8d35aac2efd 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
@@ -29,8 +29,8 @@ bool isIterator(const CXXRecordDecl *CRD) {
return false;
const auto Name = CRD->getName();
- if (!(Name.endswith_insensitive("iterator") ||
- Name.endswith_insensitive("iter") || Name.endswith_insensitive("it")))
+ if (!(Name.ends_with_insensitive("iterator") ||
+ Name.ends_with_insensitive("iter") || Name.ends_with_insensitive("it")))
return false;
bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
@@ -181,8 +181,7 @@ const ContainerData *getContainerData(ProgramStateRef State,
return State->get<ContainerMap>(Cont);
}
-const IteratorPosition *getIteratorPosition(ProgramStateRef State,
- const SVal &Val) {
+const IteratorPosition *getIteratorPosition(ProgramStateRef State, SVal Val) {
if (auto Reg = Val.getAsRegion()) {
Reg = Reg->getMostDerivedObjectRegion();
return State->get<IteratorRegionMap>(Reg);
@@ -194,7 +193,7 @@ const IteratorPosition *getIteratorPosition(ProgramStateRef State,
return nullptr;
}
-ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
+ProgramStateRef setIteratorPosition(ProgramStateRef State, SVal Val,
const IteratorPosition &Pos) {
if (auto Reg = Val.getAsRegion()) {
Reg = Reg->getMostDerivedObjectRegion();
@@ -207,8 +206,8 @@ ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
return nullptr;
}
-ProgramStateRef createIteratorPosition(ProgramStateRef State, const SVal &Val,
- const MemRegion *Cont, const Stmt* S,
+ProgramStateRef createIteratorPosition(ProgramStateRef State, SVal Val,
+ const MemRegion *Cont, const Stmt *S,
const LocationContext *LCtx,
unsigned blockCount) {
auto &StateMgr = State->getStateManager();
@@ -221,9 +220,8 @@ ProgramStateRef createIteratorPosition(ProgramStateRef State, const SVal &Val,
IteratorPosition::getPosition(Cont, Sym));
}
-ProgramStateRef advancePosition(ProgramStateRef State, const SVal &Iter,
- OverloadedOperatorKind Op,
- const SVal &Distance) {
+ProgramStateRef advancePosition(ProgramStateRef State, SVal Iter,
+ OverloadedOperatorKind Op, SVal Distance) {
const auto *Pos = getIteratorPosition(State, Iter);
if (!Pos)
return nullptr;
@@ -308,8 +306,8 @@ bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
const auto comparison =
SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType());
- assert(comparison.getAs<DefinedSVal>() &&
- "Symbol comparison must be a `DefinedSVal`");
+ assert(isa<DefinedSVal>(comparison) &&
+ "Symbol comparison must be a `DefinedSVal`");
return !State->assume(comparison.castAs<DefinedSVal>(), false);
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.h
index 37157492fe3e..46de8ea01d77 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.h
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.h
@@ -63,9 +63,7 @@ public:
return Cont == X.Cont && Valid == X.Valid && Offset == X.Offset;
}
- bool operator!=(const IteratorPosition &X) const {
- return Cont != X.Cont || Valid != X.Valid || Offset != X.Offset;
- }
+ bool operator!=(const IteratorPosition &X) const { return !(*this == X); }
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddPointer(Cont);
@@ -101,9 +99,7 @@ public:
return Begin == X.Begin && End == X.End;
}
- bool operator!=(const ContainerData &X) const {
- return Begin != X.Begin || End != X.End;
- }
+ bool operator!=(const ContainerData &X) const { return !(*this == X); }
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.Add(Begin);
@@ -165,18 +161,15 @@ 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 *getIteratorPosition(ProgramStateRef State, SVal Val);
+ProgramStateRef setIteratorPosition(ProgramStateRef State, SVal Val,
const IteratorPosition &Pos);
-ProgramStateRef createIteratorPosition(ProgramStateRef State, const SVal &Val,
- const MemRegion *Cont, const Stmt* S,
+ProgramStateRef createIteratorPosition(ProgramStateRef State, 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 advancePosition(ProgramStateRef State, SVal Iter,
+ OverloadedOperatorKind Op, SVal Distance);
ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
long Scale);
bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
index ab5e6a1c9991..a95e811c2a41 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
@@ -64,13 +64,15 @@
// making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
// a constraint which we later retrieve when doing an actual comparison.
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/DeclTemplate.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
+#include "llvm/ADT/STLExtras.h"
#include "Iterator.h"
@@ -98,18 +100,17 @@ class IteratorModeling
const AdvanceFn *Handler) const;
void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal,
- const SVal &LVal, const SVal &RVal,
- OverloadedOperatorKind Op) const;
+ SVal LVal, SVal RVal, OverloadedOperatorKind Op) const;
void processComparison(CheckerContext &C, ProgramStateRef State,
- SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
+ SymbolRef Sym1, SymbolRef Sym2, SVal RetVal,
OverloadedOperatorKind Op) const;
- void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
+ void handleIncrement(CheckerContext &C, SVal RetVal, SVal Iter,
bool Postfix) const;
- void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
+ void handleDecrement(CheckerContext &C, SVal RetVal, SVal Iter,
bool Postfix) const;
void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
- OverloadedOperatorKind Op, const SVal &RetVal,
- const SVal &Iterator, const SVal &Amount) const;
+ OverloadedOperatorKind Op, SVal RetVal,
+ SVal Iterator, SVal Amount) const;
void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator,
OverloadedOperatorKind OK, SVal Offset) const;
void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
@@ -118,7 +119,7 @@ class IteratorModeling
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,
+ void assignToContainer(CheckerContext &C, const Expr *CE, SVal RetVal,
const MemRegion *Cont) const;
bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const;
void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
@@ -150,8 +151,6 @@ public:
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,
CheckerContext &C) const;
void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
@@ -160,7 +159,7 @@ public:
bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
bool isSimpleComparisonOperator(BinaryOperatorKind OK);
-ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
+ProgramStateRef removeIteratorPosition(ProgramStateRef State, SVal Val);
ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
SymbolRef Sym2, bool Equal);
bool isBoundThroughLazyCompoundVal(const Environment &Env,
@@ -283,7 +282,7 @@ void IteratorModeling::checkPostStmt(const BinaryOperator *BO,
// The non-iterator side must have an integral or enumeration type.
if (!AmountExpr->getType()->isIntegralOrEnumerationType())
return;
- const SVal &AmountVal = IsIterOnLHS ? RVal : LVal;
+ SVal AmountVal = IsIterOnLHS ? RVal : LVal;
handlePtrIncrOrDecr(C, IterExpr, BinaryOperator::getOverloadedOperator(OK),
AmountVal);
}
@@ -304,21 +303,18 @@ void IteratorModeling::checkLiveSymbols(ProgramStateRef State,
SymbolReaper &SR) const {
// Keep symbolic expressions of iterator positions alive
auto RegionMap = State->get<IteratorRegionMap>();
- for (const auto &Reg : RegionMap) {
- const auto Offset = Reg.second.getOffset();
- for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
- if (isa<SymbolData>(*i))
- SR.markLive(*i);
+ for (const IteratorPosition &Pos : llvm::make_second_range(RegionMap)) {
+ for (SymbolRef Sym : Pos.getOffset()->symbols())
+ if (isa<SymbolData>(Sym))
+ SR.markLive(Sym);
}
auto SymbolMap = State->get<IteratorSymbolMap>();
- for (const auto &Sym : SymbolMap) {
- const auto Offset = Sym.second.getOffset();
- for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
- if (isa<SymbolData>(*i))
- SR.markLive(*i);
+ for (const IteratorPosition &Pos : llvm::make_second_range(SymbolMap)) {
+ for (SymbolRef Sym : Pos.getOffset()->symbols())
+ if (isa<SymbolData>(Sym))
+ SR.markLive(Sym);
}
-
}
void IteratorModeling::checkDeadSymbols(SymbolReaper &SR,
@@ -391,8 +387,8 @@ IteratorModeling::handleOverloadedOperator(CheckerContext &C,
const bool IsIterFirst = FirstType->isStructureOrClassType();
const SVal FirstArg = Call.getArgSVal(0);
const SVal SecondArg = Call.getArgSVal(1);
- const SVal &Iterator = IsIterFirst ? FirstArg : SecondArg;
- const SVal &Amount = IsIterFirst ? SecondArg : FirstArg;
+ SVal Iterator = IsIterFirst ? FirstArg : SecondArg;
+ SVal Amount = IsIterFirst ? SecondArg : FirstArg;
handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
Iterator, Amount);
@@ -447,14 +443,13 @@ IteratorModeling::handleAdvanceLikeFunction(CheckerContext &C,
}
void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
- SVal RetVal, const SVal &LVal,
- const SVal &RVal,
- OverloadedOperatorKind Op) const {
+ SVal RetVal, SVal LVal, SVal RVal,
+ OverloadedOperatorKind Op) const {
// Record the operands and the operator of the comparison for the next
// evalAssume, if the result is a symbolic expression. If it is a concrete
// value (only one branch is possible), then transfer the state between
// the operands according to the operator and the result
- auto State = C.getState();
+ auto State = C.getState();
const auto *LPos = getIteratorPosition(State, LVal);
const auto *RPos = getIteratorPosition(State, RVal);
const MemRegion *Cont = nullptr;
@@ -507,7 +502,7 @@ void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
void IteratorModeling::processComparison(CheckerContext &C,
ProgramStateRef State, SymbolRef Sym1,
- SymbolRef Sym2, const SVal &RetVal,
+ SymbolRef Sym2, SVal RetVal,
OverloadedOperatorKind Op) const {
if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
if ((State = relateSymbols(State, Sym1, Sym2,
@@ -535,8 +530,8 @@ void IteratorModeling::processComparison(CheckerContext &C,
}
}
-void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal,
- const SVal &Iter, bool Postfix) const {
+void IteratorModeling::handleIncrement(CheckerContext &C, SVal RetVal,
+ SVal Iter, bool Postfix) const {
// Increment the symbolic expressions which represents the position of the
// iterator
auto State = C.getState();
@@ -561,8 +556,8 @@ void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal,
C.addTransition(State);
}
-void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal,
- const SVal &Iter, bool Postfix) const {
+void IteratorModeling::handleDecrement(CheckerContext &C, SVal RetVal,
+ SVal Iter, bool Postfix) const {
// Decrement the symbolic expressions which represents the position of the
// iterator
auto State = C.getState();
@@ -589,9 +584,8 @@ void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal,
void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
OverloadedOperatorKind Op,
- const SVal &RetVal,
- const SVal &Iterator,
- const SVal &Amount) const {
+ SVal RetVal, SVal Iterator,
+ SVal Amount) const {
// Increment or decrement the symbolic expressions which represents the
// position of the iterator
auto State = C.getState();
@@ -630,7 +624,7 @@ void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C,
const Expr *Iterator,
OverloadedOperatorKind OK,
SVal Offset) const {
- if (!Offset.getAs<DefinedSVal>())
+ if (!isa<DefinedSVal>(Offset))
return;
QualType PtrType = Iterator->getType();
@@ -687,7 +681,7 @@ void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE,
}
void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE,
- const SVal &RetVal,
+ SVal RetVal,
const MemRegion *Cont) const {
Cont = Cont->getMostDerivedObjectRegion();
@@ -775,7 +769,7 @@ bool isSimpleComparisonOperator(BinaryOperatorKind OK) {
return OK == BO_EQ || OK == BO_NE;
}
-ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
+ProgramStateRef removeIteratorPosition(ProgramStateRef State, SVal Val) {
if (auto Reg = Val.getAsRegion()) {
Reg = Reg->getMostDerivedObjectRegion();
return State->remove<IteratorRegionMap>(Reg);
@@ -800,8 +794,8 @@ ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
nonloc::SymbolVal(Sym2), SVB.getConditionType());
- assert(comparison.getAs<DefinedSVal>() &&
- "Symbol comparison must be a `DefinedSVal`");
+ assert(isa<DefinedSVal>(comparison) &&
+ "Symbol comparison must be a `DefinedSVal`");
auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
if (!NewState)
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
index a47484497771..c8828219dd73 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
@@ -14,10 +14,10 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-
#include "Iterator.h"
using namespace clang;
@@ -32,7 +32,8 @@ class IteratorRangeChecker
check::PreStmt<ArraySubscriptExpr>,
check::PreStmt<MemberExpr>> {
- std::unique_ptr<BugType> OutOfRangeBugType;
+ const BugType OutOfRangeBugType{this, "Iterator out of range",
+ "Misuse of STL APIs"};
void verifyDereference(CheckerContext &C, SVal Val) const;
void verifyIncrement(CheckerContext &C, SVal Iter) const;
@@ -42,12 +43,10 @@ class IteratorRangeChecker
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,
+ void reportBug(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;
@@ -67,15 +66,10 @@ public:
bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos);
bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
-bool isZero(ProgramStateRef State, const NonLoc &Val);
+bool isZero(ProgramStateRef State, NonLoc Val);
} //namespace
-IteratorRangeChecker::IteratorRangeChecker() {
- OutOfRangeBugType.reset(
- new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
-}
-
void IteratorRangeChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
// Check for out of range access
@@ -228,7 +222,7 @@ void IteratorRangeChecker::verifyRandomIncrOrDecr(CheckerContext &C,
Value = State->getRawSVal(*ValAsLoc);
}
- if (Value.isUnknownOrUndef())
+ if (Value.isUnknownOrUndef() || !isa<NonLoc>(Value))
return;
// Incremention or decremention by 0 is never a bug.
@@ -275,10 +269,10 @@ void IteratorRangeChecker::verifyNext(CheckerContext &C, SVal LHS,
verifyRandomIncrOrDecr(C, OO_Plus, LHS, RHS);
}
-void IteratorRangeChecker::reportBug(const StringRef &Message, SVal Val,
+void IteratorRangeChecker::reportBug(StringRef Message, SVal Val,
CheckerContext &C,
ExplodedNode *ErrNode) const {
- auto R = std::make_unique<PathSensitiveBugReport>(*OutOfRangeBugType, Message,
+ auto R = std::make_unique<PathSensitiveBugReport>(OutOfRangeBugType, Message,
ErrNode);
const auto *Pos = getIteratorPosition(C.getState(), Val);
@@ -295,7 +289,7 @@ bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
-bool isZero(ProgramStateRef State, const NonLoc &Val) {
+bool isZero(ProgramStateRef State, NonLoc Val) {
auto &BVF = State->getBasicVals();
return compare(State, Val,
nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
index 3e6756efe0e6..f0276a57bdf9 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
@@ -27,14 +27,15 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/StmtVisitor.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallString.h"
@@ -44,9 +45,9 @@ using namespace ento;
namespace {
struct ChecksFilter {
/// Check for missing invalidation method declarations.
- DefaultBool check_MissingInvalidationMethod;
+ bool check_MissingInvalidationMethod = false;
/// Check that all ivars are invalidated.
- DefaultBool check_InstanceVariableInvalidation;
+ bool check_InstanceVariableInvalidation = false;
CheckerNameRef checkName_MissingInvalidationMethod;
CheckerNameRef checkName_InstanceVariableInvalidation;
@@ -63,12 +64,12 @@ class IvarInvalidationCheckerImpl {
struct InvalidationInfo {
/// Has the ivar been invalidated?
- bool IsInvalidated;
+ bool IsInvalidated = false;
/// The methods which can be used to invalidate the ivar.
MethodSet InvalidationMethods;
- InvalidationInfo() : IsInvalidated(false) {}
+ InvalidationInfo() = default;
void addInvalidationMethod(const ObjCMethodDecl *MD) {
InvalidationMethods.insert(MD);
}
@@ -80,9 +81,8 @@ class IvarInvalidationCheckerImpl {
bool hasMethod(const ObjCMethodDecl *MD) {
if (IsInvalidated)
return true;
- for (MethodSet::iterator I = InvalidationMethods.begin(),
- E = InvalidationMethods.end(); I != E; ++I) {
- if (*I == MD) {
+ for (const ObjCMethodDecl *Curr : InvalidationMethods) {
+ if (Curr == MD) {
IsInvalidated = true;
return true;
}
@@ -318,9 +318,7 @@ const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
// Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
StringRef PropName = Prop->getIdentifier()->getName();
- for (IvarSet::const_iterator I = TrackedIvars.begin(),
- E = TrackedIvars.end(); I != E; ++I) {
- const ObjCIvarDecl *Iv = I->first;
+ for (const ObjCIvarDecl *Iv : llvm::make_first_range(TrackedIvars)) {
StringRef IvarName = Iv->getName();
if (IvarName == PropName)
@@ -379,12 +377,9 @@ visit(const ObjCImplementationDecl *ImplD) const {
IvarToPropMapTy IvarToPopertyMap;
ObjCInterfaceDecl::PropertyMap PropMap;
- ObjCInterfaceDecl::PropertyDeclOrder PropOrder;
- InterfaceD->collectPropertiesToImplement(PropMap, PropOrder);
+ InterfaceD->collectPropertiesToImplement(PropMap);
- for (ObjCInterfaceDecl::PropertyMap::iterator
- I = PropMap.begin(), E = PropMap.end(); I != E; ++I) {
- const ObjCPropertyDecl *PD = I->second;
+ for (const ObjCPropertyDecl *PD : llvm::make_second_range(PropMap)) {
if (PD->isClassProperty())
continue;
@@ -423,11 +418,7 @@ visit(const ObjCImplementationDecl *ImplD) const {
// Remove ivars invalidated by the partial invalidation methods. They do not
// need to be invalidated in the regular invalidation methods.
bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false;
- for (MethodSet::iterator
- I = PartialInfo.InvalidationMethods.begin(),
- E = PartialInfo.InvalidationMethods.end(); I != E; ++I) {
- const ObjCMethodDecl *InterfD = *I;
-
+ for (const ObjCMethodDecl *InterfD : PartialInfo.InvalidationMethods) {
// Get the corresponding method in the @implementation.
const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
InterfD->isInstanceMethod());
@@ -476,10 +467,7 @@ visit(const ObjCImplementationDecl *ImplD) const {
// Check that all ivars are invalidated by the invalidation methods.
bool AtImplementationContainsAtLeastOneInvalidationMethod = false;
- for (MethodSet::iterator I = Info.InvalidationMethods.begin(),
- E = Info.InvalidationMethods.end(); I != E; ++I) {
- const ObjCMethodDecl *InterfD = *I;
-
+ for (const ObjCMethodDecl *InterfD : Info.InvalidationMethods) {
// Get the corresponding method in the @implementation.
const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
InterfD->isInstanceMethod());
@@ -502,9 +490,8 @@ visit(const ObjCImplementationDecl *ImplD) const {
continue;
// Warn on the ivars that were not invalidated by the method.
- for (IvarSet::const_iterator
- I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I)
- reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D);
+ for (const ObjCIvarDecl *Ivar : llvm::make_first_range(IvarsI))
+ reportIvarNeedsInvalidation(Ivar, IvarToPopertyMap, D);
}
}
@@ -513,9 +500,8 @@ visit(const ObjCImplementationDecl *ImplD) const {
if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) {
// Warn on the ivars that were not invalidated by the prrtial
// invalidation methods.
- for (IvarSet::const_iterator
- I = Ivars.begin(), E = Ivars.end(); I != E; ++I)
- reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, nullptr);
+ for (const ObjCIvarDecl *Ivar : llvm::make_first_range(Ivars))
+ reportIvarNeedsInvalidation(Ivar, IvarToPopertyMap, nullptr);
} else {
// Otherwise, no invalidation methods were implemented.
reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation,
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
index 1f3d8844d330..fa51aa80216b 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
@@ -273,7 +273,7 @@ void ASTFieldVisitor::ReportError(QualType T) {
os << (*I)->getName();
}
}
- os << " (type " << FieldChain.back()->getType().getAsString() << ")";
+ os << " (type " << FieldChain.back()->getType() << ")";
// Note that this will fire for every translation unit that uses this
// class. This is suboptimal, but at least scan-build will merge
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
index 28d3e058fee2..812d787e2e37 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
@@ -14,13 +14,13 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Lex/Lexer.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
@@ -28,7 +28,9 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Unicode.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -60,7 +62,8 @@ class NonLocalizedStringChecker
check::PostObjCMessage,
check::PostStmt<ObjCStringLiteral>> {
- mutable std::unique_ptr<BugType> BT;
+ const BugType BT{this, "Unlocalizable string",
+ "Localizability Issue (Apple)"};
// Methods that require a localized string
mutable llvm::DenseMap<const IdentifierInfo *,
@@ -87,12 +90,10 @@ class NonLocalizedStringChecker
Selector S) const;
public:
- NonLocalizedStringChecker();
-
// When this parameter is set to true, the checker assumes all
// methods that return NSStrings are unlocalized. Thus, more false
// positives will be reported.
- DefaultBool IsAggressive;
+ bool IsAggressive = false;
void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
void checkPostObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
@@ -106,11 +107,6 @@ public:
REGISTER_MAP_WITH_PROGRAMSTATE(LocalizedMemMap, const MemRegion *,
LocalizedState)
-NonLocalizedStringChecker::NonLocalizedStringChecker() {
- BT.reset(new BugType(this, "Unlocalizable string",
- "Localizability Issue (Apple)"));
-}
-
namespace {
class NonLocalizedStringBRVisitor final : public BugReporterVisitor {
@@ -716,7 +712,7 @@ void NonLocalizedStringChecker::setNonLocalizedState(const SVal S,
static bool isDebuggingName(std::string name) {
- return StringRef(name).lower().find("debug") != StringRef::npos;
+ return StringRef(name).contains_insensitive("debug");
}
/// Returns true when, heuristically, the analyzer may be analyzing debugging
@@ -762,7 +758,7 @@ void NonLocalizedStringChecker::reportLocalizationError(
// Generate the bug report.
auto R = std::make_unique<PathSensitiveBugReport>(
- *BT, "User-facing text should use localized string macro", ErrNode);
+ BT, "User-facing text should use localized string macro", ErrNode);
if (argumentNumber) {
R->addRange(M.getArgExpr(argumentNumber - 1)->getSourceRange());
} else {
@@ -815,9 +811,9 @@ void NonLocalizedStringChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
// Handle the case where the receiver is an NSString
// These special NSString methods draw to the screen
- if (!(SelectorName.startswith("drawAtPoint") ||
- SelectorName.startswith("drawInRect") ||
- SelectorName.startswith("drawWithRect")))
+ if (!(SelectorName.starts_with("drawAtPoint") ||
+ SelectorName.starts_with("drawInRect") ||
+ SelectorName.starts_with("drawWithRect")))
return;
SVal svTitle = msg.getReceiverSVal();
@@ -846,10 +842,9 @@ void NonLocalizedStringChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
if (argumentNumber < 0) { // There was no match in UIMethods
if (const Decl *D = msg.getDecl()) {
if (const ObjCMethodDecl *OMD = dyn_cast_or_null<ObjCMethodDecl>(D)) {
- auto formals = OMD->parameters();
- for (unsigned i = 0, ei = formals.size(); i != ei; ++i) {
- if (isAnnotatedAsTakingLocalized(formals[i])) {
- argumentNumber = i;
+ for (auto [Idx, FormalParam] : llvm::enumerate(OMD->parameters())) {
+ if (isAnnotatedAsTakingLocalized(FormalParam)) {
+ argumentNumber = Idx;
break;
}
}
@@ -949,7 +944,7 @@ void NonLocalizedStringChecker::checkPostCall(const CallEvent &Call,
const IdentifierInfo *Identifier = Call.getCalleeIdentifier();
SVal sv = Call.getReturnValue();
- if (isAnnotatedAsReturningLocalized(D) || LSF.count(Identifier) != 0) {
+ if (isAnnotatedAsReturningLocalized(D) || LSF.contains(Identifier)) {
setLocalizedState(sv, C);
} else if (isNSStringType(RT, C.getASTContext()) &&
!hasLocalizedState(sv, C)) {
@@ -1004,8 +999,8 @@ NonLocalizedStringBRVisitor::VisitNode(const ExplodedNode *Succ,
if (Satisfied)
return nullptr;
- Optional<StmtPoint> Point = Succ->getLocation().getAs<StmtPoint>();
- if (!Point.hasValue())
+ std::optional<StmtPoint> Point = Succ->getLocation().getAs<StmtPoint>();
+ if (!Point)
return nullptr;
auto *LiteralExpr = dyn_cast<ObjCStringLiteral>(Point->getStmt());
@@ -1141,12 +1136,12 @@ void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr(
SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first);
}
- llvm::Optional<llvm::MemoryBufferRef> BF =
+ std::optional<llvm::MemoryBufferRef> BF =
Mgr.getSourceManager().getBufferOrNone(SLInfo.first, SL);
if (!BF)
return;
-
- Lexer TheLexer(SL, LangOptions(), BF->getBufferStart(),
+ LangOptions LangOpts;
+ Lexer TheLexer(SL, LangOpts, BF->getBufferStart(),
BF->getBufferStart() + SLInfo.second, BF->getBufferEnd());
Token I;
@@ -1253,8 +1248,8 @@ bool PluralMisuseChecker::MethodCrawler::isCheckingPlurality(
BO = B;
}
}
- if (VD->getName().lower().find("plural") != StringRef::npos ||
- VD->getName().lower().find("singular") != StringRef::npos) {
+ if (VD->getName().contains_insensitive("plural") ||
+ VD->getName().contains_insensitive("singular")) {
return true;
}
}
@@ -1339,7 +1334,10 @@ bool PluralMisuseChecker::MethodCrawler::EndVisitIfStmt(IfStmt *I) {
}
bool PluralMisuseChecker::MethodCrawler::VisitIfStmt(const IfStmt *I) {
- const Expr *Condition = I->getCond()->IgnoreParenImpCasts();
+ const Expr *Condition = I->getCond();
+ if (!Condition)
+ return true;
+ Condition = Condition->IgnoreParenImpCasts();
if (isCheckingPlurality(Condition)) {
MatchingStatements.push_back(I);
InMatchingStatement = true;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
index b72d72580c28..153a0a51e980 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
@@ -27,8 +27,10 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -85,7 +87,7 @@ class MIGChecker : public Checker<check::PostCall, check::PreStmt<ReturnStmt>,
#undef CALL
};
- CallDescription OsRefRetain{"os_ref_retain", 1};
+ CallDescription OsRefRetain{{"os_ref_retain"}, 1};
void checkReturnAux(const ReturnStmt *RS, CheckerContext &C) const;
@@ -156,10 +158,10 @@ static bool isInMIGCall(CheckerContext &C) {
const Decl *D = SFC->getDecl();
- if (Optional<AnyCall> AC = AnyCall::forDecl(D)) {
+ if (std::optional<AnyCall> AC = AnyCall::forDecl(D)) {
// Even though there's a Sema warning when the return type of an annotated
// function is not a kern_return_t, this warning isn't an error, so we need
- // an extra sanity check here.
+ // an extra check here.
// FIXME: AnyCall doesn't support blocks yet, so they remain unchecked
// for now.
if (!AC->getReturnType(C.getASTContext())
@@ -180,7 +182,7 @@ static bool isInMIGCall(CheckerContext &C) {
}
void MIGChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const {
- if (Call.isCalled(OsRefRetain)) {
+ if (OsRefRetain.matches(Call)) {
// If the code is doing reference counting over the parameter,
// it opens up an opportunity for safely calling a destructor function.
// TODO: We should still check for over-releases.
@@ -198,7 +200,7 @@ void MIGChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const {
auto I = llvm::find_if(Deallocators,
[&](const std::pair<CallDescription, unsigned> &Item) {
- return Call.isCalled(Item.first);
+ return Item.first.matches(Call);
});
if (I == Deallocators.end())
return;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp
index bbf2ddec5762..3e374e6c240e 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp
@@ -31,7 +31,7 @@ void MPIBugReporter::reportDoubleNonblocking(
RequestRegion->getDescriptiveName() + ". ";
auto Report = std::make_unique<PathSensitiveBugReport>(
- *DoubleNonblockingBugType, ErrorText, ExplNode);
+ DoubleNonblockingBugType, ErrorText, ExplNode);
Report->addRange(MPICallEvent.getSourceRange());
SourceRange Range = RequestRegion->sourceRange();
@@ -53,7 +53,7 @@ void MPIBugReporter::reportMissingWait(
std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() +
" has no matching wait. "};
- auto Report = std::make_unique<PathSensitiveBugReport>(*MissingWaitBugType,
+ auto Report = std::make_unique<PathSensitiveBugReport>(MissingWaitBugType,
ErrorText, ExplNode);
SourceRange Range = RequestRegion->sourceRange();
@@ -73,7 +73,7 @@ void MPIBugReporter::reportUnmatchedWait(
std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() +
" has no matching nonblocking call. "};
- auto Report = std::make_unique<PathSensitiveBugReport>(*UnmatchedWaitBugType,
+ auto Report = std::make_unique<PathSensitiveBugReport>(UnmatchedWaitBugType,
ErrorText, ExplNode);
Report->addRange(CE.getSourceRange());
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h
index 9871da026b04..0222a2120b34 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h
@@ -17,6 +17,7 @@
#include "MPITypes.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "llvm/ADT/StringRef.h"
namespace clang {
namespace ento {
@@ -24,12 +25,10 @@ namespace mpi {
class MPIBugReporter {
public:
- MPIBugReporter(const CheckerBase &CB) {
- UnmatchedWaitBugType.reset(new BugType(&CB, "Unmatched wait", MPIError));
- DoubleNonblockingBugType.reset(
- new BugType(&CB, "Double nonblocking", MPIError));
- MissingWaitBugType.reset(new BugType(&CB, "Missing wait", MPIError));
- }
+ MPIBugReporter(const CheckerBase &CB)
+ : UnmatchedWaitBugType(&CB, "Unmatched wait", MPIError),
+ MissingWaitBugType(&CB, "Missing wait", MPIError),
+ DoubleNonblockingBugType(&CB, "Double nonblocking", MPIError) {}
/// Report duplicate request use by nonblocking calls without intermediate
/// wait.
@@ -68,12 +67,10 @@ public:
BugReporter &BReporter) const;
private:
- const std::string MPIError = "MPI Error";
-
- // path-sensitive bug types
- std::unique_ptr<BugType> UnmatchedWaitBugType;
- std::unique_ptr<BugType> MissingWaitBugType;
- std::unique_ptr<BugType> DoubleNonblockingBugType;
+ const llvm::StringLiteral MPIError = "MPI Error";
+ const BugType UnmatchedWaitBugType;
+ const BugType MissingWaitBugType;
+ const BugType DoubleNonblockingBugType;
/// Bug visitor class to find the node where the request region was previously
/// used in order to include it into the BugReport path.
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 5d6bd381d3cc..4c0a8ba2c7c0 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
@@ -165,7 +165,7 @@ void MPIChecker::allRegionsUsedByWait(
Ctx.getState(), SuperRegion, Ctx.getSValBuilder(),
CE.getArgExpr(1)->getType()->getPointeeType());
const llvm::APSInt &ArrSize =
- ElementCount.getAs<nonloc::ConcreteInt>()->getValue();
+ ElementCount.castAs<nonloc::ConcreteInt>().getValue();
for (size_t i = 0; i < ArrSize; ++i) {
const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
index a157ee2da5df..12bf12a0b232 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
@@ -19,8 +19,10 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -31,7 +33,8 @@ class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
check::DeadSymbols,
check::PointerEscape,
eval::Assume> {
- mutable std::unique_ptr<BugType> BT;
+ const BugType BT{this, "Improper use of SecKeychain API",
+ categories::AppleAPIMisuse};
public:
/// AllocationState is a part of the checker specific state together with the
@@ -99,12 +102,6 @@ private:
/// function.
static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator);
- inline void initBugType() const {
- if (!BT)
- BT.reset(new BugType(this, "Improper use of SecKeychain API",
- "API Misuse (Apple)"));
- }
-
void generateDeallocatorMismatchReport(const AllocationPair &AP,
const Expr *ArgExpr,
CheckerContext &C) const;
@@ -160,7 +157,7 @@ static bool isEnclosingFunctionParam(const Expr *E) {
E = E->IgnoreParenCasts();
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
const ValueDecl *VD = DRE->getDecl();
- if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
+ if (isa<ImplicitParamDecl, ParmVarDecl>(VD))
return true;
}
return false;
@@ -199,8 +196,7 @@ unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
static bool isBadDeallocationArgument(const MemRegion *Arg) {
if (!Arg)
return false;
- return isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) ||
- isa<TypedRegion>(Arg);
+ return isa<AllocaRegion, BlockDataRegion, TypedRegion>(Arg);
}
/// Given the address expression, retrieve the value it's pointing to. Assume
@@ -210,7 +206,7 @@ static SymbolRef getAsPointeeSymbol(const Expr *Expr,
ProgramStateRef State = C.getState();
SVal ArgV = C.getSVal(Expr);
- if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) {
+ if (std::optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) {
StoreManager& SM = C.getStoreManager();
SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol();
if (sym)
@@ -231,7 +227,6 @@ void MacOSKeychainAPIChecker::
if (!N)
return;
- initBugType();
SmallString<80> sbuf;
llvm::raw_svector_ostream os(sbuf);
unsigned int PDeallocIdx =
@@ -239,7 +234,7 @@ void MacOSKeychainAPIChecker::
os << "Deallocator doesn't match the allocator: '"
<< FunctionsToTrack[PDeallocIdx].Name << "' should be used.";
- auto Report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
+ auto Report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first));
Report->addRange(ArgExpr->getSourceRange());
markInteresting(Report.get(), AP);
@@ -275,7 +270,6 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
ExplodedNode *N = C.generateNonFatalErrorNode(State);
if (!N)
return;
- initBugType();
SmallString<128> sbuf;
llvm::raw_svector_ostream os(sbuf);
unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
@@ -283,8 +277,7 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
<< "the allocator: missing a call to '"
<< FunctionsToTrack[DIdx].Name
<< "'.";
- auto Report =
- std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
+ auto Report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(V));
Report->addRange(ArgExpr->getSourceRange());
Report->markInteresting(AS->Region);
@@ -337,9 +330,8 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
ExplodedNode *N = C.generateNonFatalErrorNode(State);
if (!N)
return;
- initBugType();
auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT, "Trying to free data which has not been allocated.", N);
+ BT, "Trying to free data which has not been allocated.", N);
Report->addRange(ArgExpr->getSourceRange());
if (AS)
Report->markInteresting(AS->Region);
@@ -473,7 +465,6 @@ std::unique_ptr<PathSensitiveBugReport>
MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const {
const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
- initBugType();
SmallString<70> sbuf;
llvm::raw_svector_ostream os(sbuf);
os << "Allocated data is not released: missing a call to '"
@@ -492,7 +483,7 @@ MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
AllocNode->getLocationContext());
auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT, os.str(), N, LocUsedForUniqueing,
+ BT, os.str(), N, LocUsedForUniqueing,
AllocNode->getLocationContext()->getDecl());
Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first));
@@ -530,9 +521,9 @@ ProgramStateRef MacOSKeychainAPIChecker::evalAssume(ProgramStateRef State,
}
if (ReturnSymbol)
- for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
- if (ReturnSymbol == I->second.Region)
- State = State->remove<AllocatedData>(I->first);
+ for (auto [Sym, AllocState] : AMap) {
+ if (ReturnSymbol == AllocState.Region)
+ State = State->remove<AllocatedData>(Sym);
}
return State;
@@ -547,18 +538,18 @@ void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
bool Changed = false;
AllocationPairVec Errors;
- for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
- if (!SR.isDead(I->first))
+ for (const auto &[Sym, AllocState] : AMap) {
+ if (!SR.isDead(Sym))
continue;
Changed = true;
- State = State->remove<AllocatedData>(I->first);
+ State = State->remove<AllocatedData>(Sym);
// If the allocated symbol is null do not report.
ConstraintManager &CMgr = State->getConstraintManager();
- ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey());
+ ConditionTruthVal AllocFailed = CMgr.isNull(State, Sym);
if (AllocFailed.isConstrainedTrue())
continue;
- Errors.push_back(std::make_pair(I->first, &I->second));
+ Errors.push_back(std::make_pair(Sym, &AllocState));
}
if (!Changed) {
// Generate the new, cleaned up state.
@@ -656,8 +647,8 @@ void MacOSKeychainAPIChecker::printState(raw_ostream &Out,
if (!AMap.isEmpty()) {
Out << Sep << "KeychainAPIChecker :" << NL;
- for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
- I.getKey()->dumpToStream(Out);
+ for (SymbolRef Sym : llvm::make_first_range(AMap)) {
+ Sym->dumpToStream(Out);
}
}
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
index 04e7f8dec8d7..754b16764296 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
@@ -18,6 +18,7 @@
#include "clang/Basic/TargetInfo.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/CheckerContext.h"
@@ -31,7 +32,8 @@ using namespace ento;
namespace {
class MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > {
- mutable std::unique_ptr<BugType> BT_dispatchOnce;
+ const BugType BT_dispatchOnce{this, "Improper use of 'dispatch_once'",
+ categories::AppleAPIMisuse};
static const ObjCIvarRegion *getParentIvarRegion(const MemRegion *R);
@@ -136,12 +138,8 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
if (!N)
return;
- if (!BT_dispatchOnce)
- BT_dispatchOnce.reset(new BugType(this, "Improper use of 'dispatch_once'",
- "API Misuse (Apple)"));
-
auto report =
- std::make_unique<PathSensitiveBugReport>(*BT_dispatchOnce, os.str(), N);
+ std::make_unique<PathSensitiveBugReport>(BT_dispatchOnce, os.str(), N);
report->addRange(CE->getArg(0)->getSourceRange());
C.emitReport(std::move(report));
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index a6470da09c45..79ab05f2c786 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -48,9 +48,13 @@
#include "InterCheckerAPI.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ParentMap.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/ProgramPoint.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
@@ -60,22 +64,29 @@
#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.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/DynamicExtent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.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/StoreRef.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SetOperations.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
#include <climits>
#include <functional>
+#include <optional>
#include <utility>
using namespace clang;
@@ -210,10 +221,10 @@ static bool isReleased(SymbolRef Sym, CheckerContext &C);
/// Update the RefState to reflect the new memory allocation.
/// The optional \p RetVal parameter specifies the newly allocated pointer
/// value; if unspecified, the value of expression \p E is used.
-static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E,
- ProgramStateRef State,
- AllocationFamily Family,
- Optional<SVal> RetVal = None);
+static ProgramStateRef
+MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State,
+ AllocationFamily Family,
+ std::optional<SVal> RetVal = std::nullopt);
//===----------------------------------------------------------------------===//
// The modeling of memory reallocation.
@@ -296,7 +307,9 @@ public:
/// 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;
+ bool ShouldIncludeOwnershipAnnotatedFunctions = false;
+
+ bool ShouldRegisterNoOwnershipChangeVisitor = false;
/// Many checkers are essentially built into this one, so enabling them will
/// make MallocChecker perform additional modeling and reporting.
@@ -314,7 +327,7 @@ public:
using LeakInfo = std::pair<const ExplodedNode *, const MemRegion *>;
- DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ bool ChecksEnabled[CK_NumCheckKinds] = {false};
CheckerNameRef CheckNames[CK_NumCheckKinds];
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
@@ -379,70 +392,72 @@ private:
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},
+ {{{"free"}, 1}, &MallocChecker::checkFree},
+ {{{"if_freenameindex"}, 1}, &MallocChecker::checkIfFreeNameIndex},
+ {{{"kfree"}, 1}, &MallocChecker::checkFree},
+ {{{"g_free"}, 1}, &MallocChecker::checkFree},
};
bool isFreeingCall(const CallEvent &Call) const;
+ static bool isFreeingOwnershipAttrCall(const FunctionDecl *Func);
+
+ friend class NoOwnershipChangeVisitor;
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},
+ {{{"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},
+ {{{"realloc"}, 2},
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)},
- {{"reallocf", 2},
+ {{{"reallocf"}, 2},
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, true)},
- {{"g_realloc", 2},
+ {{{"g_realloc"}, 2},
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)},
- {{"g_try_realloc", 2},
+ {{{"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},
+ {{{"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;
+ mutable std::optional<uint64_t> KernelZeroFlagVal;
- using KernelZeroSizePtrValueTy = Optional<int>;
+ using KernelZeroSizePtrValueTy = std::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;
+ mutable std::optional<KernelZeroSizePtrValueTy> KernelZeroSizePtrValue;
/// Process C++ operator new()'s allocation, which is the part of C++
/// new-expression that goes before the constructor.
- LLVM_NODISCARD
- ProgramStateRef processNewAllocation(const CXXAllocatorCall &Call,
- CheckerContext &C,
- AllocationFamily Family) const;
+ [[nodiscard]] ProgramStateRef
+ processNewAllocation(const CXXAllocatorCall &Call, CheckerContext &C,
+ AllocationFamily Family) const;
/// Perform a zero-allocation check.
///
@@ -452,11 +467,10 @@ private:
/// 0.
/// \param [in] RetVal Specifies the newly allocated pointer value;
/// if unspecified, the value of expression \p E is used.
- LLVM_NODISCARD
- static ProgramStateRef ProcessZeroAllocCheck(const CallEvent &Call,
- const unsigned IndexOfSizeArg,
- ProgramStateRef State,
- Optional<SVal> RetVal = None);
+ [[nodiscard]] static ProgramStateRef
+ ProcessZeroAllocCheck(const CallEvent &Call, const unsigned IndexOfSizeArg,
+ ProgramStateRef State,
+ std::optional<SVal> RetVal = std::nullopt);
/// Model functions with the ownership_returns attribute.
///
@@ -474,10 +488,9 @@ private:
/// \param [in] Att The ownership_returns attribute.
/// \param [in] State The \c ProgramState right before allocation.
/// \returns The ProgramState right after allocation.
- LLVM_NODISCARD
- ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call,
- const OwnershipAttr *Att,
- ProgramStateRef State) const;
+ [[nodiscard]] ProgramStateRef
+ MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call,
+ const OwnershipAttr *Att, ProgramStateRef State) const;
/// Models memory allocation.
///
@@ -488,11 +501,9 @@ private:
/// malloc leaves it undefined.
/// \param [in] State The \c ProgramState right before allocation.
/// \returns The ProgramState right after allocation.
- LLVM_NODISCARD
- static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call,
- const Expr *SizeEx, SVal Init,
- ProgramStateRef State,
- AllocationFamily Family);
+ [[nodiscard]] static ProgramStateRef
+ MallocMemAux(CheckerContext &C, const CallEvent &Call, const Expr *SizeEx,
+ SVal Init, ProgramStateRef State, AllocationFamily Family);
/// Models memory allocation.
///
@@ -503,16 +514,13 @@ private:
/// malloc leaves it undefined.
/// \param [in] State The \c ProgramState right before allocation.
/// \returns The ProgramState right after allocation.
- LLVM_NODISCARD
- static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call,
- SVal Size, SVal Init,
- ProgramStateRef State,
- AllocationFamily Family);
+ [[nodiscard]] static ProgramStateRef
+ MallocMemAux(CheckerContext &C, const CallEvent &Call, SVal Size, SVal Init,
+ ProgramStateRef State, AllocationFamily Family);
// 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>
+ [[nodiscard]] std::optional<ProgramStateRef>
performKernelMalloc(const CallEvent &Call, CheckerContext &C,
const ProgramStateRef &State) const;
@@ -533,10 +541,10 @@ private:
/// \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.
- LLVM_NODISCARD
- ProgramStateRef FreeMemAttr(CheckerContext &C, const CallEvent &Call,
- const OwnershipAttr *Att,
- ProgramStateRef State) const;
+ [[nodiscard]] ProgramStateRef FreeMemAttr(CheckerContext &C,
+ const CallEvent &Call,
+ const OwnershipAttr *Att,
+ ProgramStateRef State) const;
/// Models memory deallocation.
///
@@ -557,12 +565,10 @@ 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 CallEvent &Call,
- ProgramStateRef State, unsigned Num, bool Hold,
- bool &IsKnownToBeAllocated,
- AllocationFamily Family,
- bool ReturnsNullOnFailure = false) const;
+ [[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.
///
@@ -583,12 +589,10 @@ 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 CallEvent &Call, ProgramStateRef State,
- bool Hold, bool &IsKnownToBeAllocated,
- AllocationFamily Family,
- bool ReturnsNullOnFailure = false) const;
+ [[nodiscard]] ProgramStateRef
+ FreeMemAux(CheckerContext &C, const Expr *ArgExpr, const CallEvent &Call,
+ ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated,
+ AllocationFamily Family, bool ReturnsNullOnFailure = false) const;
// TODO: Needs some refactoring, as all other deallocation modeling
// functions are suffering from out parameters and messy code due to how
@@ -603,29 +607,27 @@ private:
/// \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.
- LLVM_NODISCARD
- ProgramStateRef ReallocMemAux(CheckerContext &C, const CallEvent &Call,
- bool ShouldFreeOnFail, ProgramStateRef State,
- AllocationFamily Family,
- bool SuffixWithN = false) const;
+ [[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.
///
/// \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);
+ [[nodiscard]] static SVal evalMulForBufferSize(CheckerContext &C,
+ const Expr *Blocks,
+ const Expr *BlockBytes);
/// Models zero initialized array allocation.
///
/// \param [in] Call The expression that reallocated memory
/// \param [in] State The \c ProgramState right before reallocation.
/// \returns The ProgramState right after allocation.
- LLVM_NODISCARD
- static ProgramStateRef CallocMem(CheckerContext &C, const CallEvent &Call,
- ProgramStateRef State);
+ [[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.
@@ -658,12 +660,11 @@ private:
SymbolRef &EscapingSymbol) const;
/// Implementation of the checkPointerEscape callbacks.
- LLVM_NODISCARD
- ProgramStateRef checkPointerEscapeAux(ProgramStateRef State,
- const InvalidatedSymbols &Escaped,
- const CallEvent *Call,
- PointerEscapeKind Kind,
- bool IsConstPointerEscape) const;
+ [[nodiscard]] ProgramStateRef
+ checkPointerEscapeAux(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call, PointerEscapeKind Kind,
+ bool IsConstPointerEscape) const;
// Implementation of the checkPreStmt and checkEndFunction callbacks.
void checkEscapeOnReturn(const ReturnStmt *S, CheckerContext &C) const;
@@ -672,11 +673,11 @@ private:
/// Tells if a given family/call/symbol is tracked by the current checker.
/// Sets CheckKind to the kind of the checker responsible for this
/// family/call/symbol.
- Optional<CheckKind> getCheckIfTracked(AllocationFamily Family,
- bool IsALeakCheck = false) const;
+ std::optional<CheckKind> getCheckIfTracked(AllocationFamily Family,
+ bool IsALeakCheck = false) const;
- Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
- bool IsALeakCheck = false) const;
+ std::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);
@@ -722,11 +723,204 @@ private:
bool isArgZERO_SIZE_PTR(ProgramStateRef State, CheckerContext &C,
SVal ArgVal) const;
};
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// Definition of NoOwnershipChangeVisitor.
+//===----------------------------------------------------------------------===//
+
+namespace {
+class NoOwnershipChangeVisitor final : public NoStateChangeFuncVisitor {
+ // The symbol whose (lack of) ownership change we are interested in.
+ SymbolRef Sym;
+ const MallocChecker &Checker;
+ using OwnerSet = llvm::SmallPtrSet<const MemRegion *, 8>;
+
+ // Collect which entities point to the allocated memory, and could be
+ // responsible for deallocating it.
+ class OwnershipBindingsHandler : public StoreManager::BindingsHandler {
+ SymbolRef Sym;
+ OwnerSet &Owners;
+
+ public:
+ OwnershipBindingsHandler(SymbolRef Sym, OwnerSet &Owners)
+ : Sym(Sym), Owners(Owners) {}
+
+ bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *Region,
+ SVal Val) override {
+ if (Val.getAsSymbol() == Sym)
+ Owners.insert(Region);
+ return true;
+ }
+
+ LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); }
+ LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &out) const {
+ out << "Owners: {\n";
+ for (const MemRegion *Owner : Owners) {
+ out << " ";
+ Owner->dumpToStream(out);
+ out << ",\n";
+ }
+ out << "}\n";
+ }
+ };
+
+protected:
+ OwnerSet getOwnersAtNode(const ExplodedNode *N) {
+ OwnerSet Ret;
+
+ ProgramStateRef State = N->getState();
+ OwnershipBindingsHandler Handler{Sym, Ret};
+ State->getStateManager().getStoreManager().iterBindings(State->getStore(),
+ Handler);
+ return Ret;
+ }
+
+ LLVM_DUMP_METHOD static std::string
+ getFunctionName(const ExplodedNode *CallEnterN) {
+ if (const CallExpr *CE = llvm::dyn_cast_or_null<CallExpr>(
+ CallEnterN->getLocationAs<CallEnter>()->getCallExpr()))
+ if (const FunctionDecl *FD = CE->getDirectCallee())
+ return FD->getQualifiedNameAsString();
+ return "";
+ }
+
+ /// Syntactically checks whether the callee is a deallocating function. Since
+ /// we have no path-sensitive information on this call (we would need a
+ /// CallEvent instead of a CallExpr for that), its possible that a
+ /// deallocation function was called indirectly through a function pointer,
+ /// but we are not able to tell, so this is a best effort analysis.
+ /// See namespace `memory_passed_to_fn_call_free_through_fn_ptr` in
+ /// clang/test/Analysis/NewDeleteLeaks.cpp.
+ bool isFreeingCallAsWritten(const CallExpr &Call) const {
+ if (Checker.FreeingMemFnMap.lookupAsWritten(Call) ||
+ Checker.ReallocatingMemFnMap.lookupAsWritten(Call))
+ return true;
+
+ if (const auto *Func =
+ llvm::dyn_cast_or_null<FunctionDecl>(Call.getCalleeDecl()))
+ return MallocChecker::isFreeingOwnershipAttrCall(Func);
+
+ return false;
+ }
+
+ /// Heuristically guess whether the callee intended to free memory. This is
+ /// done syntactically, because we are trying to argue about alternative
+ /// paths of execution, and as a consequence we don't have path-sensitive
+ /// information.
+ bool doesFnIntendToHandleOwnership(const Decl *Callee, ASTContext &ACtx) {
+ using namespace clang::ast_matchers;
+ const FunctionDecl *FD = dyn_cast<FunctionDecl>(Callee);
+
+ // Given that the stack frame was entered, the body should always be
+ // theoretically obtainable. In case of body farms, the synthesized body
+ // is not attached to declaration, thus triggering the '!FD->hasBody()'
+ // branch. That said, would a synthesized body ever intend to handle
+ // ownership? As of today they don't. And if they did, how would we
+ // put notes inside it, given that it doesn't match any source locations?
+ if (!FD || !FD->hasBody())
+ return false;
+
+ auto Matches = match(findAll(stmt(anyOf(cxxDeleteExpr().bind("delete"),
+ callExpr().bind("call")))),
+ *FD->getBody(), ACtx);
+ for (BoundNodes Match : Matches) {
+ if (Match.getNodeAs<CXXDeleteExpr>("delete"))
+ return true;
+
+ if (const auto *Call = Match.getNodeAs<CallExpr>("call"))
+ if (isFreeingCallAsWritten(*Call))
+ return true;
+ }
+ // TODO: Ownership might change with an attempt to store the allocated
+ // memory, not only through deallocation. Check for attempted stores as
+ // well.
+ return false;
+ }
+
+ bool wasModifiedInFunction(const ExplodedNode *CallEnterN,
+ const ExplodedNode *CallExitEndN) override {
+ if (!doesFnIntendToHandleOwnership(
+ CallExitEndN->getFirstPred()->getLocationContext()->getDecl(),
+ CallExitEndN->getState()->getAnalysisManager().getASTContext()))
+ return true;
+
+ if (CallEnterN->getState()->get<RegionState>(Sym) !=
+ CallExitEndN->getState()->get<RegionState>(Sym))
+ return true;
+
+ OwnerSet CurrOwners = getOwnersAtNode(CallEnterN);
+ OwnerSet ExitOwners = getOwnersAtNode(CallExitEndN);
+
+ // Owners in the current set may be purged from the analyzer later on.
+ // If a variable is dead (is not referenced directly or indirectly after
+ // some point), it will be removed from the Store before the end of its
+ // actual lifetime.
+ // This means that if the ownership status didn't change, CurrOwners
+ // must be a superset of, but not necessarily equal to ExitOwners.
+ return !llvm::set_is_subset(ExitOwners, CurrOwners);
+ }
+
+ static PathDiagnosticPieceRef emitNote(const ExplodedNode *N) {
+ PathDiagnosticLocation L = PathDiagnosticLocation::create(
+ N->getLocation(),
+ N->getState()->getStateManager().getContext().getSourceManager());
+ return std::make_shared<PathDiagnosticEventPiece>(
+ L, "Returning without deallocating memory or storing the pointer for "
+ "later deallocation");
+ }
+
+ PathDiagnosticPieceRef
+ maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
+ const ObjCMethodCall &Call,
+ const ExplodedNode *N) override {
+ // TODO: Implement.
+ return nullptr;
+ }
+
+ PathDiagnosticPieceRef
+ maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
+ const CXXConstructorCall &Call,
+ const ExplodedNode *N) override {
+ // TODO: Implement.
+ return nullptr;
+ }
+
+ PathDiagnosticPieceRef
+ maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call,
+ const ExplodedNode *N) override {
+ // TODO: Factor the logic of "what constitutes as an entity being passed
+ // into a function call" out by reusing the code in
+ // NoStoreFuncVisitor::maybeEmitNoteForParameters, maybe by incorporating
+ // the printing technology in UninitializedObject's FieldChainInfo.
+ ArrayRef<ParmVarDecl *> Parameters = Call.parameters();
+ for (unsigned I = 0; I < Call.getNumArgs() && I < Parameters.size(); ++I) {
+ SVal V = Call.getArgSVal(I);
+ if (V.getAsSymbol() == Sym)
+ return emitNote(N);
+ }
+ return nullptr;
+ }
+
+public:
+ NoOwnershipChangeVisitor(SymbolRef Sym, const MallocChecker *Checker)
+ : NoStateChangeFuncVisitor(bugreporter::TrackingKind::Thorough), Sym(Sym),
+ Checker(*Checker) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int Tag = 0;
+ ID.AddPointer(&Tag);
+ ID.AddPointer(Sym);
+ }
+};
+
+} // end anonymous namespace
//===----------------------------------------------------------------------===//
// Definition of MallocBugVisitor.
//===----------------------------------------------------------------------===//
+namespace {
/// The bug visitor which allows us to print extra diagnostics along the
/// BugReport path. For example, showing the allocation site of the leaked
/// region.
@@ -767,7 +961,7 @@ public:
/// Did not track -> allocated. Other state (released) -> allocated.
static inline bool isAllocated(const RefState *RSCurr, const RefState *RSPrev,
const Stmt *Stmt) {
- return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) &&
+ return (isa_and_nonnull<CallExpr, CXXNewExpr>(Stmt) &&
(RSCurr &&
(RSCurr->isAllocated() || RSCurr->isAllocatedOfSizeZero())) &&
(!RSPrev ||
@@ -780,8 +974,7 @@ public:
const Stmt *Stmt) {
bool IsReleased =
(RSCurr && RSCurr->isReleased()) && (!RSPrev || !RSPrev->isReleased());
- assert(!IsReleased ||
- (Stmt && (isa<CallExpr>(Stmt) || isa<CXXDeleteExpr>(Stmt))) ||
+ assert(!IsReleased || (isa_and_nonnull<CallExpr, CXXDeleteExpr>(Stmt)) ||
(!Stmt && RSCurr->getAllocationFamily() == AF_InnerBuffer));
return IsReleased;
}
@@ -789,11 +982,10 @@ public:
/// Did not track -> relinquished. Other state (allocated) -> relinquished.
static inline bool isRelinquished(const RefState *RSCurr,
const RefState *RSPrev, const Stmt *Stmt) {
- return (Stmt &&
- (isa<CallExpr>(Stmt) || isa<ObjCMessageExpr>(Stmt) ||
- isa<ObjCPropertyRefExpr>(Stmt)) &&
- (RSCurr && RSCurr->isRelinquished()) &&
- (!RSPrev || !RSPrev->isRelinquished()));
+ return (
+ isa_and_nonnull<CallExpr, ObjCMessageExpr, ObjCPropertyRefExpr>(Stmt) &&
+ (RSCurr && RSCurr->isRelinquished()) &&
+ (!RSPrev || !RSPrev->isRelinquished()));
}
/// If the expression is not a call, and the state change is
@@ -803,7 +995,7 @@ public:
static inline bool hasReallocFailed(const RefState *RSCurr,
const RefState *RSPrev,
const Stmt *Stmt) {
- return ((!Stmt || !isa<CallExpr>(Stmt)) &&
+ return ((!isa_and_nonnull<CallExpr>(Stmt)) &&
(RSCurr &&
(RSCurr->isAllocated() || RSCurr->isAllocatedOfSizeZero())) &&
(RSPrev &&
@@ -851,7 +1043,6 @@ private:
}
};
};
-
} // end anonymous namespace
// A map from the freed symbol to the symbol representing the return value of
@@ -894,12 +1085,8 @@ static bool isStandardNewDelete(const FunctionDecl *FD) {
// Methods of MallocChecker and MallocBugVisitor.
//===----------------------------------------------------------------------===//
-bool MallocChecker::isFreeingCall(const CallEvent &Call) const {
- if (FreeingMemFnMap.lookup(Call) || ReallocatingMemFnMap.lookup(Call))
- return true;
-
- const auto *Func = dyn_cast<FunctionDecl>(Call.getDecl());
- if (Func && Func->hasAttrs()) {
+bool MallocChecker::isFreeingOwnershipAttrCall(const FunctionDecl *Func) {
+ if (Func->hasAttrs()) {
for (const auto *I : Func->specific_attrs<OwnershipAttr>()) {
OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind();
if (OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds)
@@ -909,6 +1096,16 @@ bool MallocChecker::isFreeingCall(const CallEvent &Call) const {
return false;
}
+bool MallocChecker::isFreeingCall(const CallEvent &Call) const {
+ if (FreeingMemFnMap.lookup(Call) || ReallocatingMemFnMap.lookup(Call))
+ return true;
+
+ if (const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl()))
+ return isFreeingOwnershipAttrCall(Func);
+
+ return false;
+}
+
bool MallocChecker::isMemCall(const CallEvent &Call) const {
if (FreeingMemFnMap.lookup(Call) || AllocatingMemFnMap.lookup(Call) ||
ReallocatingMemFnMap.lookup(Call))
@@ -921,7 +1118,7 @@ bool MallocChecker::isMemCall(const CallEvent &Call) const {
return Func && Func->hasAttr<OwnershipAttr>();
}
-llvm::Optional<ProgramStateRef>
+std::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:
@@ -943,48 +1140,54 @@ MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C,
ASTContext &Ctx = C.getASTContext();
llvm::Triple::OSType OS = Ctx.getTargetInfo().getTriple().getOS();
- if (!KernelZeroFlagVal.hasValue()) {
- if (OS == llvm::Triple::FreeBSD)
+ if (!KernelZeroFlagVal) {
+ switch (OS) {
+ case llvm::Triple::FreeBSD:
KernelZeroFlagVal = 0x0100;
- else if (OS == llvm::Triple::NetBSD)
+ break;
+ case llvm::Triple::NetBSD:
KernelZeroFlagVal = 0x0002;
- else if (OS == llvm::Triple::OpenBSD)
+ break;
+ case llvm::Triple::OpenBSD:
KernelZeroFlagVal = 0x0008;
- else if (OS == llvm::Triple::Linux)
+ break;
+ case llvm::Triple::Linux:
// __GFP_ZERO
KernelZeroFlagVal = 0x8000;
- else
+ break;
+ default:
// FIXME: We need a more general way of getting the M_ZERO value.
// See also: O_CREAT in UnixAPIChecker.cpp.
// Fall back to normal malloc behavior on platforms where we don't
// know M_ZERO.
- return None;
+ return std::nullopt;
+ }
}
// 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 (Call.getNumArgs() < 2)
- return None;
+ return std::nullopt;
const Expr *FlagsEx = Call.getArgExpr(Call.getNumArgs() - 1);
const SVal V = C.getSVal(FlagsEx);
- if (!V.getAs<NonLoc>()) {
+ if (!isa<NonLoc>(V)) {
// The case where 'V' can be a location can only be due to a bad header,
// so in this case bail out.
- return None;
+ return std::nullopt;
}
NonLoc Flags = V.castAs<NonLoc>();
NonLoc ZeroFlag = C.getSValBuilder()
- .makeIntVal(KernelZeroFlagVal.getValue(), FlagsEx->getType())
- .castAs<NonLoc>();
+ .makeIntVal(*KernelZeroFlagVal, FlagsEx->getType())
+ .castAs<NonLoc>();
SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(State, BO_And,
Flags, ZeroFlag,
FlagsEx->getType());
if (MaskedFlagsUC.isUnknownOrUndef())
- return None;
+ return std::nullopt;
DefinedSVal MaskedFlags = MaskedFlagsUC.castAs<DefinedSVal>();
// Check if maskedFlags is non-zero.
@@ -998,7 +1201,7 @@ MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C,
AF_Malloc);
}
- return None;
+ return std::nullopt;
}
SVal MallocChecker::evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
@@ -1024,10 +1227,10 @@ void MallocChecker::checkBasicAlloc(const CallEvent &Call,
void MallocChecker::checkKernelMalloc(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
- llvm::Optional<ProgramStateRef> MaybeState =
+ std::optional<ProgramStateRef> MaybeState =
performKernelMalloc(Call, C, State);
- if (MaybeState.hasValue())
- State = MaybeState.getValue();
+ if (MaybeState)
+ State = *MaybeState;
else
State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State,
AF_Malloc);
@@ -1191,8 +1394,8 @@ void MallocChecker::checkGMalloc0(const CallEvent &Call,
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 =
+ MallocMemAux(C, Call, Call.getArgExpr(1), UnknownVal(), State, AF_Malloc);
State = ProcessZeroAllocCheck(Call, 1, State);
C.addTransition(State);
}
@@ -1294,7 +1497,7 @@ void MallocChecker::checkPostCall(const CallEvent &Call,
// Performs a 0-sized allocations check.
ProgramStateRef MallocChecker::ProcessZeroAllocCheck(
const CallEvent &Call, const unsigned IndexOfSizeArg, ProgramStateRef State,
- Optional<SVal> RetVal) {
+ std::optional<SVal> RetVal) {
if (!State)
return nullptr;
@@ -1446,7 +1649,7 @@ static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) {
FirstSlot == "initWithCharactersNoCopy";
}
-static Optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) {
+static std::optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) {
Selector S = Call.getSelector();
// FIXME: We should not rely on fully-constrained symbols being folded.
@@ -1454,7 +1657,7 @@ static Optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) {
if (S.getNameForSlot(i).equals("freeWhenDone"))
return !Call.getArgSVal(i).isZeroConstant();
- return None;
+ return std::nullopt;
}
void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
@@ -1465,7 +1668,7 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
if (!isKnownDeallocObjCMethodName(Call))
return;
- if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(Call))
+ if (std::optional<bool> FreeWhenDone = getFreeWhenDoneArg(Call))
if (!*FreeWhenDone)
return;
@@ -1476,7 +1679,7 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
ProgramStateRef State =
FreeMemAux(C, Call.getArgExpr(0), Call, C.getState(),
/*Hold=*/true, IsKnownToBeAllocatedMemory, AF_Malloc,
- /*RetNullOnFailure=*/true);
+ /*ReturnsNullOnFailure=*/true);
C.addTransition(State);
}
@@ -1491,9 +1694,9 @@ MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call,
if (Att->getModule()->getName() != "malloc")
return nullptr;
- OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
- if (I != E) {
- return MallocMemAux(C, Call, Call.getArgExpr(I->getASTIndex()),
+ if (!Att->args().empty()) {
+ return MallocMemAux(C, Call,
+ Call.getArgExpr(Att->args_begin()->getASTIndex()),
UndefinedVal(), State, AF_Malloc);
}
return MallocMemAux(C, Call, UnknownVal(), UndefinedVal(), State, AF_Malloc);
@@ -1525,21 +1728,27 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
return nullptr;
// Bind the return value to the symbolic value from the heap region.
- // TODO: We could rewrite post visit to eval call; 'malloc' does not have
- // side effects other than what we model here.
+ // TODO: move use of this functions to an EvalCall callback, becasue
+ // BindExpr() should'nt be used elsewhere.
unsigned Count = C.blockCount();
- SValBuilder &svalBuilder = C.getSValBuilder();
+ SValBuilder &SVB = C.getSValBuilder();
const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
- DefinedSVal RetVal = svalBuilder.getConjuredHeapSymbolVal(CE, LCtx, Count)
- .castAs<DefinedSVal>();
+ DefinedSVal RetVal =
+ ((Family == AF_Alloca) ? SVB.getAllocaRegionVal(CE, LCtx, Count)
+ : SVB.getConjuredHeapSymbolVal(CE, LCtx, Count)
+ .castAs<DefinedSVal>());
State = State->BindExpr(CE, C.getLocationContext(), RetVal);
// Fill the region with the initialization value.
State = State->bindDefaultInitial(RetVal, Init, LCtx);
+ // If Size is somehow undefined at this point, this line prevents a crash.
+ if (Size.isUndef())
+ Size = UnknownVal();
+
// Set the region's extent.
State = setDynamicExtent(State, RetVal.getAsRegion(),
- Size.castAs<DefinedOrUnknownSVal>(), svalBuilder);
+ Size.castAs<DefinedOrUnknownSVal>(), SVB);
return MallocUpdateRefState(C, CE, State, Family);
}
@@ -1547,7 +1756,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E,
ProgramStateRef State,
AllocationFamily Family,
- Optional<SVal> RetVal) {
+ std::optional<SVal> RetVal) {
if (!State)
return nullptr;
@@ -1695,12 +1904,12 @@ ProgramStateRef MallocChecker::FreeMemAux(
return nullptr;
SVal ArgVal = C.getSVal(ArgExpr);
- if (!ArgVal.getAs<DefinedOrUnknownSVal>())
+ if (!isa<DefinedOrUnknownSVal>(ArgVal))
return nullptr;
DefinedOrUnknownSVal location = ArgVal.castAs<DefinedOrUnknownSVal>();
// Check for null dereferences.
- if (!location.getAs<Loc>())
+ if (!isa<Loc>(location))
return nullptr;
// The explicit NULL case, no operation is performed.
@@ -1753,14 +1962,11 @@ ProgramStateRef MallocChecker::FreeMemAux(
// Parameters, locals, statics, globals, and memory returned by
// __builtin_alloca() shouldn't be freed.
- if (!(isa<UnknownSpaceRegion>(MS) || isa<HeapSpaceRegion>(MS))) {
- // FIXME: at the time this code was written, malloc() regions were
- // represented by conjured symbols, which are all in UnknownSpaceRegion.
- // This means that there isn't actually anything from HeapSpaceRegion
- // that should be freed, even though we allow it here.
- // Of course, free() can work on memory allocated outside the current
- // function, so UnknownSpaceRegion is always a possibility.
- // False negatives are better than false positives.
+ if (!isa<UnknownSpaceRegion, HeapSpaceRegion>(MS)) {
+ // Regions returned by malloc() are represented by SymbolicRegion objects
+ // within HeapSpaceRegion. Of course, free() can work on memory allocated
+ // outside the current function, so UnknownSpaceRegion is also a
+ // possibility here.
if (isa<AllocaRegion>(R))
HandleFreeAlloca(C, ArgVal, ArgExpr->getSourceRange());
@@ -1862,7 +2068,7 @@ ProgramStateRef MallocChecker::FreeMemAux(
RefState::getReleased(Family, ParentExpr));
}
-Optional<MallocChecker::CheckKind>
+std::optional<MallocChecker::CheckKind>
MallocChecker::getCheckIfTracked(AllocationFamily Family,
bool IsALeakCheck) const {
switch (Family) {
@@ -1871,7 +2077,7 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family,
case AF_IfNameIndex: {
if (ChecksEnabled[CK_MallocChecker])
return CK_MallocChecker;
- return None;
+ return std::nullopt;
}
case AF_CXXNew:
case AF_CXXNewArray: {
@@ -1883,12 +2089,12 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family,
if (ChecksEnabled[CK_NewDeleteChecker])
return CK_NewDeleteChecker;
}
- return None;
+ return std::nullopt;
}
case AF_InnerBuffer: {
if (ChecksEnabled[CK_InnerPointerChecker])
return CK_InnerPointerChecker;
- return None;
+ return std::nullopt;
}
case AF_None: {
llvm_unreachable("no family");
@@ -1897,7 +2103,7 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family,
llvm_unreachable("unhandled family");
}
-Optional<MallocChecker::CheckKind>
+std::optional<MallocChecker::CheckKind>
MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
bool IsALeakCheck) const {
if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym))
@@ -1909,11 +2115,13 @@ MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
}
bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {
- if (Optional<nonloc::ConcreteInt> IntVal = V.getAs<nonloc::ConcreteInt>())
+ if (std::optional<nonloc::ConcreteInt> IntVal =
+ V.getAs<nonloc::ConcreteInt>())
os << "an integer (" << IntVal->getValue() << ")";
- else if (Optional<loc::ConcreteInt> ConstAddr = V.getAs<loc::ConcreteInt>())
+ else if (std::optional<loc::ConcreteInt> ConstAddr =
+ V.getAs<loc::ConcreteInt>())
os << "a constant address (" << ConstAddr->getValue() << ")";
- else if (Optional<loc::GotoLabel> Label = V.getAs<loc::GotoLabel>())
+ else if (std::optional<loc::GotoLabel> Label = V.getAs<loc::GotoLabel>())
os << "the address of the label '" << Label->getLabel()->getName() << "'";
else
return false;
@@ -2005,8 +2213,8 @@ void MallocChecker::HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal,
return;
}
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
- if (!CheckKind.hasValue())
+ std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
+ if (!CheckKind)
return;
if (ExplodedNode *N = C.generateErrorNode()) {
@@ -2046,7 +2254,7 @@ void MallocChecker::HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal,
void MallocChecker::HandleFreeAlloca(CheckerContext &C, SVal ArgVal,
SourceRange Range) const {
- Optional<MallocChecker::CheckKind> CheckKind;
+ std::optional<MallocChecker::CheckKind> CheckKind;
if (ChecksEnabled[CK_MallocChecker])
CheckKind = CK_MallocChecker;
@@ -2138,8 +2346,8 @@ void MallocChecker::HandleOffsetFree(CheckerContext &C, SVal ArgVal,
return;
}
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
- if (!CheckKind.hasValue())
+ std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
+ if (!CheckKind)
return;
ExplodedNode *N = C.generateErrorNode();
@@ -2195,8 +2403,8 @@ void MallocChecker::HandleUseAfterFree(CheckerContext &C, SourceRange Range,
return;
}
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
- if (!CheckKind.hasValue())
+ std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+ if (!CheckKind)
return;
if (ExplodedNode *N = C.generateErrorNode()) {
@@ -2234,8 +2442,8 @@ void MallocChecker::HandleDoubleFree(CheckerContext &C, SourceRange Range,
return;
}
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
- if (!CheckKind.hasValue())
+ std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+ if (!CheckKind)
return;
if (ExplodedNode *N = C.generateErrorNode()) {
@@ -2264,8 +2472,8 @@ void MallocChecker::HandleDoubleDelete(CheckerContext &C, SymbolRef Sym) const {
return;
}
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
- if (!CheckKind.hasValue())
+ std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+ if (!CheckKind)
return;
if (ExplodedNode *N = C.generateErrorNode()) {
@@ -2291,9 +2499,9 @@ void MallocChecker::HandleUseZeroAlloc(CheckerContext &C, SourceRange Range,
return;
}
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+ std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
- if (!CheckKind.hasValue())
+ if (!CheckKind)
return;
if (ExplodedNode *N = C.generateErrorNode()) {
@@ -2303,7 +2511,8 @@ void MallocChecker::HandleUseZeroAlloc(CheckerContext &C, SourceRange Range,
categories::MemoryError));
auto R = std::make_unique<PathSensitiveBugReport>(
- *BT_UseZerroAllocated[*CheckKind], "Use of zero-allocated memory", N);
+ *BT_UseZerroAllocated[*CheckKind],
+ "Use of memory allocated with size zero", N);
R->addRange(Range);
if (Sym) {
@@ -2323,8 +2532,8 @@ void MallocChecker::HandleFunctionPtrFree(CheckerContext &C, SVal ArgVal,
return;
}
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
- if (!CheckKind.hasValue())
+ std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
+ if (!CheckKind)
return;
if (ExplodedNode *N = C.generateErrorNode()) {
@@ -2369,14 +2578,14 @@ MallocChecker::ReallocMemAux(CheckerContext &C, const CallEvent &Call,
const Expr *arg0Expr = CE->getArg(0);
SVal Arg0Val = C.getSVal(arg0Expr);
- if (!Arg0Val.getAs<DefinedOrUnknownSVal>())
+ if (!isa<DefinedOrUnknownSVal>(Arg0Val))
return nullptr;
DefinedOrUnknownSVal arg0Val = Arg0Val.castAs<DefinedOrUnknownSVal>();
SValBuilder &svalBuilder = C.getSValBuilder();
- DefinedOrUnknownSVal PtrEQ =
- svalBuilder.evalEQ(State, arg0Val, svalBuilder.makeNull());
+ DefinedOrUnknownSVal PtrEQ = svalBuilder.evalEQ(
+ State, arg0Val, svalBuilder.makeNullWithType(arg0Expr->getType()));
// Get the size argument.
const Expr *Arg1 = CE->getArg(1);
@@ -2385,13 +2594,14 @@ MallocChecker::ReallocMemAux(CheckerContext &C, const CallEvent &Call,
SVal TotalSize = C.getSVal(Arg1);
if (SuffixWithN)
TotalSize = evalMulForBufferSize(C, Arg1, CE->getArg(2));
- if (!TotalSize.getAs<DefinedOrUnknownSVal>())
+ if (!isa<DefinedOrUnknownSVal>(TotalSize))
return nullptr;
// Compare the size argument to 0.
DefinedOrUnknownSVal SizeZero =
- svalBuilder.evalEQ(State, TotalSize.castAs<DefinedOrUnknownSVal>(),
- svalBuilder.makeIntValWithPtrWidth(0, false));
+ svalBuilder.evalEQ(State, TotalSize.castAs<DefinedOrUnknownSVal>(),
+ svalBuilder.makeIntValWithWidth(
+ svalBuilder.getContext().getSizeType(), 0));
ProgramStateRef StatePtrIsNull, StatePtrNotNull;
std::tie(StatePtrIsNull, StatePtrNotNull) = State->assume(PtrEQ);
@@ -2533,10 +2743,10 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N,
if (Family == AF_Alloca)
return;
- Optional<MallocChecker::CheckKind>
- CheckKind = getCheckIfTracked(Family, true);
+ std::optional<MallocChecker::CheckKind> CheckKind =
+ getCheckIfTracked(Family, true);
- if (!CheckKind.hasValue())
+ if (!CheckKind)
return;
assert(N);
@@ -2579,6 +2789,8 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N,
AllocNode->getLocationContext()->getDecl());
R->markInteresting(Sym);
R->addVisitor<MallocBugVisitor>(Sym, true);
+ if (ShouldRegisterNoOwnershipChangeVisitor)
+ R->addVisitor<NoOwnershipChangeVisitor>(Sym, this);
C.emitReport(std::move(R));
}
@@ -2591,12 +2803,12 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
RegionStateTy RS = OldRS;
SmallVector<SymbolRef, 2> Errors;
- for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {
- if (SymReaper.isDead(I->first)) {
- if (I->second.isAllocated() || I->second.isAllocatedOfSizeZero())
- Errors.push_back(I->first);
+ for (auto [Sym, State] : RS) {
+ if (SymReaper.isDead(Sym)) {
+ if (State.isAllocated() || State.isAllocatedOfSizeZero())
+ Errors.push_back(Sym);
// Remove the dead symbol from the map.
- RS = F.remove(RS, I->first);
+ RS = F.remove(RS, Sym);
}
}
@@ -2611,19 +2823,17 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
// Cleanup the Realloc Pairs Map.
ReallocPairsTy RP = state->get<ReallocPairs>();
- for (ReallocPairsTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) {
- if (SymReaper.isDead(I->first) ||
- SymReaper.isDead(I->second.ReallocatedSym)) {
- state = state->remove<ReallocPairs>(I->first);
+ for (auto [Sym, ReallocPair] : RP) {
+ if (SymReaper.isDead(Sym) || SymReaper.isDead(ReallocPair.ReallocatedSym)) {
+ state = state->remove<ReallocPairs>(Sym);
}
}
// Cleanup the FreeReturnValue Map.
FreeReturnValueTy FR = state->get<FreeReturnValue>();
- for (FreeReturnValueTy::iterator I = FR.begin(), E = FR.end(); I != E; ++I) {
- if (SymReaper.isDead(I->first) ||
- SymReaper.isDead(I->second)) {
- state = state->remove<FreeReturnValue>(I->first);
+ for (auto [Sym, RetSym] : FR) {
+ if (SymReaper.isDead(Sym) || SymReaper.isDead(RetSym)) {
+ state = state->remove<FreeReturnValue>(Sym);
}
}
@@ -2633,9 +2843,8 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
static CheckerProgramPointTag Tag("MallocChecker", "DeadSymbolsLeak");
N = C.generateNonFatalErrorNode(C.getState(), &Tag);
if (N) {
- for (SmallVectorImpl<SymbolRef>::iterator
- I = Errors.begin(), E = Errors.end(); I != E; ++I) {
- HandleLeak(*I, N, C);
+ for (SymbolRef Sym : Errors) {
+ HandleLeak(Sym, N, C);
}
}
}
@@ -2692,7 +2901,7 @@ void MallocChecker::checkPreCall(const CallEvent &Call,
// Check arguments for being used after free.
for (unsigned I = 0, E = Call.getNumArgs(); I != E; ++I) {
SVal ArgSVal = Call.getArgSVal(I);
- if (ArgSVal.getAs<Loc>()) {
+ if (isa<Loc>(ArgSVal)) {
SymbolRef Sym = ArgSVal.getAsSymbol();
if (!Sym)
continue;
@@ -2733,7 +2942,7 @@ void MallocChecker::checkEscapeOnReturn(const ReturnStmt *S,
// the callee could still free the memory.
// TODO: This logic should be a part of generic symbol escape callback.
if (const MemRegion *MR = RetVal.getAsRegion())
- if (isa<FieldRegion>(MR) || isa<ElementRegion>(MR))
+ if (isa<FieldRegion, ElementRegion>(MR))
if (const SymbolicRegion *BMR =
dyn_cast<SymbolicRegion>(MR->getBaseRegion()))
Sym = BMR->getSymbol();
@@ -2758,18 +2967,16 @@ void MallocChecker::checkPostStmt(const BlockExpr *BE,
const BlockDataRegion *R =
cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
- BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
- E = R->referenced_vars_end();
-
- if (I == E)
+ auto ReferencedVars = R->referenced_vars();
+ if (ReferencedVars.empty())
return;
SmallVector<const MemRegion*, 10> Regions;
const LocationContext *LC = C.getLocationContext();
MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
- for ( ; I != E; ++I) {
- const VarRegion *VR = I.getCapturedRegion();
+ for (const auto &Var : ReferencedVars) {
+ const VarRegion *VR = Var.getCapturedRegion();
if (VR->getSuperRegion() == R) {
VR = MemMgr.getVarRegion(VR->getDecl(), LC);
}
@@ -2865,28 +3072,28 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
SVal Cond,
bool Assumption) const {
RegionStateTy RS = state->get<RegionState>();
- for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {
+ for (SymbolRef Sym : llvm::make_first_range(RS)) {
// If the symbol is assumed to be NULL, remove it from consideration.
ConstraintManager &CMgr = state->getConstraintManager();
- ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey());
+ ConditionTruthVal AllocFailed = CMgr.isNull(state, Sym);
if (AllocFailed.isConstrainedTrue())
- state = state->remove<RegionState>(I.getKey());
+ state = state->remove<RegionState>(Sym);
}
// Realloc returns 0 when reallocation fails, which means that we should
// restore the state of the pointer being reallocated.
ReallocPairsTy RP = state->get<ReallocPairs>();
- for (ReallocPairsTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) {
+ for (auto [Sym, ReallocPair] : RP) {
// If the symbol is assumed to be NULL, remove it from consideration.
ConstraintManager &CMgr = state->getConstraintManager();
- ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey());
+ ConditionTruthVal AllocFailed = CMgr.isNull(state, Sym);
if (!AllocFailed.isConstrainedTrue())
continue;
- SymbolRef ReallocSym = I.getData().ReallocatedSym;
+ SymbolRef ReallocSym = ReallocPair.ReallocatedSym;
if (const RefState *RS = state->get<RegionState>(ReallocSym)) {
if (RS->isReleased()) {
- switch (I.getData().Kind) {
+ switch (ReallocPair.Kind) {
case OAR_ToBeFreedAfterFailure:
state = state->set<RegionState>(ReallocSym,
RefState::getAllocated(RS->getAllocationFamily(), RS->getStmt()));
@@ -2895,11 +3102,11 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
state = state->remove<RegionState>(ReallocSym);
break;
default:
- assert(I.getData().Kind == OAR_FreeOnFailure);
+ assert(ReallocPair.Kind == OAR_FreeOnFailure);
}
}
}
- state = state->remove<ReallocPairs>(I.getKey());
+ state = state->remove<ReallocPairs>(Sym);
}
return state;
@@ -2916,7 +3123,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
// TODO: If we want to be more optimistic here, we'll need to make sure that
// regions escape to C++ containers. They seem to do that even now, but for
// mysterious reasons.
- if (!(isa<SimpleFunctionCall>(Call) || isa<ObjCMethodCall>(Call)))
+ if (!isa<SimpleFunctionCall, ObjCMethodCall>(Call))
return true;
// Check Objective-C messages by selector name.
@@ -2935,7 +3142,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
// about, we can't be sure that the object will use free() to deallocate the
// memory, so we can't model it explicitly. The best we can do is use it to
// decide whether the pointer escapes.
- if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg))
+ if (std::optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg))
return *FreeWhenDone;
// If the first selector piece ends with "NoCopy", and there is no
@@ -2943,16 +3150,16 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
// transferred. Again, though, we can't be sure that the object will use
// free() to deallocate the memory, so we can't model it explicitly.
StringRef FirstSlot = Msg->getSelector().getNameForSlot(0);
- if (FirstSlot.endswith("NoCopy"))
+ if (FirstSlot.ends_with("NoCopy"))
return true;
// If the first selector starts with addPointer, insertPointer,
// or replacePointer, assume we are dealing with NSPointerArray or similar.
// This is similar to C++ containers (vector); we still might want to check
// that the pointers get freed by following the container itself.
- if (FirstSlot.startswith("addPointer") ||
- FirstSlot.startswith("insertPointer") ||
- FirstSlot.startswith("replacePointer") ||
+ if (FirstSlot.starts_with("addPointer") ||
+ FirstSlot.starts_with("insertPointer") ||
+ FirstSlot.starts_with("replacePointer") ||
FirstSlot.equals("valueWithPointer")) {
return true;
}
@@ -2992,7 +3199,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
// White list the 'XXXNoCopy' CoreFoundation functions.
// We specifically check these before
- if (FName.endswith("NoCopy")) {
+ if (FName.ends_with("NoCopy")) {
// Look for the deallocator argument. We know that the memory ownership
// is not transferred only if the deallocator argument is
// 'kCFAllocatorNull'.
@@ -3024,7 +3231,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
const Expr *ArgE = Call->getArgExpr(0)->IgnoreParenCasts();
if (const DeclRefExpr *ArgDRE = dyn_cast<DeclRefExpr>(ArgE))
if (const VarDecl *D = dyn_cast<VarDecl>(ArgDRE->getDecl()))
- if (D->getCanonicalDecl()->getName().find("std") != StringRef::npos)
+ if (D->getCanonicalDecl()->getName().contains("std"))
return true;
}
}
@@ -3052,6 +3259,11 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
return true;
}
+ if (FName == "singleShotImpl" &&
+ FD->getQualifiedNameAsString() == "QTimer::singleShotImpl") {
+ return true;
+ }
+
// Handle cases where we know a buffer's /address/ can escape.
// Note that the above checks handle some special cases where we know that
// even though the address escapes, it's still our responsibility to free the
@@ -3100,11 +3312,7 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(
return State;
}
- for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
- E = Escaped.end();
- I != E; ++I) {
- SymbolRef sym = *I;
-
+ for (SymbolRef sym : Escaped) {
if (EscapingSymbol && EscapingSymbol != sym)
continue;
@@ -3224,7 +3432,7 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
allocation_state::getContainerObjRegion(statePrev, Sym);
const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion);
QualType ObjTy = TypedRegion->getValueType();
- OS << "Inner buffer of '" << ObjTy.getAsString() << "' ";
+ OS << "Inner buffer of '" << ObjTy << "' ";
if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) {
OS << "deallocated by call to destructor";
@@ -3239,7 +3447,8 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
OS << OpCallE->getDirectCallee()->getDeclName();
} else if (const auto *CallE = dyn_cast<CallExpr>(S)) {
auto &CEMgr = BRC.getStateManager().getCallEventManager();
- CallEventRef<> Call = CEMgr.getSimpleCall(CallE, state, CurrentLC);
+ CallEventRef<> Call =
+ CEMgr.getSimpleCall(CallE, state, CurrentLC, {nullptr, 0});
if (const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl()))
OS << D->getDeclName();
else
@@ -3351,17 +3560,18 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,
if (!RS.isEmpty()) {
Out << Sep << "MallocChecker :" << NL;
- for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {
- const RefState *RefS = State->get<RegionState>(I.getKey());
+ for (auto [Sym, Data] : RS) {
+ const RefState *RefS = State->get<RegionState>(Sym);
AllocationFamily Family = RefS->getAllocationFamily();
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
- if (!CheckKind.hasValue())
- CheckKind = getCheckIfTracked(Family, true);
+ std::optional<MallocChecker::CheckKind> CheckKind =
+ getCheckIfTracked(Family);
+ if (!CheckKind)
+ CheckKind = getCheckIfTracked(Family, true);
- I.getKey()->dumpToStream(Out);
+ Sym->dumpToStream(Out);
Out << " : ";
- I.getData().dump(Out);
- if (CheckKind.hasValue())
+ Data.dump(Out);
+ if (CheckKind)
Out << " (" << CheckNames[*CheckKind].getName() << ")";
Out << NL;
}
@@ -3395,6 +3605,9 @@ void ento::registerDynamicMemoryModeling(CheckerManager &mgr) {
auto *checker = mgr.registerChecker<MallocChecker>();
checker->ShouldIncludeOwnershipAnnotatedFunctions =
mgr.getAnalyzerOptions().getCheckerBooleanOption(checker, "Optimistic");
+ checker->ShouldRegisterNoOwnershipChangeVisitor =
+ mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ checker, "AddNoOwnershipChangeNotes");
}
bool ento::shouldRegisterDynamicMemoryModeling(const CheckerManager &mgr) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
index e31630f63b5a..3c8b38973c6b 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
@@ -24,6 +24,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/SmallVector.h"
+#include <optional>
#include <utility>
using namespace clang;
@@ -32,12 +33,14 @@ using llvm::APSInt;
namespace {
struct MallocOverflowCheck {
+ const CallExpr *call;
const BinaryOperator *mulop;
const Expr *variable;
APSInt maxVal;
- MallocOverflowCheck(const BinaryOperator *m, const Expr *v, APSInt val)
- : mulop(m), variable(v), maxVal(std::move(val)) {}
+ MallocOverflowCheck(const CallExpr *call, const BinaryOperator *m,
+ const Expr *v, APSInt val)
+ : call(call), mulop(m), variable(v), maxVal(std::move(val)) {}
};
class MallocOverflowSecurityChecker : public Checker<check::ASTCodeBody> {
@@ -46,8 +49,8 @@ public:
BugReporter &BR) const;
void CheckMallocArgument(
- SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
- const Expr *TheArgument, ASTContext &Context) const;
+ SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
+ const CallExpr *TheCall, ASTContext &Context) const;
void OutputPossibleOverflows(
SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
@@ -62,16 +65,15 @@ static inline bool EvaluatesToZero(APSInt &Val, BinaryOperatorKind op) {
}
void MallocOverflowSecurityChecker::CheckMallocArgument(
- SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
- const Expr *TheArgument,
- ASTContext &Context) const {
+ SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
+ const CallExpr *TheCall, ASTContext &Context) const {
/* Look for a linear combination with a single variable, and at least
one multiplication.
Reject anything that applies to the variable: an explicit cast,
conditional expression, an operation that could reduce the range
of the result, or anything too complicated :-). */
- const Expr *e = TheArgument;
+ const Expr *e = TheCall->getArg(0);
const BinaryOperator * mulop = nullptr;
APSInt maxVal;
@@ -101,8 +103,7 @@ void MallocOverflowSecurityChecker::CheckMallocArgument(
e = rhs;
} else
return;
- }
- else if (isa<DeclRefExpr>(e) || isa<MemberExpr>(e))
+ } else if (isa<DeclRefExpr, MemberExpr>(e))
break;
else
return;
@@ -115,9 +116,8 @@ void MallocOverflowSecurityChecker::CheckMallocArgument(
// the data so when the body of the function is completely available
// we can check for comparisons.
- // TODO: Could push this into the innermost scope where 'e' is
- // defined, rather than the whole function.
- PossibleMallocOverflows.push_back(MallocOverflowCheck(mulop, e, maxVal));
+ PossibleMallocOverflows.push_back(
+ MallocOverflowCheck(TheCall, mulop, e, maxVal));
}
namespace {
@@ -153,17 +153,19 @@ private:
return getDecl(CheckDR) == getDecl(DR) && Pred(Check);
return false;
};
- toScanFor.erase(std::remove_if(toScanFor.begin(), toScanFor.end(), P),
- toScanFor.end());
+ llvm::erase_if(toScanFor, P);
}
void CheckExpr(const Expr *E_p) {
- auto PredTrue = [](const MallocOverflowCheck &) { return true; };
const Expr *E = E_p->IgnoreParenImpCasts();
+ const auto PrecedesMalloc = [E, this](const MallocOverflowCheck &c) {
+ return Context.getSourceManager().isBeforeInTranslationUnit(
+ E->getExprLoc(), c.call->getExprLoc());
+ };
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E))
- Erase<DeclRefExpr>(DR, PredTrue);
+ Erase<DeclRefExpr>(DR, PrecedesMalloc);
else if (const auto *ME = dyn_cast<MemberExpr>(E)) {
- Erase<MemberExpr>(ME, PredTrue);
+ Erase<MemberExpr>(ME, PrecedesMalloc);
}
}
@@ -278,17 +280,13 @@ void MallocOverflowSecurityChecker::OutputPossibleOverflows(
c.Visit(mgr.getAnalysisDeclContext(D)->getBody());
// Output warnings for all overflows that are left.
- for (CheckOverflowOps::theVecType::iterator
- i = PossibleMallocOverflows.begin(),
- e = PossibleMallocOverflows.end();
- i != e;
- ++i) {
+ for (const MallocOverflowCheck &Check : PossibleMallocOverflows) {
BR.EmitBasicReport(
D, this, "malloc() size overflow", categories::UnixAPI,
"the computation of the size of the memory allocation may overflow",
- PathDiagnosticLocation::createOperatorLoc(i->mulop,
+ PathDiagnosticLocation::createOperatorLoc(Check.mulop,
BR.getSourceManager()),
- i->mulop->getSourceRange());
+ Check.mulop->getSourceRange());
}
}
@@ -307,26 +305,27 @@ void MallocOverflowSecurityChecker::checkASTCodeBody(const Decl *D,
CFGBlock *block = *it;
for (CFGBlock::iterator bi = block->begin(), be = block->end();
bi != be; ++bi) {
- if (Optional<CFGStmt> CS = bi->getAs<CFGStmt>()) {
- if (const CallExpr *TheCall = dyn_cast<CallExpr>(CS->getStmt())) {
- // Get the callee.
- const FunctionDecl *FD = TheCall->getDirectCallee();
-
- if (!FD)
- continue;
-
- // Get the name of the callee. If it's a builtin, strip off the prefix.
- IdentifierInfo *FnInfo = FD->getIdentifier();
- if (!FnInfo)
- continue;
-
- if (FnInfo->isStr ("malloc") || FnInfo->isStr ("_MALLOC")) {
- if (TheCall->getNumArgs() == 1)
- CheckMallocArgument(PossibleMallocOverflows, TheCall->getArg(0),
- mgr.getASTContext());
+ if (std::optional<CFGStmt> CS = bi->getAs<CFGStmt>()) {
+ if (const CallExpr *TheCall = dyn_cast<CallExpr>(CS->getStmt())) {
+ // Get the callee.
+ const FunctionDecl *FD = TheCall->getDirectCallee();
+
+ if (!FD)
+ continue;
+
+ // Get the name of the callee. If it's a builtin, strip off the
+ // prefix.
+ IdentifierInfo *FnInfo = FD->getIdentifier();
+ if (!FnInfo)
+ continue;
+
+ if (FnInfo->isStr("malloc") || FnInfo->isStr("_MALLOC")) {
+ if (TheCall->getNumArgs() == 1)
+ CheckMallocArgument(PossibleMallocOverflows, TheCall,
+ mgr.getASTContext());
+ }
}
}
- }
}
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
index 4b5206a102b8..9e81a6bd19fc 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
@@ -12,14 +12,15 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/TypeLoc.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
@@ -182,22 +183,20 @@ public:
AnalysisDeclContext *ADC = mgr.getAnalysisDeclContext(D);
CastedAllocFinder Finder(&BR.getContext());
Finder.Visit(D->getBody());
- for (CastedAllocFinder::CallVec::iterator i = Finder.Calls.begin(),
- e = Finder.Calls.end(); i != e; ++i) {
- QualType CastedType = i->CastedExpr->getType();
+ for (const auto &CallRec : Finder.Calls) {
+ QualType CastedType = CallRec.CastedExpr->getType();
if (!CastedType->isPointerType())
continue;
QualType PointeeType = CastedType->getPointeeType();
if (PointeeType->isVoidType())
continue;
- for (CallExpr::const_arg_iterator ai = i->AllocCall->arg_begin(),
- ae = i->AllocCall->arg_end(); ai != ae; ++ai) {
- if (!(*ai)->getType()->isIntegralOrUnscopedEnumerationType())
+ for (const Expr *Arg : CallRec.AllocCall->arguments()) {
+ if (!Arg->getType()->isIntegralOrUnscopedEnumerationType())
continue;
SizeofFinder SFinder;
- SFinder.Visit(*ai);
+ SFinder.Visit(Arg);
if (SFinder.Sizeofs.size() != 1)
continue;
@@ -212,34 +211,33 @@ public:
continue;
const TypeSourceInfo *TSI = nullptr;
- if (i->CastedExprParent.is<const VarDecl *>()) {
- TSI =
- i->CastedExprParent.get<const VarDecl *>()->getTypeSourceInfo();
+ if (CallRec.CastedExprParent.is<const VarDecl *>()) {
+ TSI = CallRec.CastedExprParent.get<const VarDecl *>()
+ ->getTypeSourceInfo();
} else {
- TSI = i->ExplicitCastType;
+ TSI = CallRec.ExplicitCastType;
}
SmallString<64> buf;
llvm::raw_svector_ostream OS(buf);
OS << "Result of ";
- const FunctionDecl *Callee = i->AllocCall->getDirectCallee();
+ const FunctionDecl *Callee = CallRec.AllocCall->getDirectCallee();
if (Callee && Callee->getIdentifier())
OS << '\'' << Callee->getIdentifier()->getName() << '\'';
else
OS << "call";
- OS << " is converted to a pointer of type '"
- << PointeeType.getAsString() << "', which is incompatible with "
- << "sizeof operand type '" << SizeofType.getAsString() << "'";
+ OS << " is converted to a pointer of type '" << PointeeType
+ << "', which is incompatible with "
+ << "sizeof operand type '" << SizeofType << "'";
SmallVector<SourceRange, 4> Ranges;
- Ranges.push_back(i->AllocCall->getCallee()->getSourceRange());
+ Ranges.push_back(CallRec.AllocCall->getCallee()->getSourceRange());
Ranges.push_back(SFinder.Sizeofs[0]->getSourceRange());
if (TSI)
Ranges.push_back(TSI->getTypeLoc().getSourceRange());
- PathDiagnosticLocation L =
- PathDiagnosticLocation::createBegin(i->AllocCall->getCallee(),
- BR.getSourceManager(), ADC);
+ PathDiagnosticLocation L = PathDiagnosticLocation::createBegin(
+ CallRec.AllocCall->getCallee(), BR.getSourceManager(), ADC);
BR.EmitBasicReport(D, this, "Allocator sizeof operand mismatch",
categories::UnixAPI, OS.str(), L, Ranges);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp
index 1960873599f7..82a622831817 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp
@@ -30,22 +30,18 @@ namespace {
class MismatchedIteratorChecker
: public Checker<check::PreCall, check::PreStmt<BinaryOperator>> {
- std::unique_ptr<BugType> MismatchedBugType;
-
- void verifyMatch(CheckerContext &C, const SVal &Iter,
- const MemRegion *Cont) const;
- void verifyMatch(CheckerContext &C, const SVal &Iter1,
- const SVal &Iter2) const;
- void reportBug(const StringRef &Message, const SVal &Val1,
- const SVal &Val2, CheckerContext &C,
- ExplodedNode *ErrNode) const;
- void reportBug(const StringRef &Message, const SVal &Val,
- const MemRegion *Reg, CheckerContext &C,
+ const BugType MismatchedBugType{this, "Iterator(s) mismatched",
+ "Misuse of STL APIs",
+ /*SuppressOnSink=*/true};
+
+ void verifyMatch(CheckerContext &C, SVal Iter, const MemRegion *Cont) const;
+ void verifyMatch(CheckerContext &C, SVal Iter1, SVal Iter2) const;
+ void reportBug(StringRef Message, SVal Val1, SVal Val2, CheckerContext &C,
ExplodedNode *ErrNode) const;
+ void reportBug(StringRef Message, SVal Val, const MemRegion *Reg,
+ CheckerContext &C, ExplodedNode *ErrNode) const;
public:
- MismatchedIteratorChecker();
-
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const;
@@ -53,12 +49,6 @@ public:
} // namespace
-MismatchedIteratorChecker::MismatchedIteratorChecker() {
- MismatchedBugType.reset(
- new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs",
- /*SuppressOnSink=*/true));
-}
-
void MismatchedIteratorChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
// Check for iterator mismatches
@@ -176,8 +166,10 @@ void MismatchedIteratorChecker::checkPreCall(const CallEvent &Call,
const auto *Param = Func->getParamDecl(J);
const auto *ParamType =
Param->getType()->getAs<SubstTemplateTypeParmType>();
- if (!ParamType ||
- ParamType->getReplacedParameter()->getDecl() != TPDecl)
+ if (!ParamType)
+ continue;
+ const TemplateTypeParmDecl *D = ParamType->getReplacedParameter();
+ if (D != TPDecl)
continue;
if (LHS.isUndef()) {
LHS = Call.getArgSVal(J);
@@ -200,7 +192,7 @@ void MismatchedIteratorChecker::checkPreStmt(const BinaryOperator *BO,
verifyMatch(C, LVal, RVal);
}
-void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
+void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, SVal Iter,
const MemRegion *Cont) const {
// Verify match between a container and the container of an iterator
Cont = Cont->getMostDerivedObjectRegion();
@@ -236,9 +228,8 @@ void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
}
}
-void MismatchedIteratorChecker::verifyMatch(CheckerContext &C,
- const SVal &Iter1,
- const SVal &Iter2) const {
+void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, SVal Iter1,
+ SVal Iter2) const {
// Verify match between the containers of two iterators
auto State = C.getState();
const auto *Pos1 = getIteratorPosition(State, Iter1);
@@ -275,23 +266,21 @@ void MismatchedIteratorChecker::verifyMatch(CheckerContext &C,
}
}
-void MismatchedIteratorChecker::reportBug(const StringRef &Message,
- const SVal &Val1,
- const SVal &Val2,
- CheckerContext &C,
+void MismatchedIteratorChecker::reportBug(StringRef Message, SVal Val1,
+ SVal Val2, CheckerContext &C,
ExplodedNode *ErrNode) const {
- auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
+ auto R = std::make_unique<PathSensitiveBugReport>(MismatchedBugType, Message,
ErrNode);
R->markInteresting(Val1);
R->markInteresting(Val2);
C.emitReport(std::move(R));
}
-void MismatchedIteratorChecker::reportBug(const StringRef &Message,
- const SVal &Val, const MemRegion *Reg,
+void MismatchedIteratorChecker::reportBug(StringRef Message, SVal Val,
+ const MemRegion *Reg,
CheckerContext &C,
ExplodedNode *ErrNode) const {
- auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
+ auto R = std::make_unique<PathSensitiveBugReport>(MismatchedBugType, Message,
ErrNode);
R->markInteresting(Val);
R->markInteresting(Reg);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
index 5d63d6efd234..2e31c16e457c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
@@ -15,14 +15,15 @@
#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/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
using namespace ento;
-using llvm::APSInt;
namespace {
class MmapWriteExecChecker : public Checker<check::PreCall> {
@@ -31,9 +32,11 @@ class MmapWriteExecChecker : public Checker<check::PreCall> {
static int ProtWrite;
static int ProtExec;
static int ProtRead;
- mutable std::unique_ptr<BugType> BT;
+ const BugType BT{this, "W^X check fails, Write Exec prot flags set",
+ "Security"};
+
public:
- MmapWriteExecChecker() : MmapFn("mmap", 6), MprotectFn("mprotect", 3) {}
+ MmapWriteExecChecker() : MmapFn({"mmap"}, 6), MprotectFn({"mprotect"}, 3) {}
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
int ProtExecOv;
int ProtReadOv;
@@ -46,9 +49,11 @@ int MmapWriteExecChecker::ProtRead = 0x01;
void MmapWriteExecChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
- if (Call.isCalled(MmapFn) || Call.isCalled(MprotectFn)) {
+ if (matchesAny(Call, MmapFn, MprotectFn)) {
SVal ProtVal = Call.getArgSVal(2);
- Optional<nonloc::ConcreteInt> ProtLoc = ProtVal.getAs<nonloc::ConcreteInt>();
+ auto ProtLoc = ProtVal.getAs<nonloc::ConcreteInt>();
+ if (!ProtLoc)
+ return;
int64_t Prot = ProtLoc->getValue().getSExtValue();
if (ProtExecOv != ProtExec)
ProtExec = ProtExecOv;
@@ -60,17 +65,16 @@ void MmapWriteExecChecker::checkPreCall(const CallEvent &Call,
return;
if ((Prot & (ProtWrite | ProtExec)) == (ProtWrite | ProtExec)) {
- if (!BT)
- BT.reset(new BugType(this, "W^X check fails, Write Exec prot flags set", "Security"));
-
ExplodedNode *N = C.generateNonFatalErrorNode();
if (!N)
return;
auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT, "Both PROT_WRITE and PROT_EXEC flags are set. This can "
- "lead to exploitable memory regions, which could be overwritten "
- "with malicious code", N);
+ BT,
+ "Both PROT_WRITE and PROT_EXEC flags are set. This can "
+ "lead to exploitable memory regions, which could be overwritten "
+ "with malicious code",
+ N);
Report->addRange(Call.getArgSourceRange(2));
C.emitReport(std::move(Report));
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
index cbe938982000..5240352a9bd2 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
@@ -49,7 +49,6 @@ class MoveChecker
: public Checker<check::PreCall, check::PostCall,
check::DeadSymbols, check::RegionChanges> {
public:
- void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
void checkPreCall(const CallEvent &MC, CheckerContext &C) const;
void checkPostCall(const CallEvent &MC, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
@@ -185,7 +184,7 @@ private:
bool Found;
};
- AggressivenessKind Aggressiveness;
+ AggressivenessKind Aggressiveness = AK_KnownsAndLocals;
public:
void setAggressiveness(StringRef Str, CheckerManager &Mgr) {
@@ -214,8 +213,9 @@ private:
// Returns the exploded node against which the report was emitted.
// The caller *must* add any further transitions against this node.
- ExplodedNode *reportBug(const MemRegion *Region, const CXXRecordDecl *RD,
- CheckerContext &C, MisuseKind MK) const;
+ // Returns nullptr and does not report if such node already exists.
+ ExplodedNode *tryToReportBug(const MemRegion *Region, const CXXRecordDecl *RD,
+ CheckerContext &C, MisuseKind MK) const;
bool isInMoveSafeContext(const LocationContext *LC) const;
bool isStateResetMethod(const CXXMethodDecl *MethodDec) const;
@@ -310,7 +310,7 @@ MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N,
// If it's not a dereference, we don't care if it was reset to null
// or that it is even a smart pointer.
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case SK_NonStd:
case SK_Safe:
OS << "Object";
@@ -378,19 +378,20 @@ void MoveChecker::modelUse(ProgramStateRef State, const MemRegion *Region,
return;
}
- ExplodedNode *N = reportBug(Region, RD, C, MK);
+ ExplodedNode *N = tryToReportBug(Region, RD, C, MK);
// If the program has already crashed on this path, don't bother.
- if (N->isSink())
+ if (!N || N->isSink())
return;
State = State->set<TrackedRegionMap>(Region, RegionState::getReported());
C.addTransition(State, N);
}
-ExplodedNode *MoveChecker::reportBug(const MemRegion *Region,
- const CXXRecordDecl *RD, CheckerContext &C,
- MisuseKind MK) const {
+ExplodedNode *MoveChecker::tryToReportBug(const MemRegion *Region,
+ const CXXRecordDecl *RD,
+ CheckerContext &C,
+ MisuseKind MK) const {
if (ExplodedNode *N = misuseCausesCrash(MK) ? C.generateErrorNode()
: C.generateNonFatalErrorNode()) {
// Uniqueing report to the same object.
@@ -554,7 +555,8 @@ MoveChecker::classifyObject(const MemRegion *MR,
// as not-"STL" types, because that's how the checker treats them.
MR = unwrapRValueReferenceIndirection(MR);
bool IsLocal =
- MR && isa<VarRegion>(MR) && isa<StackSpaceRegion>(MR->getMemorySpace());
+ isa_and_nonnull<VarRegion, CXXLifetimeExtendedObjectRegion>(MR) &&
+ isa<StackSpaceRegion>(MR->getMemorySpace());
if (!RD || !RD->getDeclContext()->isStdNamespace())
return { IsLocal, SK_NonStd };
@@ -588,7 +590,7 @@ void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR,
break;
// We only care about the type if it's a dereference.
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case SK_Unsafe:
OS << " of type '" << RD->getQualifiedNameAsString() << "'";
break;
@@ -619,10 +621,6 @@ void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const {
if (!IC)
return;
- // Calling a destructor on a moved object is fine.
- if (isa<CXXDestructorCall>(IC))
- return;
-
const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
if (!ThisRegion)
return;
@@ -632,6 +630,10 @@ void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const {
if (!MethodDecl)
return;
+ // Calling a destructor on a moved object is fine.
+ if (isa<CXXDestructorDecl>(MethodDecl))
+ return;
+
// We want to investigate the whole object, not only sub-object of a parent
// class in which the encountered method defined.
ThisRegion = ThisRegion->getMostDerivedObjectRegion();
@@ -712,12 +714,9 @@ ProgramStateRef MoveChecker::checkRegionChanges(
// directly, but not all of them end up being invalidated.
// But when they do, they appear in the InvalidatedRegions array as well.
for (const auto *Region : RequestedRegions) {
- if (ThisRegion != Region) {
- if (llvm::find(InvalidatedRegions, Region) !=
- std::end(InvalidatedRegions)) {
- State = removeFromState(State, Region);
- }
- }
+ if (ThisRegion != Region &&
+ llvm::is_contained(InvalidatedRegions, Region))
+ State = removeFromState(State, Region);
}
} else {
// For invalidations that aren't caused by calls, assume nothing. In
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
index be17e401fb53..0648084a7d39 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
@@ -31,7 +31,8 @@ using namespace ento;
namespace {
class NSAutoreleasePoolChecker
: public Checker<check::PreObjCMessage> {
- mutable std::unique_ptr<BugType> BT;
+ const BugType BT{this, "Use -drain instead of -release",
+ "API Upgrade (Apple)"};
mutable Selector releaseS;
public:
@@ -57,10 +58,6 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
if (msg.getSelector() != releaseS)
return;
- if (!BT)
- BT.reset(new BugType(this, "Use -drain instead of -release",
- "API Upgrade (Apple)"));
-
ExplodedNode *N = C.generateNonFatalErrorNode();
if (!N) {
assert(0);
@@ -68,7 +65,7 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
}
auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT,
+ BT,
"Use -drain instead of -release when using NSAutoreleasePool and "
"garbage collection",
N);
@@ -80,7 +77,7 @@ void ento::registerNSAutoreleasePoolChecker(CheckerManager &mgr) {
mgr.registerChecker<NSAutoreleasePoolChecker>();
}
-bool ento::shouldRegisterNSAutoreleasePoolChecker(const CheckerManager &mgr) {
+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 90c5583d8969..54870bcb4bb2 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
-// This file defines a CheckNSError, a flow-insenstive check
+// This file defines a CheckNSError, a flow-insensitive check
// that determines if an Objective-C class interface correctly returns
// a non-void return type.
//
@@ -24,6 +24,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -38,10 +39,10 @@ static bool IsCFError(QualType T, IdentifierInfo *II);
namespace {
class NSErrorMethodChecker
: public Checker< check::ASTDecl<ObjCMethodDecl> > {
- mutable IdentifierInfo *II;
+ mutable IdentifierInfo *II = nullptr;
public:
- NSErrorMethodChecker() : II(nullptr) {}
+ NSErrorMethodChecker() = default;
void checkASTDecl(const ObjCMethodDecl *D,
AnalysisManager &mgr, BugReporter &BR) const;
@@ -118,7 +119,7 @@ void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
II = &D->getASTContext().Idents.get("CFErrorRef");
bool hasCFError = false;
- for (auto I : D->parameters()) {
+ for (auto *I : D->parameters()) {
if (IsCFError(I->getType(), II)) {
hasCFError = true;
break;
@@ -166,7 +167,7 @@ class NSOrCFErrorDerefChecker
mutable std::unique_ptr<NSErrorDerefBug> NSBT;
mutable std::unique_ptr<CFErrorDerefBug> CFBT;
public:
- DefaultBool ShouldCheckNSError, ShouldCheckCFError;
+ bool ShouldCheckNSError = false, ShouldCheckCFError = false;
CheckerNameRef NSErrorName, CFErrorName;
NSOrCFErrorDerefChecker() : NSErrorII(nullptr), CFErrorII(nullptr) {}
@@ -197,7 +198,7 @@ static void setFlag(ProgramStateRef state, SVal val, CheckerContext &C) {
static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) {
const StackFrameContext * SFC = C.getStackFrame();
- if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) {
+ if (std::optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) {
const MemRegion* R = X->getRegion();
if (const VarRegion *VR = R->getAs<VarRegion>())
if (const StackArgumentsSpaceRegion *
@@ -214,7 +215,7 @@ void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad,
CheckerContext &C) const {
if (!isLoad)
return;
- if (loc.isUndef() || !loc.getAs<Loc>())
+ if (loc.isUndef() || !isa<Loc>(loc))
return;
ASTContext &Ctx = C.getASTContext();
@@ -266,7 +267,7 @@ void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const {
SmallString<128> Buf;
llvm::raw_svector_ostream os(Buf);
- os << "Potential null dereference. According to coding standards ";
+ os << "Potential null dereference. According to coding standards ";
os << (isNSError
? "in 'Creating and Returning NSError Objects' the parameter"
: "documented in CoreFoundation/CFError.h the parameter");
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
index af208e867318..17c3cb4e9e04 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
@@ -44,9 +44,11 @@ void NoReturnFunctionChecker::checkPostCall(const CallEvent &CE,
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE.getDecl()))
BuildSinks = FD->hasAttr<AnalyzerNoReturnAttr>() || FD->isNoReturn();
- const Expr *Callee = CE.getOriginExpr();
- if (!BuildSinks && Callee)
- BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn();
+ if (const CallExpr *CExpr = dyn_cast_or_null<CallExpr>(CE.getOriginExpr());
+ CExpr && !BuildSinks) {
+ if (const Expr *C = CExpr->getCallee())
+ BuildSinks = getFunctionExtInfo(C->getType()).getNoReturn();
+ }
if (!BuildSinks && CE.isGlobalCFunction()) {
if (const IdentifierInfo *II = CE.getCalleeIdentifier()) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
index 534b5d68434f..a9002ee7c966 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
@@ -18,6 +18,7 @@
#include "clang/Analysis/AnyCall.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"
@@ -31,8 +32,9 @@ namespace {
class NonNullParamChecker
: public Checker<check::PreCall, check::BeginFunction,
EventDispatcher<ImplicitNullDerefEvent>> {
- mutable std::unique_ptr<BugType> BTAttrNonNull;
- mutable std::unique_ptr<BugType> BTNullRefArg;
+ const BugType BTAttrNonNull{
+ this, "Argument with 'nonnull' attribute passed null", "API"};
+ const BugType BTNullRefArg{this, "Dereference of null pointer"};
public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
@@ -136,10 +138,10 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
if (!DV)
continue;
- assert(!HasRefTypeParam || DV->getAs<Loc>());
+ assert(!HasRefTypeParam || isa<Loc>(*DV));
// Process the case when the argument is not a location.
- if (ExpectedToBeNonNull && !DV->getAs<Loc>()) {
+ if (ExpectedToBeNonNull && !isa<Loc>(*DV)) {
// If the argument is a union type, we want to handle a potential
// transparent_union GCC extension.
if (!ArgE)
@@ -161,7 +163,7 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
assert(++CSV->begin() == CSV->end());
// FIXME: Handle (some_union){ some_other_union_val }, which turns into
// a LazyCompoundVal inside a CompoundVal.
- if (!V.getAs<Loc>())
+ if (!isa<Loc>(V))
continue;
// Retrieve the corresponding expression.
@@ -278,13 +280,6 @@ std::unique_ptr<PathSensitiveBugReport>
NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,
const Expr *ArgE,
unsigned IdxOfArg) const {
- // Lazily allocate the BugType object if it hasn't already been
- // created. Ownership is transferred to the BugReporter object once
- // the BugReport is passed to 'EmitWarning'.
- if (!BTAttrNonNull)
- BTAttrNonNull.reset(new BugType(
- this, "Argument with 'nonnull' attribute passed null", "API"));
-
llvm::SmallString<256> SBuf;
llvm::raw_svector_ostream OS(SBuf);
OS << "Null pointer passed to "
@@ -292,7 +287,7 @@ NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,
<< " parameter expecting 'nonnull'";
auto R =
- std::make_unique<PathSensitiveBugReport>(*BTAttrNonNull, SBuf, ErrorNode);
+ std::make_unique<PathSensitiveBugReport>(BTAttrNonNull, SBuf, ErrorNode);
if (ArgE)
bugreporter::trackExpressionValue(ErrorNode, ArgE, *R);
@@ -302,11 +297,8 @@ NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,
std::unique_ptr<PathSensitiveBugReport>
NonNullParamChecker::genReportReferenceToNullPointer(
const ExplodedNode *ErrorNode, const Expr *ArgE) const {
- if (!BTNullRefArg)
- BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer"));
-
auto R = std::make_unique<PathSensitiveBugReport>(
- *BTNullRefArg, "Forming reference to null pointer", ErrorNode);
+ BTNullRefArg, "Forming reference to null pointer", ErrorNode);
if (ArgE) {
const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);
if (!ArgEDeref)
@@ -314,7 +306,6 @@ NonNullParamChecker::genReportReferenceToNullPointer(
bugreporter::trackExpressionValue(ErrorNode, ArgEDeref, *R);
}
return R;
-
}
void ento::registerNonNullParamChecker(CheckerManager &mgr) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
index c5437b16c688..72c6a869d225 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
@@ -26,6 +26,7 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -77,7 +78,8 @@ void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad,
if (isGlobalConstString(location)) {
SVal V = State->getSVal(location.castAs<Loc>());
- Optional<DefinedOrUnknownSVal> Constr = V.getAs<DefinedOrUnknownSVal>();
+ std::optional<DefinedOrUnknownSVal> Constr =
+ V.getAs<DefinedOrUnknownSVal>();
if (Constr) {
@@ -91,7 +93,7 @@ void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad,
/// \param V loaded lvalue.
/// \return whether @c val is a string-like const global.
bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const {
- Optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>();
+ std::optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>();
if (!RegionVal)
return false;
auto *Region = dyn_cast<VarRegion>(RegionVal->getAsRegion());
@@ -109,17 +111,20 @@ bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const {
// Look through the typedefs.
while (const Type *T = Ty.getTypePtr()) {
- if (const auto *TT = dyn_cast<TypedefType>(T)) {
+ if (const auto *AT = dyn_cast<AttributedType>(T)) {
+ if (AT->getAttrKind() == attr::TypeNonNull)
+ return true;
+ Ty = AT->getModifiedType();
+ } else if (const auto *ET = dyn_cast<ElaboratedType>(T)) {
+ const auto *TT = dyn_cast<TypedefType>(ET->getNamedType());
+ if (!TT)
+ return false;
Ty = TT->getDecl()->getUnderlyingType();
// It is sufficient for any intermediate typedef
// to be classified const.
HasConst = HasConst || Ty.isConstQualified();
if (isNonnullType(Ty) && HasConst)
return true;
- } else if (const auto *AT = dyn_cast<AttributedType>(T)) {
- if (AT->getAttrKind() == attr::TypeNonNull)
- return true;
- Ty = AT->getModifiedType();
} else {
return false;
}
@@ -136,7 +141,7 @@ bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const {
if (auto *T = dyn_cast<ObjCObjectPointerType>(Ty)) {
return T->getInterfaceDecl() &&
T->getInterfaceDecl()->getIdentifier() == NSStringII;
- } else if (auto *T = dyn_cast<TypedefType>(Ty)) {
+ } else if (auto *T = Ty->getAs<TypedefType>()) {
IdentifierInfo* II = T->getDecl()->getIdentifier();
return II == CFStringRefII || II == CFBooleanRefII || II == CFNullRefII;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
index fe8f7e7bf69e..06f1ad00eaf2 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
@@ -26,13 +26,15 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/Analysis/AnyCall.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Path.h"
@@ -80,8 +82,9 @@ enum class ErrorKind : int {
class NullabilityChecker
: public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>,
check::PostCall, check::PostStmt<ExplicitCastExpr>,
- check::PostObjCMessage, check::DeadSymbols,
- check::Location, check::Event<ImplicitNullDerefEvent>> {
+ check::PostObjCMessage, check::DeadSymbols, eval::Assume,
+ check::Location, check::Event<ImplicitNullDerefEvent>,
+ check::BeginFunction> {
public:
// If true, the checker will not diagnose nullabilility issues for calls
@@ -90,7 +93,7 @@ public:
// find warnings about nullability annotations that they have explicitly
// added themselves higher priority to fix than warnings on calls to system
// libraries.
- DefaultBool NoDiagnoseCallsToSystemHeaders;
+ bool NoDiagnoseCallsToSystemHeaders = false;
void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
void checkPostStmt(const ExplicitCastExpr *CE, CheckerContext &C) const;
@@ -102,6 +105,9 @@ public:
void checkEvent(ImplicitNullDerefEvent Event) const;
void checkLocation(SVal Location, bool IsLoad, const Stmt *S,
CheckerContext &C) const;
+ void checkBeginFunction(CheckerContext &Ctx) const;
+ ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
+ bool Assumption) const;
void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
const char *Sep) const override;
@@ -115,7 +121,7 @@ public:
CK_NumCheckKinds
};
- DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ bool ChecksEnabled[CK_NumCheckKinds] = {false};
CheckerNameRef CheckNames[CK_NumCheckKinds];
mutable std::unique_ptr<BugType> BTs[CK_NumCheckKinds];
@@ -129,8 +135,8 @@ public:
// When set to false no nullability information will be tracked in
// NullabilityMap. It is possible to catch errors like passing a null pointer
// to a callee that expects nonnull argument without the information that is
- // stroed in the NullabilityMap. This is an optimization.
- DefaultBool NeedTracking;
+ // stored in the NullabilityMap. This is an optimization.
+ bool NeedTracking = false;
private:
class NullabilityBugVisitor : public BugReporterVisitor {
@@ -230,10 +236,41 @@ bool operator==(NullabilityState Lhs, NullabilityState Rhs) {
Lhs.getNullabilitySource() == Rhs.getNullabilitySource();
}
+// For the purpose of tracking historical property accesses, the key for lookup
+// is an object pointer (could be an instance or a class) paired with the unique
+// identifier for the property being invoked on that object.
+using ObjectPropPair = std::pair<const MemRegion *, const IdentifierInfo *>;
+
+// Metadata associated with the return value from a recorded property access.
+struct ConstrainedPropertyVal {
+ // This will reference the conjured return SVal for some call
+ // of the form [object property]
+ DefinedOrUnknownSVal Value;
+
+ // If the SVal has been determined to be nonnull, that is recorded here
+ bool isConstrainedNonnull;
+
+ ConstrainedPropertyVal(DefinedOrUnknownSVal SV)
+ : Value(SV), isConstrainedNonnull(false) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ Value.Profile(ID);
+ ID.AddInteger(isConstrainedNonnull ? 1 : 0);
+ }
+};
+
+bool operator==(const ConstrainedPropertyVal &Lhs,
+ const ConstrainedPropertyVal &Rhs) {
+ return Lhs.Value == Rhs.Value &&
+ Lhs.isConstrainedNonnull == Rhs.isConstrainedNonnull;
+}
+
} // end anonymous namespace
REGISTER_MAP_WITH_PROGRAMSTATE(NullabilityMap, const MemRegion *,
NullabilityState)
+REGISTER_MAP_WITH_PROGRAMSTATE(PropertyAccessesMap, ObjectPropPair,
+ ConstrainedPropertyVal)
// We say "the nullability type invariant is violated" when a location with a
// non-null type contains NULL or a function with a non-null return type returns
@@ -273,6 +310,10 @@ static NullConstraint getNullConstraint(DefinedOrUnknownSVal Val,
return NullConstraint::Unknown;
}
+static bool isValidPointerType(QualType T) {
+ return T->isAnyPointerType() || T->isBlockPointerType();
+}
+
const SymbolicRegion *
NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const {
if (!NeedTracking)
@@ -285,8 +326,11 @@ NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const {
const MemRegion *Region = RegionSVal->getRegion();
if (CheckSuperRegion) {
- if (auto FieldReg = Region->getAs<FieldRegion>())
+ if (const SubRegion *FieldReg = Region->getAs<FieldRegion>()) {
+ if (const auto *ER = dyn_cast<ElementRegion>(FieldReg->getSuperRegion()))
+ FieldReg = ER;
return dyn_cast<SymbolicRegion>(FieldReg->getSuperRegion());
+ }
if (auto ElementReg = Region->getAs<ElementRegion>())
return dyn_cast<SymbolicRegion>(ElementReg->getSuperRegion());
}
@@ -455,15 +499,24 @@ void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
NullabilityMapTy Nullabilities = State->get<NullabilityMap>();
- for (NullabilityMapTy::iterator I = Nullabilities.begin(),
- E = Nullabilities.end();
- I != E; ++I) {
- const auto *Region = I->first->getAs<SymbolicRegion>();
+ for (const MemRegion *Reg : llvm::make_first_range(Nullabilities)) {
+ const auto *Region = Reg->getAs<SymbolicRegion>();
assert(Region && "Non-symbolic region is tracked.");
if (SR.isDead(Region->getSymbol())) {
- State = State->remove<NullabilityMap>(I->first);
+ State = State->remove<NullabilityMap>(Reg);
}
}
+
+ // When an object goes out of scope, we can free the history associated
+ // with any property accesses on that object
+ PropertyAccessesMapTy PropertyAccesses = State->get<PropertyAccessesMap>();
+ for (ObjectPropPair PropKey : llvm::make_first_range(PropertyAccesses)) {
+ const MemRegion *ReceiverRegion = PropKey.first;
+ if (!SR.isLiveRegion(ReceiverRegion)) {
+ State = State->remove<PropertyAccessesMap>(PropKey);
+ }
+ }
+
// When one of the nonnull arguments are constrained to be null, nullability
// preconditions are violated. It is not enough to check this only when we
// actually report an error, because at that time interesting symbols might be
@@ -510,6 +563,37 @@ void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const {
}
}
+void NullabilityChecker::checkBeginFunction(CheckerContext &C) const {
+ if (!C.inTopFrame())
+ return;
+
+ const LocationContext *LCtx = C.getLocationContext();
+ auto AbstractCall = AnyCall::forDecl(LCtx->getDecl());
+ if (!AbstractCall || AbstractCall->parameters().empty())
+ return;
+
+ ProgramStateRef State = C.getState();
+ for (const ParmVarDecl *Param : AbstractCall->parameters()) {
+ if (!isValidPointerType(Param->getType()))
+ continue;
+
+ Nullability RequiredNullability =
+ getNullabilityAnnotation(Param->getType());
+ if (RequiredNullability != Nullability::Nullable)
+ continue;
+
+ const VarRegion *ParamRegion = State->getRegion(Param, LCtx);
+ const MemRegion *ParamPointeeRegion =
+ State->getSVal(ParamRegion).getAsRegion();
+ if (!ParamPointeeRegion)
+ continue;
+
+ State = State->set<NullabilityMap>(ParamPointeeRegion,
+ NullabilityState(RequiredNullability));
+ }
+ C.addTransition(State);
+}
+
// 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.
@@ -572,7 +656,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
if (!RetExpr)
return;
- if (!RetExpr->getType()->isAnyPointerType())
+ if (!isValidPointerType(RetExpr->getType()))
return;
ProgramStateRef State = C.getState();
@@ -705,7 +789,7 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
if (!ArgSVal)
continue;
- if (!Param->getType()->isAnyPointerType() &&
+ if (!isValidPointerType(Param->getType()) &&
!Param->getType()->isReferenceType())
continue;
@@ -714,7 +798,7 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
Nullability RequiredNullability =
getNullabilityAnnotation(Param->getType());
Nullability ArgExprTypeLevelNullability =
- getNullabilityAnnotation(ArgExpr->getType());
+ getNullabilityAnnotation(lookThroughImplicitCasts(ArgExpr)->getType());
unsigned ParamIdx = Param->getFunctionScopeIndex() + 1;
@@ -792,7 +876,7 @@ void NullabilityChecker::checkPostCall(const CallEvent &Call,
if (!FuncType)
return;
QualType ReturnType = FuncType->getReturnType();
- if (!ReturnType->isAnyPointerType())
+ if (!isValidPointerType(ReturnType))
return;
ProgramStateRef State = C.getState();
if (State->get<InvariantViolated>())
@@ -806,7 +890,7 @@ void NullabilityChecker::checkPostCall(const CallEvent &Call,
// of CG calls.
const SourceManager &SM = C.getSourceManager();
StringRef FilePath = SM.getFilename(SM.getSpellingLoc(Decl->getBeginLoc()));
- if (llvm::sys::path::filename(FilePath).startswith("CG")) {
+ if (llvm::sys::path::filename(FilePath).starts_with("CG")) {
State = State->set<NullabilityMap>(Region, Nullability::Contradicted);
C.addTransition(State);
return;
@@ -815,6 +899,14 @@ void NullabilityChecker::checkPostCall(const CallEvent &Call,
const NullabilityState *TrackedNullability =
State->get<NullabilityMap>(Region);
+ // ObjCMessageExpr gets the actual type through
+ // Sema::getMessageSendResultType, instead of using the return type of
+ // MethodDecl directly. The final type is generated by considering the
+ // nullability of receiver and MethodDecl together. Thus, The type of
+ // ObjCMessageExpr is prefer.
+ if (const Expr *E = Call.getOriginExpr())
+ ReturnType = E->getType();
+
if (!TrackedNullability &&
getNullabilityAnnotation(ReturnType) == Nullability::Nullable) {
State = State->set<NullabilityMap>(Region, Nullability::Nullable);
@@ -851,6 +943,30 @@ static Nullability getReceiverNullability(const ObjCMethodCall &M,
return Nullability::Unspecified;
}
+// The return value of a property access is typically a temporary value which
+// will not be tracked in a persistent manner by the analyzer. We use
+// evalAssume() in order to immediately record constraints on those temporaries
+// at the time they are imposed (e.g. by a nil-check conditional).
+ProgramStateRef NullabilityChecker::evalAssume(ProgramStateRef State, SVal Cond,
+ bool Assumption) const {
+ PropertyAccessesMapTy PropertyAccesses = State->get<PropertyAccessesMap>();
+ for (auto [PropKey, PropVal] : PropertyAccesses) {
+ if (!PropVal.isConstrainedNonnull) {
+ ConditionTruthVal IsNonNull = State->isNonNull(PropVal.Value);
+ if (IsNonNull.isConstrainedTrue()) {
+ ConstrainedPropertyVal Replacement = PropVal;
+ Replacement.isConstrainedNonnull = true;
+ State = State->set<PropertyAccessesMap>(PropKey, Replacement);
+ } else if (IsNonNull.isConstrainedFalse()) {
+ // Space optimization: no point in tracking constrained-null cases
+ State = State->remove<PropertyAccessesMap>(PropKey);
+ }
+ }
+ }
+
+ return State;
+}
+
/// Calculate the nullability of the result of a message expr based on the
/// nullability of the receiver, the nullability of the return value, and the
/// constraints.
@@ -860,7 +976,7 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M,
if (!Decl)
return;
QualType RetType = Decl->getReturnType();
- if (!RetType->isAnyPointerType())
+ if (!isValidPointerType(RetType))
return;
ProgramStateRef State = C.getState();
@@ -876,7 +992,7 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M,
// In order to reduce the noise in the diagnostics generated by this checker,
// some framework and programming style based heuristics are used. These
// heuristics are for Cocoa APIs which have NS prefix.
- if (Name.startswith("NS")) {
+ if (Name.starts_with("NS")) {
// Developers rely on dynamic invariants such as an item should be available
// in a collection, or a collection is not empty often. Those invariants can
// not be inferred by any static analysis tool. To not to bother the users
@@ -907,7 +1023,7 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M,
// this class of methods reduced the emitted diagnostics by about 30% on
// some projects (and all of that was false positives).
if (Name.contains("String")) {
- for (auto Param : M.parameters()) {
+ for (auto *Param : M.parameters()) {
if (Param->getName() == "encoding") {
State = State->set<NullabilityMap>(ReturnRegion,
Nullability::Contradicted);
@@ -945,14 +1061,55 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M,
}
// No tracked information. Use static type information for return value.
- Nullability RetNullability = getNullabilityAnnotation(RetType);
+ Nullability RetNullability = getNullabilityAnnotation(Message->getType());
- // Properties might be computed. For this reason the static analyzer creates a
- // new symbol each time an unknown property is read. To avoid false pozitives
- // do not treat unknown properties as nullable, even when they explicitly
- // marked nullable.
- if (M.getMessageKind() == OCM_PropertyAccess && !C.wasInlined)
- RetNullability = Nullability::Nonnull;
+ // Properties might be computed, which means the property value could
+ // theoretically change between calls even in commonly-observed cases like
+ // this:
+ //
+ // if (foo.prop) { // ok, it's nonnull here...
+ // [bar doStuffWithNonnullVal:foo.prop]; // ...but what about
+ // here?
+ // }
+ //
+ // If the property is nullable-annotated, a naive analysis would lead to many
+ // false positives despite the presence of probably-correct nil-checks. To
+ // reduce the false positive rate, we maintain a history of the most recently
+ // observed property value. For each property access, if the prior value has
+ // been constrained to be not nil then we will conservatively assume that the
+ // next access can be inferred as nonnull.
+ if (RetNullability != Nullability::Nonnull &&
+ M.getMessageKind() == OCM_PropertyAccess && !C.wasInlined) {
+ bool LookupResolved = false;
+ if (const MemRegion *ReceiverRegion = getTrackRegion(M.getReceiverSVal())) {
+ if (IdentifierInfo *Ident = M.getSelector().getIdentifierInfoForSlot(0)) {
+ LookupResolved = true;
+ ObjectPropPair Key = std::make_pair(ReceiverRegion, Ident);
+ const ConstrainedPropertyVal *PrevPropVal =
+ State->get<PropertyAccessesMap>(Key);
+ if (PrevPropVal && PrevPropVal->isConstrainedNonnull) {
+ RetNullability = Nullability::Nonnull;
+ } else {
+ // If a previous property access was constrained as nonnull, we hold
+ // on to that constraint (effectively inferring that all subsequent
+ // accesses on that code path can be inferred as nonnull). If the
+ // previous property access was *not* constrained as nonnull, then
+ // let's throw it away in favor of keeping the SVal associated with
+ // this more recent access.
+ if (auto ReturnSVal =
+ M.getReturnValue().getAs<DefinedOrUnknownSVal>()) {
+ State = State->set<PropertyAccessesMap>(
+ Key, ConstrainedPropertyVal(*ReturnSVal));
+ }
+ }
+ }
+ }
+
+ if (!LookupResolved) {
+ // Fallback: err on the side of suppressing the false positive.
+ RetNullability = Nullability::Nonnull;
+ }
+ }
Nullability ComputedNullab = getMostNullable(RetNullability, SelfNullability);
if (ComputedNullab == Nullability::Nullable) {
@@ -973,9 +1130,9 @@ void NullabilityChecker::checkPostStmt(const ExplicitCastExpr *CE,
CheckerContext &C) const {
QualType OriginType = CE->getSubExpr()->getType();
QualType DestType = CE->getType();
- if (!OriginType->isAnyPointerType())
+ if (!isValidPointerType(OriginType))
return;
- if (!DestType->isAnyPointerType())
+ if (!isValidPointerType(DestType))
return;
ProgramStateRef State = C.getState();
@@ -1099,7 +1256,7 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
return;
QualType LocType = TVR->getValueType();
- if (!LocType->isAnyPointerType())
+ if (!isValidPointerType(LocType))
return;
ProgramStateRef State = C.getState();
@@ -1221,9 +1378,9 @@ void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State,
if (!State->get<InvariantViolated>())
Out << Sep << NL;
- for (NullabilityMapTy::iterator I = B.begin(), E = B.end(); I != E; ++I) {
- Out << I->first << " : ";
- I->second.print(Out);
+ for (auto [Region, State] : B) {
+ Out << Region << " : ";
+ State.print(Out);
Out << NL;
}
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
index df01cc760e7e..f217520d8f4a 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
@@ -143,7 +143,7 @@ void Callback::run(const MatchFinder::MatchResult &Result) {
else
OS << "Converting ";
- OS << "a pointer value of type '" << ObjT.getAsString() << "' to a ";
+ OS << "a pointer value of type '" << ObjT << "' to a ";
std::string EuphemismForPlain = "primitive";
std::string SuggestedApi = IsObjC ? (IsInteger ? "" : "-boolValue")
@@ -196,12 +196,10 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D,
AnalysisManager &AM,
BugReporter &BR) const {
// Currently this matches CoreFoundation opaque pointer typedefs.
- auto CSuspiciousNumberObjectExprM =
- expr(ignoringParenImpCasts(
- expr(hasType(
- typedefType(hasDeclaration(anyOf(
- typedefDecl(hasName("CFNumberRef")),
- typedefDecl(hasName("CFBooleanRef")))))))
+ auto CSuspiciousNumberObjectExprM = expr(ignoringParenImpCasts(
+ expr(hasType(elaboratedType(namesType(typedefType(
+ hasDeclaration(anyOf(typedefDecl(hasName("CFNumberRef")),
+ typedefDecl(hasName("CFBooleanRef")))))))))
.bind("c_object")));
// Currently this matches XNU kernel number-object pointers.
@@ -240,8 +238,9 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D,
// The .bind here is in order to compose the error message more accurately.
auto ObjCSuspiciousScalarBooleanTypeM =
- qualType(typedefType(hasDeclaration(
- typedefDecl(hasName("BOOL"))))).bind("objc_bool_type");
+ qualType(elaboratedType(namesType(
+ typedefType(hasDeclaration(typedefDecl(hasName("BOOL")))))))
+ .bind("objc_bool_type");
// The .bind here is in order to compose the error message more accurately.
auto SuspiciousScalarBooleanTypeM =
@@ -253,9 +252,9 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D,
// for storing pointers.
auto SuspiciousScalarNumberTypeM =
qualType(hasCanonicalType(isInteger()),
- unless(typedefType(hasDeclaration(
- typedefDecl(matchesName("^::u?intptr_t$"))))))
- .bind("int_type");
+ unless(elaboratedType(namesType(typedefType(hasDeclaration(
+ typedefDecl(matchesName("^::u?intptr_t$"))))))))
+ .bind("int_type");
auto SuspiciousScalarTypeM =
qualType(anyOf(SuspiciousScalarBooleanTypeM,
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
index 43af4bb14286..552c222a251a 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
@@ -25,8 +25,10 @@ using namespace ento;
namespace {
class ObjCAtSyncChecker
: public Checker< check::PreStmt<ObjCAtSynchronizedStmt> > {
- mutable std::unique_ptr<BuiltinBug> BT_null;
- mutable std::unique_ptr<BuiltinBug> BT_undef;
+ const BugType BT_null{this, "Nil value used as mutex for @synchronized() "
+ "(no synchronization will occur)"};
+ const BugType BT_undef{this, "Uninitialized value used as mutex "
+ "for @synchronized"};
public:
void checkPreStmt(const ObjCAtSynchronizedStmt *S, CheckerContext &C) const;
@@ -41,13 +43,10 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S,
SVal V = C.getSVal(Ex);
// Uninitialized value used for the mutex?
- if (V.getAs<UndefinedVal>()) {
+ if (isa<UndefinedVal>(V)) {
if (ExplodedNode *N = C.generateErrorNode()) {
- if (!BT_undef)
- BT_undef.reset(new BuiltinBug(this, "Uninitialized value used as mutex "
- "for @synchronized"));
auto report = std::make_unique<PathSensitiveBugReport>(
- *BT_undef, BT_undef->getDescription(), N);
+ BT_undef, BT_undef.getDescription(), N);
bugreporter::trackExpressionValue(N, Ex, *report);
C.emitReport(std::move(report));
}
@@ -66,12 +65,8 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S,
// Generate an error node. This isn't a sink since
// a null mutex just means no synchronization occurs.
if (ExplodedNode *N = C.generateNonFatalErrorNode(nullState)) {
- if (!BT_null)
- BT_null.reset(new BuiltinBug(
- this, "Nil value used as mutex for @synchronized() "
- "(no synchronization will occur)"));
auto report = std::make_unique<PathSensitiveBugReport>(
- *BT_null, BT_null->getDescription(), N);
+ BT_null, BT_null.getDescription(), N);
bugreporter::trackExpressionValue(N, Ex, *report);
C.emitReport(std::move(report));
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp
index c8eab3288094..514f53b4804f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp
@@ -98,11 +98,13 @@ private:
};
}
-static inline std::vector<llvm::StringRef> toRefs(std::vector<std::string> V) {
+static inline std::vector<llvm::StringRef>
+toRefs(const std::vector<std::string> &V) {
return std::vector<llvm::StringRef>(V.begin(), V.end());
}
-static decltype(auto) callsNames(std::vector<std::string> FunctionNames) {
+static decltype(auto)
+callsNames(const std::vector<std::string> &FunctionNames) {
return callee(functionDecl(hasAnyName(toRefs(FunctionNames))));
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
index 8428b2294ba6..2b008d1c775a 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
@@ -72,7 +72,7 @@ class WalkAST : public StmtVisitor<WalkAST> {
public:
WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)
: BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()),
- PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {}
+ PtrWidth(ASTC.getTargetInfo().getPointerWidth(LangAS::Default)) {}
// Statement visitor methods.
void VisitChildren(Stmt *S);
@@ -135,9 +135,9 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {
llvm::raw_svector_ostream Os(Buf);
// Use "second" and "third" since users will expect 1-based indexing
// for parameter names when mentioned in prose.
- Os << " The "<< ((ArgNum == 1) ? "second" : "third") << " argument to '"
- << Name << "' must be a C array of pointer-sized values, not '"
- << Arg->getType().getAsString() << "'";
+ Os << " The " << ((ArgNum == 1) ? "second" : "third") << " argument to '"
+ << Name << "' must be a C array of pointer-sized values, not '"
+ << Arg->getType() << "'";
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
index 13985af76b00..28e88245ca95 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
@@ -30,12 +30,7 @@ namespace {
class ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>,
check::PostStmt<CallExpr>,
check::PointerEscape> {
- mutable std::unique_ptr<BugType> BT;
- inline void initBugType() const {
- if (!BT)
- BT.reset(new BugType(this, "CFArray API",
- categories::CoreFoundationObjectiveC));
- }
+ const BugType BT{this, "CFArray API", categories::CoreFoundationObjectiveC};
inline SymbolRef getArraySym(const Expr *E, CheckerContext &C) const {
SVal ArrayRef = C.getSVal(E);
@@ -47,9 +42,6 @@ class ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>,
CheckerContext &C) const;
public:
- /// A tag to id this checker.
- static void *getTag() { static int Tag; return &Tag; }
-
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
ProgramStateRef checkPointerEscape(ProgramStateRef State,
@@ -137,15 +129,15 @@ void ObjCContainersChecker::checkPreStmt(const CallExpr *CE,
// Now, check if 'Idx in [0, Size-1]'.
const QualType T = IdxExpr->getType();
- ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T);
- ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T);
+ ProgramStateRef StInBound, StOutBound;
+ std::tie(StInBound, StOutBound) = State->assumeInBoundDual(Idx, *Size, T);
if (StOutBound && !StInBound) {
ExplodedNode *N = C.generateErrorNode(StOutBound);
if (!N)
return;
- initBugType();
+
auto R = std::make_unique<PathSensitiveBugReport>(
- *BT, "Index is out of bounds", N);
+ BT, "Index is out of bounds", N);
R->addRange(IdxExpr->getSourceRange());
bugreporter::trackExpressionValue(N, IdxExpr, *R,
{bugreporter::TrackingKind::Thorough,
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
index 35a600f2d7b8..598b368e74d4 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
@@ -64,7 +64,7 @@ private:
class ObjCSuperCallChecker : public Checker<
check::ASTDecl<ObjCImplementationDecl> > {
public:
- ObjCSuperCallChecker() : IsInitialized(false) {}
+ ObjCSuperCallChecker() = default;
void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
BugReporter &BR) const;
@@ -75,7 +75,7 @@ private:
void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel,
StringRef ClassName) const;
mutable llvm::StringMap<llvm::SmallPtrSet<Selector, 16>> SelectorsForClass;
- mutable bool IsInitialized;
+ mutable bool IsInitialized = false;
};
}
@@ -103,9 +103,7 @@ void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx,
llvm::SmallPtrSet<Selector, 16> &ClassSelectors =
SelectorsForClass[ClassName];
// Fill the Selectors SmallSet with all selectors we want to check.
- for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end();
- I != E; ++I) {
- SelectorDescriptor Descriptor = *I;
+ for (SelectorDescriptor Descriptor : Sel) {
assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet.
// Get the selector.
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp
index 4636fd160511..08ad6877cbe6 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp
@@ -50,7 +50,7 @@ void ObjCPropertyChecker::checkCopyMutable(const ObjCPropertyDecl *D,
const std::string &PropTypeName(T->getPointeeType().getCanonicalType()
.getUnqualifiedType()
.getAsString());
- if (!StringRef(PropTypeName).startswith("NSMutable"))
+ if (!StringRef(PropTypeName).starts_with("NSMutable"))
return;
const ObjCImplDecl *ImplD = nullptr;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
index 17d3c042ac40..217c46451f80 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
@@ -61,13 +61,13 @@ class ObjCSelfInitChecker : public Checker< check::PostObjCMessage,
check::PostCall,
check::Location,
check::Bind > {
- mutable std::unique_ptr<BugType> BT;
+ const BugType BT{this, "Missing \"self = [(super or self) init...]\"",
+ categories::CoreFoundationObjectiveC};
void checkForInvalidSelf(const Expr *E, CheckerContext &C,
const char *errorStr) const;
public:
- ObjCSelfInitChecker() {}
void checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const;
void checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const;
void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
@@ -94,19 +94,19 @@ enum SelfFlagEnum {
};
}
-REGISTER_MAP_WITH_PROGRAMSTATE(SelfFlag, SymbolRef, unsigned)
+REGISTER_MAP_WITH_PROGRAMSTATE(SelfFlag, SymbolRef, SelfFlagEnum)
REGISTER_TRAIT_WITH_PROGRAMSTATE(CalledInit, bool)
/// A call receiving a reference to 'self' invalidates the object that
/// 'self' contains. This keeps the "self flags" assigned to the 'self'
/// object before the call so we can assign them to the new object that 'self'
/// points to after the call.
-REGISTER_TRAIT_WITH_PROGRAMSTATE(PreCallSelfFlags, unsigned)
+REGISTER_TRAIT_WITH_PROGRAMSTATE(PreCallSelfFlags, SelfFlagEnum)
static SelfFlagEnum getSelfFlags(SVal val, ProgramStateRef state) {
if (SymbolRef sym = val.getAsSymbol())
- if (const unsigned *attachedFlags = state->get<SelfFlag>(sym))
- return (SelfFlagEnum)*attachedFlags;
+ if (const SelfFlagEnum *attachedFlags = state->get<SelfFlag>(sym))
+ return *attachedFlags;
return SelfFlag_None;
}
@@ -118,7 +118,8 @@ static void addSelfFlag(ProgramStateRef state, SVal val,
SelfFlagEnum flag, CheckerContext &C) {
// We tag the symbol that the SVal wraps.
if (SymbolRef sym = val.getAsSymbol()) {
- state = state->set<SelfFlag>(sym, getSelfFlags(val, state) | flag);
+ state = state->set<SelfFlag>(sym,
+ SelfFlagEnum(getSelfFlags(val, state) | flag));
C.addTransition(state);
}
}
@@ -156,10 +157,7 @@ void ObjCSelfInitChecker::checkForInvalidSelf(const Expr *E, CheckerContext &C,
if (!N)
return;
- if (!BT)
- BT.reset(new BugType(this, "Missing \"self = [(super or self) init...]\"",
- categories::CoreFoundationObjectiveC));
- C.emitReport(std::make_unique<PathSensitiveBugReport>(*BT, errorStr, N));
+ C.emitReport(std::make_unique<PathSensitiveBugReport>(BT, errorStr, N));
}
void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg,
@@ -251,11 +249,12 @@ void ObjCSelfInitChecker::checkPreCall(const CallEvent &CE,
for (unsigned i = 0; i < NumArgs; ++i) {
SVal argV = CE.getArgSVal(i);
if (isSelfVar(argV, C)) {
- unsigned selfFlags = getSelfFlags(state->getSVal(argV.castAs<Loc>()), C);
+ SelfFlagEnum selfFlags =
+ getSelfFlags(state->getSVal(argV.castAs<Loc>()), C);
C.addTransition(state->set<PreCallSelfFlags>(selfFlags));
return;
} else if (hasSelfFlag(argV, SelfFlag_Self, C)) {
- unsigned selfFlags = getSelfFlags(argV, C);
+ SelfFlagEnum selfFlags = getSelfFlags(argV, C);
C.addTransition(state->set<PreCallSelfFlags>(selfFlags));
return;
}
@@ -270,7 +269,7 @@ void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE,
return;
ProgramStateRef state = C.getState();
- SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>();
+ SelfFlagEnum prevFlags = state->get<PreCallSelfFlags>();
if (!prevFlags)
return;
state = state->remove<PreCallSelfFlags>();
@@ -338,7 +337,7 @@ void ObjCSelfInitChecker::printState(raw_ostream &Out, ProgramStateRef State,
const char *NL, const char *Sep) const {
SelfFlagTy FlagMap = State->get<SelfFlag>();
bool DidCallInit = State->get<CalledInit>();
- SelfFlagEnum PreCallFlags = (SelfFlagEnum)State->get<PreCallSelfFlags>();
+ SelfFlagEnum PreCallFlags = State->get<PreCallSelfFlags>();
if (FlagMap.isEmpty() && !DidCallInit && !PreCallFlags)
return;
@@ -360,18 +359,17 @@ void ObjCSelfInitChecker::printState(raw_ostream &Out, ProgramStateRef State,
}
Out << NL;
- for (SelfFlagTy::iterator I = FlagMap.begin(), E = FlagMap.end();
- I != E; ++I) {
- Out << I->first << " : ";
+ for (auto [Sym, Flag] : FlagMap) {
+ Out << Sym << " : ";
- if (I->second == SelfFlag_None)
+ if (Flag == SelfFlag_None)
Out << "none";
- if (I->second & SelfFlag_Self)
+ if (Flag & SelfFlag_Self)
Out << "self variable";
- if (I->second & SelfFlag_InitRes) {
- if (I->second != SelfFlag_InitRes)
+ if (Flag & SelfFlag_InitRes) {
+ if (Flag != SelfFlag_InitRes)
Out << " | ";
Out << "result of init method";
}
@@ -411,7 +409,7 @@ static bool isSelfVar(SVal location, CheckerContext &C) {
AnalysisDeclContext *analCtx = C.getCurrentAnalysisDeclContext();
if (!analCtx->getSelfDecl())
return false;
- if (!location.getAs<loc::MemRegionVal>())
+ if (!isa<loc::MemRegionVal>(location))
return false;
loc::MemRegionVal MRV = location.castAs<loc::MemRegionVal>();
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
index 3547b7bb61a2..eb40711812e1 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
@@ -26,18 +26,19 @@ namespace {
class ObjCSuperDeallocChecker
: public Checker<check::PostObjCMessage, check::PreObjCMessage,
check::PreCall, check::Location> {
-
- mutable IdentifierInfo *IIdealloc, *IINSObject;
+ mutable IdentifierInfo *IIdealloc = nullptr;
+ mutable IdentifierInfo *IINSObject = nullptr;
mutable Selector SELdealloc;
- std::unique_ptr<BugType> DoubleSuperDeallocBugType;
+ const BugType DoubleSuperDeallocBugType{
+ this, "[super dealloc] should not be called more than once",
+ categories::CoreFoundationObjectiveC};
void initIdentifierInfoAndSelectors(ASTContext &Ctx) const;
bool isSuperDeallocMessage(const ObjCMethodCall &M) const;
public:
- ObjCSuperDeallocChecker();
void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
@@ -188,7 +189,7 @@ void ObjCSuperDeallocChecker::reportUseAfterDealloc(SymbolRef Sym,
Desc = "Use of 'self' after it has been deallocated";
// Generate the report.
- auto BR = std::make_unique<PathSensitiveBugReport>(*DoubleSuperDeallocBugType,
+ auto BR = std::make_unique<PathSensitiveBugReport>(DoubleSuperDeallocBugType,
Desc, ErrNode);
BR->addRange(S->getSourceRange());
BR->addVisitor(std::make_unique<SuperDeallocBRVisitor>(Sym));
@@ -213,14 +214,6 @@ void ObjCSuperDeallocChecker::diagnoseCallArguments(const CallEvent &CE,
}
}
-ObjCSuperDeallocChecker::ObjCSuperDeallocChecker()
- : IIdealloc(nullptr), IINSObject(nullptr) {
-
- DoubleSuperDeallocBugType.reset(
- new BugType(this, "[super dealloc] should not be called more than once",
- categories::CoreFoundationObjectiveC));
-}
-
void
ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(ASTContext &Ctx) const {
if (IIdealloc)
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
index c9828c36a06a..1c2d84254d46 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
@@ -12,16 +12,17 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
-#include "clang/Analysis/PathDiagnostic.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprObjC.h"
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "llvm/ADT/STLExtras.h"
using namespace clang;
using namespace ento;
@@ -48,9 +49,7 @@ static void Scan(IvarUsageMap& M, const Stmt *S) {
}
if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S))
- for (PseudoObjectExpr::const_semantics_iterator
- i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) {
- const Expr *sub = *i;
+ for (const Expr *sub : POE->semantics()) {
if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))
sub = OVE->getSourceExpr();
Scan(M, sub);
@@ -134,8 +133,8 @@ static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
// Any potentially unused ivars?
bool hasUnused = false;
- for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
- if (I->second == Unused) {
+ for (IVarState State : llvm::make_second_range(M))
+ if (State == Unused) {
hasUnused = true;
break;
}
@@ -152,16 +151,16 @@ static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM);
// Find ivars that are unused.
- for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
- if (I->second == Unused) {
+ for (auto [Ivar, State] : M)
+ if (State == Unused) {
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
- os << "Instance variable '" << *I->first << "' in class '" << *ID
+ os << "Instance variable '" << *Ivar << "' in class '" << *ID
<< "' is never used by the methods in its @implementation "
"(although it may be used by category methods).";
PathDiagnosticLocation L =
- PathDiagnosticLocation::create(I->first, BR.getSourceManager());
+ PathDiagnosticLocation::create(Ivar, BR.getSourceManager());
BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization",
os.str(), L);
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
index 40472ccfe7e6..eee9449f3180 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
@@ -32,7 +32,7 @@ using namespace ento;
namespace {
class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
private:
- mutable std::unique_ptr<BugType> PaddingBug;
+ const BugType PaddingBug{this, "Excessive Padding", "Performance"};
mutable BugReporter *BR;
public:
@@ -182,7 +182,7 @@ public:
return false;
};
- if (std::any_of(RD->field_begin(), RD->field_end(), IsTrickyField))
+ if (llvm::any_of(RD->fields(), IsTrickyField))
return true;
return false;
}
@@ -273,7 +273,7 @@ public:
SmallVector<const FieldDecl *, 20> OptimalFieldsOrder;
while (!Fields.empty()) {
unsigned TrailingZeros =
- llvm::countTrailingZeros((unsigned long long)NewOffset.getQuantity());
+ llvm::countr_zero((unsigned long long)NewOffset.getQuantity());
// If NewOffset is zero, then countTrailingZeros will be 64. Shifting
// 64 will overflow our unsigned long long. Shifting 63 will turn
// our long long (and CharUnits internal type) negative. So shift 62.
@@ -310,10 +310,6 @@ public:
void reportRecord(
const RecordDecl *RD, CharUnits BaselinePad, CharUnits OptimalPad,
const SmallVector<const FieldDecl *, 20> &OptimalFieldsOrder) const {
- if (!PaddingBug)
- PaddingBug =
- std::make_unique<BugType>(this, "Excessive Padding", "Performance");
-
SmallString<100> Buf;
llvm::raw_svector_ostream Os(Buf);
Os << "Excessive padding in '";
@@ -332,17 +328,16 @@ public:
}
Os << " (" << BaselinePad.getQuantity() << " padding bytes, where "
- << OptimalPad.getQuantity() << " is optimal). \n"
- << "Optimal fields order: \n";
+ << OptimalPad.getQuantity() << " is optimal). "
+ << "Optimal fields order: ";
for (const auto *FD : OptimalFieldsOrder)
- Os << FD->getName() << ", \n";
+ Os << FD->getName() << ", ";
Os << "consider reordering the fields or adding explicit padding "
"members.";
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::create(RD, BR->getSourceManager());
- auto Report =
- std::make_unique<BasicBugReport>(*PaddingBug, Os.str(), CELoc);
+ auto Report = std::make_unique<BasicBugReport>(PaddingBug, Os.str(), CELoc);
Report->setDeclWithIssue(RD);
Report->addRange(RD->getSourceRange());
BR->emitReport(std::move(Report));
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
index d3e2849a0ce6..1141f07428b4 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
@@ -11,13 +11,14 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/StringRef.h"
using namespace clang;
using namespace ento;
@@ -55,8 +56,8 @@ class PointerArithChecker
bool PointedNeeded = false) const;
void initAllocIdentifiers(ASTContext &C) const;
- mutable std::unique_ptr<BuiltinBug> BT_pointerArith;
- mutable std::unique_ptr<BuiltinBug> BT_polyArray;
+ const BugType BT_pointerArith{this, "Dangerous pointer arithmetic"};
+ const BugType BT_polyArray{this, "Dangerous pointer arithmetic"};
mutable llvm::SmallSet<IdentifierInfo *, 8> AllocFunctions;
public:
@@ -79,10 +80,9 @@ void PointerArithChecker::checkDeadSymbols(SymbolReaper &SR,
// see http://reviews.llvm.org/D14203 for further information.
/*ProgramStateRef State = C.getState();
RegionStateTy RegionStates = State->get<RegionState>();
- for (RegionStateTy::iterator I = RegionStates.begin(), E = RegionStates.end();
- I != E; ++I) {
- if (!SR.isLiveRegion(I->first))
- State = State->remove<RegionState>(I->first);
+ for (const MemRegion *Reg: llvm::make_first_range(RegionStates)) {
+ if (!SR.isLiveRegion(Reg))
+ State = State->remove<RegionState>(Reg);
}
C.addTransition(State);*/
}
@@ -168,13 +168,10 @@ void PointerArithChecker::reportPointerArithMisuse(const Expr *E,
if (!IsPolymorphic)
return;
if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
- if (!BT_polyArray)
- BT_polyArray.reset(new BuiltinBug(
- this, "Dangerous pointer arithmetic",
- "Pointer arithmetic on a pointer to base class is dangerous "
- "because derived and base class may have different size."));
- auto R = std::make_unique<PathSensitiveBugReport>(
- *BT_polyArray, BT_polyArray->getDescription(), N);
+ constexpr llvm::StringLiteral Msg =
+ "Pointer arithmetic on a pointer to base class is dangerous "
+ "because derived and base class may have different size.";
+ auto R = std::make_unique<PathSensitiveBugReport>(BT_polyArray, Msg, N);
R->addRange(E->getSourceRange());
R->markInteresting(ArrayRegion);
C.emitReport(std::move(R));
@@ -191,13 +188,10 @@ void PointerArithChecker::reportPointerArithMisuse(const Expr *E,
return;
if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
- if (!BT_pointerArith)
- BT_pointerArith.reset(new BuiltinBug(this, "Dangerous pointer arithmetic",
- "Pointer arithmetic on non-array "
- "variables relies on memory layout, "
- "which is dangerous."));
- auto R = std::make_unique<PathSensitiveBugReport>(
- *BT_pointerArith, BT_pointerArith->getDescription(), N);
+ constexpr llvm::StringLiteral Msg =
+ "Pointer arithmetic on non-array variables relies on memory layout, "
+ "which is dangerous.";
+ auto R = std::make_unique<PathSensitiveBugReport>(BT_pointerArith, Msg, N);
R->addRange(SR);
R->markInteresting(Region);
C.emitReport(std::move(R));
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
index 81c19d9a0940..2438cf30b39b 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/StringRef.h"
using namespace clang;
using namespace ento;
@@ -24,7 +25,7 @@ using namespace ento;
namespace {
class PointerSubChecker
: public Checker< check::PreStmt<BinaryOperator> > {
- mutable std::unique_ptr<BuiltinBug> BT;
+ const BugType BT{this, "Pointer subtraction"};
public:
void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
@@ -58,13 +59,10 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
return;
if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
- if (!BT)
- BT.reset(
- new BuiltinBug(this, "Pointer subtraction",
- "Subtraction of two pointers that do not point to "
- "the same memory chunk may cause incorrect result."));
- auto R =
- std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
+ constexpr llvm::StringLiteral Msg =
+ "Subtraction of two pointers that do not point to the same memory "
+ "chunk may cause incorrect result.";
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
R->addRange(B->getSourceRange());
C.emitReport(std::move(R));
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index ee71b55a39e6..fa8572cf85ed 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -21,6 +21,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -77,7 +78,7 @@ public:
CK_C11LockChecker,
CK_NumCheckKinds
};
- DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ bool ChecksEnabled[CK_NumCheckKinds] = {false};
CheckerNameRef CheckNames[CK_NumCheckKinds];
private:
@@ -86,7 +87,7 @@ private:
CheckerKind CheckKind) const;
CallDescriptionMap<FnCheck> PThreadCallbacks = {
// Init.
- {{"pthread_mutex_init", 2}, &PthreadLockChecker::InitAnyLock},
+ {{{"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.
@@ -94,74 +95,74 @@ private:
// 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},
+ {{{"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},
+ {{{"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},
+ {{{"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},
+ {{{"pthread_mutex_destroy"}, 1}, &PthreadLockChecker::DestroyPthreadLock},
+ {{{"lck_mtx_destroy"}, 2}, &PthreadLockChecker::DestroyXNULock},
// TODO: pthread_rwlock_destroy(1 argument).
// TODO: lck_rw_destroy(2 arguments).
};
CallDescriptionMap<FnCheck> FuchsiaCallbacks = {
// Init.
- {{"spin_lock_init", 1}, &PthreadLockChecker::InitAnyLock},
+ {{{"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},
+ {{{"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},
+ {{{"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},
+ {{{"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},
+ {{{"mtx_init"}, 2}, &PthreadLockChecker::InitAnyLock},
// Acquire.
- {{"mtx_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{{"mtx_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock},
// Try.
- {{"mtx_trylock", 1}, &PthreadLockChecker::TryC11Lock},
- {{"mtx_timedlock", 2}, &PthreadLockChecker::TryC11Lock},
+ {{{"mtx_trylock"}, 1}, &PthreadLockChecker::TryC11Lock},
+ {{{"mtx_timedlock"}, 2}, &PthreadLockChecker::TryC11Lock},
// Release.
- {{"mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{{"mtx_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
// Destroy
- {{"mtx_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock},
+ {{{"mtx_destroy"}, 1}, &PthreadLockChecker::DestroyPthreadLock},
};
ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
@@ -290,6 +291,7 @@ ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex(
// Existence in DestroyRetVal ensures existence in LockMap.
// Existence in Destroyed also ensures that the lock state for lockR is either
// UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed.
+ assert(lstate);
assert(lstate->isUntouchedAndPossiblyDestroyed() ||
lstate->isUnlockedAndPossiblyDestroyed());
@@ -681,9 +683,7 @@ ProgramStateRef PthreadLockChecker::checkRegionChanges(
// 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())
+ if (IsLibraryFunction && !llvm::is_contained(ExplicitRegions, R))
continue;
State = State->remove<LockMap>(R);
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 3f3267ff9391..7e74b418b335 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
@@ -14,6 +14,7 @@
#include "RetainCountChecker.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -45,7 +46,7 @@ static ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) {
void RefVal::print(raw_ostream &Out) const {
if (!T.isNull())
- Out << "Tracked " << T.getAsString() << " | ";
+ Out << "Tracked " << T << " | ";
switch (getKind()) {
default: llvm_unreachable("Invalid RefVal kind");
@@ -154,10 +155,8 @@ void RetainCountChecker::checkPostStmt(const BlockExpr *BE,
ProgramStateRef state = C.getState();
auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
- BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
- E = R->referenced_vars_end();
-
- if (I == E)
+ auto ReferencedVars = R->referenced_vars();
+ if (ReferencedVars.empty())
return;
// FIXME: For now we invalidate the tracking of all symbols passed to blocks
@@ -167,8 +166,8 @@ void RetainCountChecker::checkPostStmt(const BlockExpr *BE,
const LocationContext *LC = C.getLocationContext();
MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
- for ( ; I != E; ++I) {
- const VarRegion *VR = I.getCapturedRegion();
+ for (auto Var : ReferencedVars) {
+ const VarRegion *VR = Var.getCapturedRegion();
if (VR->getSuperRegion() == R) {
VR = MemMgr.getVarRegion(VR->getDecl(), LC);
}
@@ -284,13 +283,13 @@ void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex,
void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE,
CheckerContext &C) const {
- Optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>();
+ std::optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>();
if (!IVarLoc)
return;
ProgramStateRef State = C.getState();
SymbolRef Sym = State->getSVal(*IVarLoc).getAsSymbol();
- if (!Sym || !dyn_cast_or_null<ObjCIvarRegion>(Sym->getOriginRegion()))
+ if (!Sym || !isa_and_nonnull<ObjCIvarRegion>(Sym->getOriginRegion()))
return;
// Accessing an ivar directly is unusual. If we've done that, be more
@@ -412,15 +411,15 @@ static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) {
return RetTy;
}
-static Optional<RefVal> refValFromRetEffect(RetEffect RE,
- QualType ResultTy) {
+static std::optional<RefVal> refValFromRetEffect(RetEffect RE,
+ QualType ResultTy) {
if (RE.isOwned()) {
return RefVal::makeOwned(RE.getObjKind(), ResultTy);
} else if (RE.notOwned()) {
return RefVal::makeNotOwned(RE.getObjKind(), ResultTy);
}
- return None;
+ return std::nullopt;
}
static bool isPointerToObject(QualType QT) {
@@ -692,7 +691,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ,
assert(Ex);
ResultTy = GetReturnType(Ex, C.getASTContext());
}
- if (Optional<RefVal> updatedRefVal = refValFromRetEffect(RE, ResultTy))
+ if (std::optional<RefVal> updatedRefVal = refValFromRetEffect(RE, ResultTy))
state = setRefBinding(state, Sym, *updatedRefVal);
}
@@ -767,7 +766,7 @@ ProgramStateRef RetainCountChecker::updateSymbol(ProgramStateRef state,
break;
}
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case DoNothing:
return state;
@@ -907,7 +906,7 @@ bool RetainCountChecker::evalCall(const CallEvent &Call,
const LocationContext *LCtx = C.getLocationContext();
using BehaviorSummary = RetainSummaryManager::BehaviorSummary;
- Optional<BehaviorSummary> BSmr =
+ std::optional<BehaviorSummary> BSmr =
SmrMgr.canEval(CE, FD, hasTrustedImplementationAnnotation);
// See if it's one of the specific functions we know how to eval.
@@ -945,7 +944,8 @@ 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);
+ CE, LCtx, C.getSValBuilder().makeNullWithType(ResultTy),
+ /*Invalidate=*/false);
C.addTransition(NullOutputState, &getCastFailTag());
// And on the original branch assume that both input and
@@ -1188,14 +1188,14 @@ ProgramStateRef RetainCountChecker::checkRegionChanges(
if (!invalidated)
return state;
- llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols;
+ llvm::SmallPtrSet<SymbolRef, 8> AllowedSymbols;
for (const MemRegion *I : ExplicitRegions)
if (const SymbolicRegion *SR = I->StripCasts()->getAs<SymbolicRegion>())
- WhitelistedSymbols.insert(SR->getSymbol());
+ AllowedSymbols.insert(SR->getSymbol());
for (SymbolRef sym : *invalidated) {
- if (WhitelistedSymbols.count(sym))
+ if (AllowedSymbols.count(sym))
continue;
// Remove any existing reference-count binding.
state = removeRefBinding(state, sym);
@@ -1335,7 +1335,7 @@ void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const {
RetainSummaryManager &SmrMgr = getSummaryManager(Ctx);
const LocationContext *LCtx = Ctx.getLocationContext();
const Decl *D = LCtx->getDecl();
- Optional<AnyCall> C = AnyCall::forDecl(D);
+ std::optional<AnyCall> C = AnyCall::forDecl(D);
if (!C || SmrMgr.isTrustedReferenceCountImplementation(D))
return;
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 223e28c2c5b8..d4d7c4c74c56 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
@@ -36,7 +36,6 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableList.h"
-#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
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 64ac6bc4c06b..c3acb73ba717 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
@@ -15,6 +15,7 @@
#include "RetainCountChecker.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -73,11 +74,8 @@ RefCountBug::RefCountBug(CheckerNameRef Checker, RefCountBugKind BT)
static bool isNumericLiteralExpression(const Expr *E) {
// FIXME: This set of cases was copied from SemaExprObjC.
- return isa<IntegerLiteral>(E) ||
- isa<CharacterLiteral>(E) ||
- isa<FloatingLiteral>(E) ||
- isa<ObjCBoolLiteralExpr>(E) ||
- isa<CXXBoolLiteralExpr>(E);
+ return isa<IntegerLiteral, CharacterLiteral, FloatingLiteral,
+ ObjCBoolLiteralExpr, CXXBoolLiteralExpr>(E);
}
/// If type represents a pointer to CXXRecordDecl,
@@ -168,13 +166,12 @@ static bool shouldGenerateNote(llvm::raw_string_ostream &os,
/// Finds argument index of the out paramter in the call @c S
/// corresponding to the symbol @c Sym.
-/// If none found, returns None.
-static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt,
- const LocationContext *LCtx,
- SymbolRef &Sym,
- Optional<CallEventRef<>> CE) {
+/// If none found, returns std::nullopt.
+static std::optional<unsigned>
+findArgIdxOfSymbol(ProgramStateRef CurrSt, const LocationContext *LCtx,
+ SymbolRef &Sym, std::optional<CallEventRef<>> CE) {
if (!CE)
- return None;
+ return std::nullopt;
for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++)
if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion())
@@ -182,25 +179,25 @@ static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt,
if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymbol() == Sym)
return Idx;
- return None;
+ return std::nullopt;
}
-static Optional<std::string> findMetaClassAlloc(const Expr *Callee) {
+static std::optional<std::string> findMetaClassAlloc(const Expr *Callee) {
if (const auto *ME = dyn_cast<MemberExpr>(Callee)) {
if (ME->getMemberDecl()->getNameAsString() != "alloc")
- return None;
+ return std::nullopt;
const Expr *This = ME->getBase()->IgnoreParenImpCasts();
if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) {
const ValueDecl *VD = DRE->getDecl();
if (VD->getNameAsString() != "metaClass")
- return None;
+ return std::nullopt;
if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext()))
return RD->getNameAsString();
}
}
- return None;
+ return std::nullopt;
}
static std::string findAllocatedObjectName(const Stmt *S, QualType QT) {
@@ -237,8 +234,8 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
os << "Operator 'new'";
} else {
assert(isa<ObjCMessageExpr>(S));
- CallEventRef<ObjCMethodCall> Call =
- Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx);
+ CallEventRef<ObjCMethodCall> Call = Mgr.getObjCMethodCall(
+ cast<ObjCMessageExpr>(S), CurrSt, LCtx, {nullptr, 0});
switch (Call->getMessageKind()) {
case OCM_Message:
@@ -253,7 +250,7 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
}
}
- Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx);
+ std::optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx, {nullptr, 0});
auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE);
// If index is not found, we assume that the symbol was returned.
@@ -264,14 +261,12 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
}
if (CurrV.getObjKind() == ObjKind::CF) {
- os << "a Core Foundation object of type '"
- << Sym->getType().getAsString() << "' with a ";
+ os << "a Core Foundation object of type '" << Sym->getType() << "' with a ";
} else if (CurrV.getObjKind() == ObjKind::OS) {
os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType())
<< "' with a ";
} else if (CurrV.getObjKind() == ObjKind::Generalized) {
- os << "an object of type '" << Sym->getType().getAsString()
- << "' with a ";
+ os << "an object of type '" << Sym->getType() << "' with a ";
} else {
assert(CurrV.getObjKind() == ObjKind::ObjC);
QualType T = Sym->getType();
@@ -279,8 +274,7 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
os << "an Objective-C object with a ";
} else {
const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T);
- os << "an instance of " << PT->getPointeeType().getAsString()
- << " with a ";
+ os << "an instance of " << PT->getPointeeType() << " with a ";
}
}
@@ -608,16 +602,17 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
return std::move(P);
}
-static Optional<std::string> describeRegion(const MemRegion *MR) {
+static std::optional<std::string> describeRegion(const MemRegion *MR) {
if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))
return std::string(VR->getDecl()->getName());
// Once we support more storage locations for bindings,
// this would need to be improved.
- return None;
+ return std::nullopt;
}
using Bindings = llvm::SmallVector<std::pair<const MemRegion *, SVal>, 4>;
+namespace {
class VarBindingsCollector : public StoreManager::BindingsHandler {
SymbolRef Sym;
Bindings &Result;
@@ -638,6 +633,7 @@ public:
return true;
}
};
+} // namespace
Bindings getAllVarBindingsForSymbol(ProgramStateManager &Manager,
const ExplodedNode *Node, SymbolRef Sym) {
@@ -734,7 +730,7 @@ static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
const LocationContext *InterestingMethodContext = nullptr;
if (InitMethodContext) {
const ProgramPoint AllocPP = AllocationNode->getLocation();
- if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
+ if (std::optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
if (ME->getMethodFamily() == OMF_alloc)
InterestingMethodContext = InitMethodContext;
@@ -777,7 +773,7 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
os << "Object leaked: ";
- Optional<std::string> RegionDescription = describeRegion(LastBinding);
+ std::optional<std::string> RegionDescription = describeRegion(LastBinding);
if (RegionDescription) {
os << "object allocated and stored into '" << *RegionDescription << '\'';
} else {
@@ -790,9 +786,6 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
assert(RV);
if (RV->getKind() == RefVal::ErrorLeakReturned) {
- // FIXME: Per comments in rdar://6320065, "create" only applies to CF
- // objects. Only "copy", "alloc", "retain" and "new" transfer ownership
- // to the caller for NS objects.
const Decl *D = &EndN->getCodeDecl();
os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "
@@ -923,7 +916,7 @@ void RefLeakReport::createDescription(CheckerContext &Ctx) {
llvm::raw_string_ostream os(Description);
os << "Potential leak of an object";
- Optional<std::string> RegionDescription =
+ std::optional<std::string> RegionDescription =
describeRegion(AllocBindingToReport);
if (RegionDescription) {
os << " stored into '" << *RegionDescription << '\'';
@@ -975,7 +968,7 @@ void RefLeakReport::findBindingToReport(CheckerContext &Ctx,
// Let's pick one of them at random (if there is something to pick from).
AllocBindingToReport = AllVarBindings[0].first;
- // Because 'AllocBindingToReport' is not the the same as
+ // Because 'AllocBindingToReport' is not the same as
// 'AllocFirstBinding', we need to explain how the leaking object
// got from one to another.
//
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
index 885750218b9e..09d82ebabd4c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -25,7 +26,9 @@ using namespace ento;
namespace {
class ReturnPointerRangeChecker :
public Checker< check::PreStmt<ReturnStmt> > {
- mutable std::unique_ptr<BuiltinBug> BT;
+ // FIXME: This bug correspond to CWE-466. Eventually we should have bug
+ // types explicitly reference such exploit categories (when applicable).
+ const BugType BT{this, "Buffer overflow"};
public:
void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
@@ -40,6 +43,10 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
if (!RetE)
return;
+ // Skip "body farmed" functions.
+ if (RetE->getSourceRange().isInvalid())
+ return;
+
SVal V = C.getSVal(RetE);
const MemRegion *R = V.getAsRegion();
@@ -63,32 +70,55 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
if (Idx == ElementCount)
return;
- ProgramStateRef StInBound = state->assumeInBound(Idx, ElementCount, true);
- ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false);
+ ProgramStateRef StInBound, StOutBound;
+ std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, ElementCount);
if (StOutBound && !StInBound) {
ExplodedNode *N = C.generateErrorNode(StOutBound);
if (!N)
return;
- // FIXME: This bug correspond to CWE-466. Eventually we should have bug
- // types explicitly reference such exploit categories (when applicable).
- if (!BT)
- BT.reset(new BuiltinBug(
- this, "Buffer overflow",
- "Returned pointer value points outside the original object "
- "(potential buffer overflow)"));
-
- // FIXME: It would be nice to eventually make this diagnostic more clear,
- // e.g., by referencing the original declaration or by saying *why* this
- // reference is outside the range.
+ constexpr llvm::StringLiteral Msg =
+ "Returned pointer value points outside the original object "
+ "(potential buffer overflow)";
// Generate a report for this bug.
- auto report =
- std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
-
- report->addRange(RetE->getSourceRange());
- C.emitReport(std::move(report));
+ auto Report = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
+ Report->addRange(RetE->getSourceRange());
+
+ const auto ConcreteElementCount = ElementCount.getAs<nonloc::ConcreteInt>();
+ const auto ConcreteIdx = Idx.getAs<nonloc::ConcreteInt>();
+
+ const auto *DeclR = ER->getSuperRegion()->getAs<DeclRegion>();
+
+ if (DeclR)
+ Report->addNote("Original object declared here",
+ {DeclR->getDecl(), C.getSourceManager()});
+
+ if (ConcreteElementCount) {
+ SmallString<128> SBuf;
+ llvm::raw_svector_ostream OS(SBuf);
+ OS << "Original object ";
+ if (DeclR) {
+ OS << "'";
+ DeclR->getDecl()->printName(OS);
+ OS << "' ";
+ }
+ OS << "is an array of " << ConcreteElementCount->getValue() << " '";
+ ER->getValueType().print(OS,
+ PrintingPolicy(C.getASTContext().getLangOpts()));
+ OS << "' objects";
+ if (ConcreteIdx) {
+ OS << ", returned pointer points at index " << ConcreteIdx->getValue();
+ }
+
+ Report->addNote(SBuf,
+ {RetE, C.getSourceManager(), C.getLocationContext()});
+ }
+
+ bugreporter::trackExpressionValue(N, RetE, *Report);
+
+ C.emitReport(std::move(Report));
}
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
index 5266cbf86b44..efffbf2ee755 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
@@ -24,8 +24,8 @@ using namespace ento;
namespace {
class ReturnUndefChecker : public Checker< check::PreStmt<ReturnStmt> > {
- mutable std::unique_ptr<BuiltinBug> BT_Undef;
- mutable std::unique_ptr<BuiltinBug> BT_NullReference;
+ const BugType BT_Undef{this, "Garbage return value"};
+ const BugType BT_NullReference{this, "Returning null reference"};
void emitUndef(CheckerContext &C, const Expr *RetE) const;
void checkReference(CheckerContext &C, const Expr *RetE,
@@ -77,14 +77,13 @@ void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS,
}
}
-static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE,
- const Expr *TrackingE = nullptr) {
+static void emitBug(CheckerContext &C, const BugType &BT, StringRef Msg,
+ const Expr *RetE, const Expr *TrackingE = nullptr) {
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
- auto Report =
- std::make_unique<PathSensitiveBugReport>(BT, BT.getDescription(), N);
+ auto Report = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
Report->addRange(RetE->getSourceRange());
bugreporter::trackExpressionValue(N, TrackingE ? TrackingE : RetE, *Report);
@@ -93,11 +92,7 @@ static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE,
}
void ReturnUndefChecker::emitUndef(CheckerContext &C, const Expr *RetE) const {
- if (!BT_Undef)
- BT_Undef.reset(
- new BuiltinBug(this, "Garbage return value",
- "Undefined or garbage value returned to caller"));
- emitBug(C, *BT_Undef, RetE);
+ emitBug(C, BT_Undef, "Undefined or garbage value returned to caller", RetE);
}
void ReturnUndefChecker::checkReference(CheckerContext &C, const Expr *RetE,
@@ -112,10 +107,8 @@ void ReturnUndefChecker::checkReference(CheckerContext &C, const Expr *RetE,
}
// The return value is known to be null. Emit a bug report.
- if (!BT_NullReference)
- BT_NullReference.reset(new BuiltinBug(this, "Returning null reference"));
-
- emitBug(C, *BT_NullReference, RetE, bugreporter::getDerefExpr(RetE));
+ emitBug(C, BT_NullReference, BT_NullReference.getDescription(), RetE,
+ bugreporter::getDerefExpr(RetE));
}
void ento::registerReturnUndefChecker(CheckerManager &mgr) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
index 14ecede17083..c3112ebe4e79 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
@@ -14,10 +14,11 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -58,7 +59,7 @@ private:
} // namespace
static std::string getName(const CallEvent &Call) {
- std::string Name = "";
+ std::string Name;
if (const auto *MD = dyn_cast<CXXMethodDecl>(Call.getDecl()))
if (const CXXRecordDecl *RD = MD->getParent())
Name += RD->getNameAsString() + "::";
@@ -69,11 +70,11 @@ static std::string getName(const CallEvent &Call) {
// The predefinitions ('CDM') could break due to the ever growing code base.
// Check for the expected invariants and see whether they apply.
-static Optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV,
- CheckerContext &C) {
+static std::optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV,
+ CheckerContext &C) {
auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>();
if (!ReturnDV)
- return None;
+ return std::nullopt;
if (ExpectedValue)
return C.getState()->isNull(*ReturnDV).isConstrainedTrue();
@@ -89,7 +90,8 @@ void ReturnValueChecker::checkPostCall(const CallEvent &Call,
SVal ReturnV = Call.getReturnValue();
bool ExpectedValue = *RawExpectedValue;
- Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
+ std::optional<bool> IsInvariantBreak =
+ isInvariantBreak(ExpectedValue, ReturnV, C);
if (!IsInvariantBreak)
return;
@@ -136,7 +138,8 @@ void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS,
SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext());
bool ExpectedValue = *RawExpectedValue;
- Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
+ std::optional<bool> IsInvariantBreak =
+ isInvariantBreak(ExpectedValue, ReturnV, C);
if (!IsInvariantBreak)
return;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp
index 933e0146ff59..788f2875863c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp
@@ -12,6 +12,7 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -60,7 +61,7 @@ class STLAlgorithmModeling : public Checker<eval::Call> {
public:
STLAlgorithmModeling() = default;
- bool AggressiveStdFindModeling;
+ bool AggressiveStdFindModeling = false;
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
}; //
@@ -130,7 +131,7 @@ void STLAlgorithmModeling::Find(CheckerContext &C, const CallExpr *CE,
nonloc::SymbolVal(NewPos->getOffset()),
nonloc::SymbolVal(Pos->getOffset()),
SVB.getConditionType());
- assert(GreaterOrEqual.getAs<DefinedSVal>() &&
+ assert(isa<DefinedSVal>(GreaterOrEqual) &&
"Symbol comparison must be a `DefinedSVal`");
StateFound = StateFound->assume(GreaterOrEqual.castAs<DefinedSVal>(), true);
}
@@ -152,7 +153,7 @@ void STLAlgorithmModeling::Find(CheckerContext &C, const CallExpr *CE,
nonloc::SymbolVal(NewPos->getOffset()),
nonloc::SymbolVal(Pos->getOffset()),
SVB.getConditionType());
- assert(Less.getAs<DefinedSVal>() &&
+ assert(isa<DefinedSVal>(Less) &&
"Symbol comparison must be a `DefinedSVal`");
StateFound = StateFound->assume(Less.castAs<DefinedSVal>(), true);
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
index 8d380ed1b93d..7cbe271dfbf9 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include <utility>
@@ -51,10 +52,13 @@ class SimpleStreamChecker : public Checker<check::PostCall,
check::PreCall,
check::DeadSymbols,
check::PointerEscape> {
- CallDescription OpenFn, CloseFn;
+ const CallDescription OpenFn{{"fopen"}, 2};
+ const CallDescription CloseFn{{"fclose"}, 1};
- std::unique_ptr<BugType> DoubleCloseBugType;
- std::unique_ptr<BugType> LeakBugType;
+ const BugType DoubleCloseBugType{this, "Double fclose",
+ "Unix Stream API Error"};
+ const BugType LeakBugType{this, "Resource Leak", "Unix Stream API Error",
+ /*SuppressOnSink=*/true};
void reportDoubleClose(SymbolRef FileDescSym,
const CallEvent &Call,
@@ -66,8 +70,6 @@ class SimpleStreamChecker : public Checker<check::PostCall,
bool guaranteedNotToCloseFile(const CallEvent &Call) const;
public:
- SimpleStreamChecker();
-
/// Process fopen.
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
/// Process fclose.
@@ -88,38 +90,12 @@ public:
/// state. Let's store it in the ProgramState.
REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
-namespace {
-class StopTrackingCallback final : public SymbolVisitor {
- ProgramStateRef state;
-public:
- StopTrackingCallback(ProgramStateRef st) : state(std::move(st)) {}
- ProgramStateRef getState() const { return state; }
-
- bool VisitSymbol(SymbolRef sym) override {
- state = state->remove<StreamMap>(sym);
- return true;
- }
-};
-} // end anonymous namespace
-
-SimpleStreamChecker::SimpleStreamChecker()
- : OpenFn("fopen"), CloseFn("fclose", 1) {
- // Initialize the bug types.
- DoubleCloseBugType.reset(
- new BugType(this, "Double fclose", "Unix Stream API Error"));
-
- // Sinks are higher importance bugs as well as calls to assert() or exit(0).
- LeakBugType.reset(
- new BugType(this, "Resource Leak", "Unix Stream API Error",
- /*SuppressOnSink=*/true));
-}
-
void SimpleStreamChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
if (!Call.isGlobalCFunction())
return;
- if (!Call.isCalled(OpenFn))
+ if (!OpenFn.matches(Call))
return;
// Get the symbolic value corresponding to the file handle.
@@ -138,7 +114,7 @@ void SimpleStreamChecker::checkPreCall(const CallEvent &Call,
if (!Call.isGlobalCFunction())
return;
- if (!Call.isCalled(CloseFn))
+ if (!CloseFn.matches(Call))
return;
// Get the symbolic value corresponding to the file handle.
@@ -176,13 +152,11 @@ void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
ProgramStateRef State = C.getState();
SymbolVector LeakedStreams;
StreamMapTy TrackedStreams = State->get<StreamMap>();
- for (StreamMapTy::iterator I = TrackedStreams.begin(),
- E = TrackedStreams.end(); I != E; ++I) {
- SymbolRef Sym = I->first;
+ for (auto [Sym, StreamStatus] : TrackedStreams) {
bool IsSymDead = SymReaper.isDead(Sym);
// Collect leaked symbols.
- if (isLeaked(Sym, I->second, IsSymDead, State))
+ if (isLeaked(Sym, StreamStatus, IsSymDead, State))
LeakedStreams.push_back(Sym);
// Remove the dead symbol from the streams map.
@@ -207,7 +181,7 @@ void SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym,
// Generate the report.
auto R = std::make_unique<PathSensitiveBugReport>(
- *DoubleCloseBugType, "Closing a previously closed file stream", ErrNode);
+ DoubleCloseBugType, "Closing a previously closed file stream", ErrNode);
R->addRange(Call.getSourceRange());
R->markInteresting(FileDescSym);
C.emitReport(std::move(R));
@@ -220,7 +194,7 @@ void SimpleStreamChecker::reportLeaks(ArrayRef<SymbolRef> LeakedStreams,
// TODO: Identify the leaked file descriptor.
for (SymbolRef LeakedStream : LeakedStreams) {
auto R = std::make_unique<PathSensitiveBugReport>(
- *LeakBugType, "Opened file is never closed; potential resource leak",
+ LeakBugType, "Opened file is never closed; potential resource leak",
ErrNode);
R->markInteresting(LeakedStream);
C.emitReport(std::move(R));
@@ -254,11 +228,7 @@ SimpleStreamChecker::checkPointerEscape(ProgramStateRef State,
return State;
}
- for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
- E = Escaped.end();
- I != E; ++I) {
- SymbolRef Sym = *I;
-
+ for (SymbolRef Sym : Escaped) {
// The symbol escaped. Optimistically, assume that the corresponding file
// handle will be closed somewhere else.
State = State->remove<StreamMap>(Sym);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h
index 6a40f8eda5fa..b4352b450c7f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h
@@ -25,8 +25,6 @@ bool isStdSmartPtrCall(const CallEvent &Call);
bool isStdSmartPtr(const CXXRecordDecl *RD);
bool isStdSmartPtr(const Expr *E);
-bool isStdSmartPtr(const CXXRecordDecl *RD);
-
/// Returns whether the smart pointer is null or not.
bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
index 09e885e8133f..268fc742f050 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
@@ -23,6 +23,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
@@ -30,8 +31,9 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
-#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/ErrorHandling.h"
+#include <optional>
#include <string>
using namespace clang;
@@ -47,9 +49,8 @@ class SmartPtrModeling
public:
// Whether the checker should model for null dereferences of smart pointers.
- DefaultBool ModelSmartPtrDereference;
+ bool ModelSmartPtrDereference = false;
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
- void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
ProgramStateRef
checkRegionChanges(ProgramStateRef State,
@@ -70,7 +71,8 @@ private:
bool handleMoveCtr(const CallEvent &Call, CheckerContext &C,
const MemRegion *ThisRegion) const;
bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion,
- const MemRegion *OtherSmartPtrRegion) const;
+ const MemRegion *OtherSmartPtrRegion,
+ const CallEvent &Call) const;
void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const;
bool handleComparisionOp(const CallEvent &Call, CheckerContext &C) const;
bool handleOstreamOperator(const CallEvent &Call, CheckerContext &C) const;
@@ -84,10 +86,10 @@ private:
using SmartPtrMethodHandlerFn =
void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
- {{"reset"}, &SmartPtrModeling::handleReset},
- {{"release"}, &SmartPtrModeling::handleRelease},
- {{"swap", 1}, &SmartPtrModeling::handleSwapMethod},
- {{"get"}, &SmartPtrModeling::handleGet}};
+ {{{"reset"}}, &SmartPtrModeling::handleReset},
+ {{{"release"}}, &SmartPtrModeling::handleRelease},
+ {{{"swap"}, 1}, &SmartPtrModeling::handleSwapMethod},
+ {{{"get"}}, &SmartPtrModeling::handleGet}};
const CallDescription StdSwapCall{{"std", "swap"}, 2};
const CallDescription StdMakeUniqueCall{{"std", "make_unique"}};
const CallDescription StdMakeUniqueForOverwriteCall{
@@ -102,12 +104,8 @@ static bool hasStdClassWithName(const CXXRecordDecl *RD,
ArrayRef<llvm::StringLiteral> Names) {
if (!RD || !RD->getDeclContext()->isStdNamespace())
return false;
- if (RD->getDeclName().isIdentifier()) {
- StringRef Name = RD->getName();
- return llvm::any_of(Names, [&Name](StringRef GivenName) -> bool {
- return Name == GivenName;
- });
- }
+ if (RD->getDeclName().isIdentifier())
+ return llvm::is_contained(Names, RD->getName());
return false;
}
@@ -203,7 +201,7 @@ static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD) {
static QualType getPointerTypeFromTemplateArg(const CallEvent &Call,
CheckerContext &C) {
const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
- if (!FD || !FD->isFunctionTemplateSpecialization())
+ if (!FD || !FD->getPrimaryTemplate())
return {};
const auto &TemplateArgs = FD->getTemplateSpecializationArgs()->asArray();
if (TemplateArgs.size() == 0)
@@ -289,7 +287,7 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
if (ModelSmartPtrDereference && isStdOstreamOperatorCall(Call))
return handleOstreamOperator(Call, C);
- if (Call.isCalled(StdSwapCall)) {
+ if (StdSwapCall.matches(Call)) {
// Check the first arg, if it is of std::unique_ptr type.
assert(Call.getNumArgs() == 2 && "std::swap should have two arguments");
const Expr *FirstArg = Call.getArgExpr(0);
@@ -298,12 +296,12 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C);
}
- if (Call.isCalled(StdMakeUniqueCall) ||
- Call.isCalled(StdMakeUniqueForOverwriteCall)) {
+ if (matchesAny(Call, StdMakeUniqueCall, StdMakeUniqueForOverwriteCall)) {
if (!ModelSmartPtrDereference)
return false;
-
- const Optional<SVal> ThisRegionOpt = Call.getReturnValueUnderConstruction();
+
+ const std::optional<SVal> ThisRegionOpt =
+ Call.getReturnValueUnderConstruction();
if (!ThisRegionOpt)
return false;
@@ -383,11 +381,13 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
if (!ThisRegion)
return false;
+ QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
+
if (CC->getDecl()->isMoveConstructor())
return handleMoveCtr(Call, C, ThisRegion);
if (Call.getNumArgs() == 0) {
- auto NullVal = C.getSValBuilder().makeNull();
+ auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
C.addTransition(
@@ -588,10 +588,9 @@ void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State,
SymbolReaper &SR) const {
// Marking tracked symbols alive
TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
- for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) {
- SVal Val = I->second;
- for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) {
- SR.markLive(*si);
+ for (SVal Val : llvm::make_second_range(TrackedRegions)) {
+ for (SymbolRef Sym : Val.symbols()) {
+ SR.markLive(Sym);
}
}
}
@@ -644,7 +643,8 @@ void SmartPtrModeling::handleRelease(const CallEvent &Call,
*InnerPointVal);
}
- auto ValueToUpdate = C.getSValBuilder().makeNull();
+ QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
+ auto ValueToUpdate = C.getSValBuilder().makeNullWithType(ThisType);
State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
@@ -742,13 +742,15 @@ bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
if (!ThisRegion)
return false;
+ QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
+
const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion();
// In case of 'nullptr' or '0' assigned
if (!OtherSmartPtrRegion) {
bool AssignedNull = Call.getArgSVal(0).isZeroConstant();
if (!AssignedNull)
return false;
- auto NullVal = C.getSValBuilder().makeNull();
+ auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
llvm::raw_ostream &OS) {
@@ -762,7 +764,7 @@ bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
return true;
}
- return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
+ return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call);
}
bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C,
@@ -771,17 +773,19 @@ bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C,
if (!OtherSmartPtrRegion)
return false;
- return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
+ return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call);
}
bool SmartPtrModeling::updateMovedSmartPointers(
CheckerContext &C, const MemRegion *ThisRegion,
- const MemRegion *OtherSmartPtrRegion) const {
+ const MemRegion *OtherSmartPtrRegion, const CallEvent &Call) const {
ProgramStateRef State = C.getState();
+ QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion);
if (OtherInnerPtr) {
State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
- auto NullVal = C.getSValBuilder().makeNull();
+
+ auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
bool IsArgValNull = OtherInnerPtr->isZeroConstant();
@@ -807,7 +811,8 @@ bool SmartPtrModeling::updateMovedSmartPointers(
} else {
// In case we dont know anything about value we are moving from
// remove the entry from map for which smart pointer got moved to.
- auto NullVal = C.getSValBuilder().makeNull();
+ // For unique_ptr<A>, Ty will be 'A*'.
+ auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
State = State->remove<TrackedRegionMap>(ThisRegion);
State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion,
@@ -834,6 +839,8 @@ void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
const MemRegion *ThisRegion =
cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
+ QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
+
SVal InnerPointerVal;
if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
InnerPointerVal = *InnerValPtr;
@@ -872,7 +879,7 @@ void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
std::tie(NotNullState, NullState) =
State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>());
- auto NullVal = C.getSValBuilder().makeNull();
+ auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
// Explicitly tracking the region as null.
NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index b5c9356322fc..ea09c43cc5ce 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -11,9 +11,9 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Basic/SourceManager.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"
@@ -29,11 +29,11 @@ namespace {
class StackAddrEscapeChecker
: public Checker<check::PreCall, check::PreStmt<ReturnStmt>,
check::EndFunction> {
- mutable IdentifierInfo *dispatch_semaphore_tII;
- mutable std::unique_ptr<BuiltinBug> BT_stackleak;
- mutable std::unique_ptr<BuiltinBug> BT_returnstack;
- mutable std::unique_ptr<BuiltinBug> BT_capturedstackasync;
- mutable std::unique_ptr<BuiltinBug> BT_capturedstackret;
+ mutable IdentifierInfo *dispatch_semaphore_tII = nullptr;
+ mutable std::unique_ptr<BugType> BT_stackleak;
+ mutable std::unique_ptr<BugType> BT_returnstack;
+ mutable std::unique_ptr<BugType> BT_capturedstackasync;
+ mutable std::unique_ptr<BugType> BT_capturedstackret;
public:
enum CheckKind {
@@ -42,7 +42,7 @@ public:
CK_NumCheckKinds
};
- DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ bool ChecksEnabled[CK_NumCheckKinds] = {false};
CheckerNameRef CheckNames[CK_NumCheckKinds];
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
@@ -61,7 +61,6 @@ private:
ASTContext &Ctx);
static SmallVector<const MemRegion *, 4>
getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C);
- static bool isArcManagedBlock(const MemRegion *R, CheckerContext &C);
static bool isNotInCurrentFrame(const MemRegion *R, CheckerContext &C);
};
} // namespace
@@ -97,6 +96,14 @@ SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,
os << "stack memory associated with local variable '" << VR->getString()
<< '\'';
range = VR->getDecl()->getSourceRange();
+ } else if (const auto *LER = dyn_cast<CXXLifetimeExtendedObjectRegion>(R)) {
+ QualType Ty = LER->getValueType().getLocalUnqualifiedType();
+ os << "stack memory associated with temporary object of type '";
+ Ty.print(os, Ctx.getPrintingPolicy());
+ os << "' lifetime extended by local variable";
+ if (const IdentifierInfo *ID = LER->getExtendingDecl()->getIdentifier())
+ os << " '" << ID->getName() << '\'';
+ range = LER->getExpr()->getSourceRange();
} else if (const auto *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
QualType Ty = TOR->getValueType().getLocalUnqualifiedType();
os << "stack memory associated with temporary object of type '";
@@ -110,13 +117,6 @@ SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,
return range;
}
-bool StackAddrEscapeChecker::isArcManagedBlock(const MemRegion *R,
- CheckerContext &C) {
- assert(R && "MemRegion should not be null");
- return C.getASTContext().getLangOpts().ObjCAutoRefCount &&
- isa<BlockDataRegion>(R);
-}
-
bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R,
CheckerContext &C) {
const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace());
@@ -138,10 +138,8 @@ SmallVector<const MemRegion *, 4>
StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,
CheckerContext &C) {
SmallVector<const MemRegion *, 4> Regions;
- BlockDataRegion::referenced_vars_iterator I = B.referenced_vars_begin();
- BlockDataRegion::referenced_vars_iterator E = B.referenced_vars_end();
- for (; I != E; ++I) {
- SVal Val = C.getState()->getSVal(I.getCapturedRegion());
+ for (auto Var : B.referenced_vars()) {
+ SVal Val = C.getState()->getSVal(Var.getCapturedRegion());
const MemRegion *Region = Val.getAsRegion();
if (Region && isa<StackSpaceRegion>(Region->getMemorySpace()))
Regions.push_back(Region);
@@ -156,7 +154,7 @@ void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
if (!N)
return;
if (!BT_returnstack)
- BT_returnstack = std::make_unique<BuiltinBug>(
+ BT_returnstack = std::make_unique<BugType>(
CheckNames[CK_StackAddrEscapeChecker],
"Return of address to stack-allocated memory");
// Generate a report for this bug.
@@ -196,7 +194,7 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
if (!N)
continue;
if (!BT_capturedstackasync)
- BT_capturedstackasync = std::make_unique<BuiltinBug>(
+ BT_capturedstackasync = std::make_unique<BugType>(
CheckNames[CK_StackAddrAsyncEscapeChecker],
"Address of stack-allocated memory is captured");
SmallString<128> Buf;
@@ -214,13 +212,13 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
void StackAddrEscapeChecker::checkReturnedBlockCaptures(
const BlockDataRegion &B, CheckerContext &C) const {
for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
- if (isArcManagedBlock(Region, C) || isNotInCurrentFrame(Region, C))
+ if (isNotInCurrentFrame(Region, C))
continue;
ExplodedNode *N = C.generateNonFatalErrorNode();
if (!N)
continue;
if (!BT_capturedstackret)
- BT_capturedstackret = std::make_unique<BuiltinBug>(
+ BT_capturedstackret = std::make_unique<BugType>(
CheckNames[CK_StackAddrEscapeChecker],
"Address of stack-allocated memory is captured");
SmallString<128> Buf;
@@ -267,8 +265,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
checkReturnedBlockCaptures(*B, C);
- if (!isa<StackSpaceRegion>(R->getMemorySpace()) ||
- isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C))
+ if (!isa<StackSpaceRegion>(R->getMemorySpace()) || isNotInCurrentFrame(R, C))
return;
// Returning a record by value is fine. (In this case, the returned
@@ -303,21 +300,52 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
class CallBack : public StoreManager::BindingsHandler {
private:
CheckerContext &Ctx;
- const StackFrameContext *CurSFC;
+ const StackFrameContext *PoppedFrame;
+
+ /// Look for stack variables referring to popped stack variables.
+ /// Returns true only if it found some dangling stack variables
+ /// referred by an other stack variable from different stack frame.
+ bool checkForDanglingStackVariable(const MemRegion *Referrer,
+ const MemRegion *Referred) {
+ const auto *ReferrerMemSpace =
+ Referrer->getMemorySpace()->getAs<StackSpaceRegion>();
+ const auto *ReferredMemSpace =
+ Referred->getMemorySpace()->getAs<StackSpaceRegion>();
+
+ if (!ReferrerMemSpace || !ReferredMemSpace)
+ return false;
+
+ const auto *ReferrerFrame = ReferrerMemSpace->getStackFrame();
+ const auto *ReferredFrame = ReferredMemSpace->getStackFrame();
+
+ if (ReferrerMemSpace && ReferredMemSpace) {
+ if (ReferredFrame == PoppedFrame &&
+ ReferrerFrame->isParentOf(PoppedFrame)) {
+ V.emplace_back(Referrer, Referred);
+ return true;
+ }
+ }
+ return false;
+ }
public:
SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V;
- CallBack(CheckerContext &CC) : Ctx(CC), CurSFC(CC.getStackFrame()) {}
+ CallBack(CheckerContext &CC) : Ctx(CC), PoppedFrame(CC.getStackFrame()) {}
bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region,
SVal Val) override {
+ const MemRegion *VR = Val.getAsRegion();
+ if (!VR)
+ return true;
+ if (checkForDanglingStackVariable(Region, VR))
+ return true;
+
+ // Check the globals for the same.
if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace()))
return true;
- const MemRegion *VR = Val.getAsRegion();
- if (VR && isa<StackSpaceRegion>(VR->getMemorySpace()) &&
- !isArcManagedBlock(VR, Ctx) && !isNotInCurrentFrame(VR, Ctx))
+ if (VR && VR->hasStackStorage() && !isNotInCurrentFrame(VR, Ctx))
V.emplace_back(Region, VR);
return true;
}
@@ -336,27 +364,54 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
return;
if (!BT_stackleak)
- BT_stackleak = std::make_unique<BuiltinBug>(
- 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");
+ BT_stackleak =
+ std::make_unique<BugType>(CheckNames[CK_StackAddrEscapeChecker],
+ "Stack address stored into global variable");
for (const auto &P : Cb.V) {
+ const MemRegion *Referrer = P.first->getBaseRegion();
+ const MemRegion *Referred = P.second;
+
// Generate a report for this bug.
+ const StringRef CommonSuffix =
+ "upon returning to the caller. This will be a dangling reference";
SmallString<128> Buf;
llvm::raw_svector_ostream Out(Buf);
- SourceRange Range = genName(Out, P.second, Ctx.getASTContext());
- Out << " is still referred to by the ";
- if (isa<StaticGlobalSpaceRegion>(P.first->getMemorySpace()))
- Out << "static";
- else
- Out << "global";
- Out << " variable '";
- const VarRegion *VR = cast<VarRegion>(P.first->getBaseRegion());
- Out << *VR->getDecl()
- << "' upon returning to the caller. This will be a dangling reference";
+ const SourceRange Range = genName(Out, Referred, Ctx.getASTContext());
+
+ if (isa<CXXTempObjectRegion, CXXLifetimeExtendedObjectRegion>(Referrer)) {
+ Out << " is still referred to by a temporary object on the stack "
+ << CommonSuffix;
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
+ if (Range.isValid())
+ Report->addRange(Range);
+ Ctx.emitReport(std::move(Report));
+ return;
+ }
+
+ const StringRef ReferrerMemorySpace = [](const MemSpaceRegion *Space) {
+ if (isa<StaticGlobalSpaceRegion>(Space))
+ return "static";
+ if (isa<GlobalsSpaceRegion>(Space))
+ return "global";
+ assert(isa<StackSpaceRegion>(Space));
+ return "stack";
+ }(Referrer->getMemorySpace());
+
+ // We should really only have VarRegions here.
+ // Anything else is really surprising, and we should get notified if such
+ // ever happens.
+ const auto *ReferrerVar = dyn_cast<VarRegion>(Referrer);
+ if (!ReferrerVar) {
+ assert(false && "We should have a VarRegion here");
+ continue; // Defensively skip this one.
+ }
+ const std::string ReferrerVarName =
+ ReferrerVar->getDecl()->getDeclName().getAsString();
+
+ Out << " is still referred to by the " << ReferrerMemorySpace
+ << " variable '" << ReferrerVarName << "' " << CommonSuffix;
auto Report =
std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
if (Range.isValid())
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index e758b465af1b..fcd907a9bb0d 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -38,17 +38,9 @@
// Non-pure functions, for which only partial improvement over the default
// behavior is expected, are modeled via check::PostCall, non-intrusively.
//
-// The following standard C functions are currently supported:
-//
-// fgetc getline isdigit isupper toascii
-// fread isalnum isgraph isxdigit
-// fwrite isalpha islower read
-// getc isascii isprint write
-// getchar isblank ispunct toupper
-// getdelim iscntrl isspace tolower
-//
//===----------------------------------------------------------------------===//
+#include "ErrnoModeling.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
@@ -57,9 +49,12 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/FormatVariadic.h"
+#include <optional>
#include <string>
using namespace clang;
@@ -72,58 +67,165 @@ class StdLibraryFunctionsChecker
class Summary;
/// Specify how much the analyzer engine should entrust modeling this function
- /// to us. If he doesn't, he performs additional invalidations.
- enum InvalidationKind { NoEvalCall, EvalCallAsPure };
+ /// to us.
+ enum InvalidationKind {
+ /// No \c eval::Call for the function, it can be modeled elsewhere.
+ /// This checker checks only pre and post conditions.
+ NoEvalCall,
+ /// The function is modeled completely in this checker.
+ EvalCallAsPure
+ };
+
+ /// Given a range, should the argument stay inside or outside this range?
+ enum RangeKind { OutOfRange, WithinRange };
- // The universal integral type to use in value range descriptions.
- // Unsigned to make sure overflows are well-defined.
+ static RangeKind negateKind(RangeKind K) {
+ switch (K) {
+ case OutOfRange:
+ return WithinRange;
+ case WithinRange:
+ return OutOfRange;
+ }
+ llvm_unreachable("Unknown range kind");
+ }
+
+ /// The universal integral type to use in value range descriptions.
+ /// Unsigned to make sure overflows are well-defined.
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.
+ /// Describes a single range constraint. Eg. {{0, 1}, {3, 4}} is
+ /// a non-negative integer, which less than 5 and not equal to 2.
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 ArgNo;
+ /// Special argument number for specifying the return value.
static const ArgNo Ret;
- /// Returns the string representation of an argument index.
+ /// Get a string representation of an argument index.
/// E.g.: (1) -> '1st arg', (2) - > '2nd arg'
- static SmallString<8> getArgDesc(ArgNo);
+ static void printArgDesc(ArgNo, llvm::raw_ostream &Out);
+ /// Print value X of the argument in form " (which is X)",
+ /// if the value is a fixed known value, otherwise print nothing.
+ /// This is used as simple explanation of values if possible.
+ static void printArgValueInfo(ArgNo ArgN, ProgramStateRef State,
+ const CallEvent &Call, llvm::raw_ostream &Out);
+ /// Append textual description of a numeric range [RMin,RMax] to
+ /// \p Out.
+ static void appendInsideRangeDesc(llvm::APSInt RMin, llvm::APSInt RMax,
+ QualType ArgT, BasicValueFactory &BVF,
+ llvm::raw_ostream &Out);
+ /// Append textual description of a numeric range out of [RMin,RMax] to
+ /// \p Out.
+ static void appendOutOfRangeDesc(llvm::APSInt RMin, llvm::APSInt RMax,
+ QualType ArgT, BasicValueFactory &BVF,
+ llvm::raw_ostream &Out);
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.
+ /// Pointer to the ValueConstraint. We need a copyable, polymorphic and
+ /// default initializable 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.
+ /// Mind that a pointer to a new value constraint is created when the negate
+ /// function is used.
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.
+ /// These are used as argument constraints (preconditions) of functions, in
+ /// which case a bug report may be emitted if the constraint is not satisfied.
+ /// Another use is as conditions for summary cases, to create different
+ /// classes of behavior for a function. In this case no description of the
+ /// constraint is needed because the summary cases have an own (not generated)
+ /// description string.
class ValueConstraint {
public:
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;
+
+ /// Represents that in which context do we require a description of the
+ /// constraint.
+ enum DescriptionKind {
+ /// Describe a constraint that was violated.
+ /// Description should start with something like "should be".
+ Violation,
+ /// Describe a constraint that was assumed to be true.
+ /// This can be used when a precondition is satisfied, or when a summary
+ /// case is applied.
+ /// Description should start with something like "is".
+ Assumption
+ };
+
+ /// Give a description that explains the constraint to the user. Used when
+ /// a bug is reported or when the constraint is applied and displayed as a
+ /// note. The description should not mention the argument (getArgNo).
+ /// See StdLibraryFunctionsChecker::reportBug about how this function is
+ /// used (this function is used not only there).
+ virtual void describe(DescriptionKind DK, const CallEvent &Call,
+ ProgramStateRef State, const Summary &Summary,
+ llvm::raw_ostream &Out) const {
+ // There are some descendant classes that are not used as argument
+ // constraints, e.g. ComparisonConstraint. In that case we can safely
+ // ignore the implementation of this function.
+ llvm_unreachable(
+ "Description not implemented for summary case constraints");
+ }
+
+ /// Give a description that explains the actual argument value (where the
+ /// current ValueConstraint applies to) to the user. This function should be
+ /// called only when the current constraint is satisfied by the argument.
+ /// It should produce a more precise description than the constraint itself.
+ /// The actual value of the argument and the program state can be used to
+ /// make the description more precise. In the most simple case, if the
+ /// argument has a fixed known value this value can be printed into \p Out,
+ /// this is done by default.
+ /// The function should return true if a description was printed to \p Out,
+ /// otherwise false.
+ /// See StdLibraryFunctionsChecker::reportBug about how this function is
+ /// used.
+ virtual bool describeArgumentValue(const CallEvent &Call,
+ ProgramStateRef State,
+ const Summary &Summary,
+ llvm::raw_ostream &Out) const {
+ if (auto N = getArgSVal(Call, getArgNo()).getAs<NonLoc>()) {
+ if (const llvm::APSInt *Int = N->getAsInteger()) {
+ Out << *Int;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// Return those arguments that should be tracked when we report a bug about
+ /// argument constraint violation. By default it is the argument that is
+ /// constrained, however, in some special cases we need to track other
+ /// arguments as well. E.g. a buffer size might be encoded in another
+ /// argument.
+ /// The "return value" argument number can not occur as returned value.
+ virtual std::vector<ArgNo> getArgsToTrack() const { return {ArgN}; }
+
+ /// Get a constraint that represents exactly the opposite of the current.
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).
+ /// 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).
+ /// This condition can indicate if a probably wrong or unexpected function
+ /// was found where the constraint is to be applied.
bool checkValidity(const FunctionDecl *FD) const {
const bool ValidArg = ArgN == Ret || ArgN < FD->getNumParams();
assert(ValidArg && "Arg out of range!");
@@ -132,95 +234,75 @@ class StdLibraryFunctionsChecker
// Subclasses may further refine the validation.
return checkSpecificValidity(FD);
}
- ArgNo getArgNo() const { return ArgN; }
- // Return those arguments that should be tracked when we report a bug. By
- // default it is the argument that is constrained, however, in some special
- // cases we need to track other arguments as well. E.g. a buffer size might
- // be encoded in another argument.
- virtual std::vector<ArgNo> getArgsToTrack() const { return {ArgN}; }
-
- virtual StringRef getName() const = 0;
-
- // Give a description that explains the constraint to the user. Used when
- // the bug is reported.
- virtual std::string describe(ProgramStateRef State,
- const Summary &Summary) const {
- // There are some descendant classes that are not used as argument
- // constraints, e.g. ComparisonConstraint. In that case we can safely
- // ignore the implementation of this function.
- llvm_unreachable("Not implemented");
- }
+ /// Return the argument number (may be placeholder for "return value").
+ ArgNo getArgNo() const { return ArgN; }
protected:
- ArgNo ArgN; // Argument to which we apply the constraint.
-
- /// Do polymorphic sanity check on the constraint.
+ /// Argument to which to apply the constraint. It can be a real argument of
+ /// the function to check, or a special value to indicate the return value
+ /// of the function.
+ /// Every constraint is assigned to one main argument, even if other
+ /// arguments are involved.
+ ArgNo ArgN;
+
+ /// Do constraint-specific validation check.
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 range on a single symbol.
+ /// Check if a single argument falls into a specific "range".
+ /// A range is formed as a set of intervals.
+ /// E.g. \code {['A', 'Z'], ['a', 'z'], ['_', '_']} \endcode
+ /// The intervals are closed intervals that contain one or more values.
+ ///
+ /// The default constructed RangeConstraint has an empty range, applying
+ /// such constraint does not involve any assumptions, thus the State remains
+ /// unchanged. This is meaningful, if the range is dependent on a looked up
+ /// type (e.g. [0, Socklen_tMax]). If the type is not found, then the range
+ /// is default initialized to be empty.
class RangeConstraint : public ValueConstraint {
+ /// The constraint can be specified by allowing or disallowing the range.
+ /// WithinRange indicates allowing the range, OutOfRange indicates
+ /// disallowing it (allowing the complementary range).
RangeKind Kind;
- // A range is formed as a set of intervals (sub-ranges).
- // E.g. {['A', 'Z'], ['a', 'z']}
- //
- // The default constructed RangeConstraint has an empty range set, applying
- // such constraint does not involve any assumptions, thus the State remains
- // unchanged. This is meaningful, if the range is dependent on a looked up
- // type (e.g. [0, Socklen_tMax]). If the type is not found, then the range
- // is default initialized to be empty.
+
+ /// A set of intervals.
IntRangeVector Ranges;
- public:
- StringRef getName() const override { return "Range"; }
- RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges)
- : ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges) {}
+ /// A textual description of this constraint for the specific case where the
+ /// constraint is used. If empty a generated description will be used that
+ /// is built from the range of the constraint.
+ StringRef Description;
- std::string describe(ProgramStateRef State,
- const Summary &Summary) const override;
+ public:
+ RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges,
+ StringRef Desc = "")
+ : ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges), Description(Desc) {
+ }
const IntRangeVector &getRanges() const { return Ranges; }
- private:
- 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 Summary &Summary,
- CheckerContext &C) const override {
- switch (Kind) {
- case OutOfRange:
- return applyAsOutOfRange(State, Call, Summary);
- case WithinRange:
- return applyAsWithinRange(State, Call, Summary);
- }
- llvm_unreachable("Unknown range kind!");
- }
+ CheckerContext &C) const override;
+
+ void describe(DescriptionKind DK, const CallEvent &Call,
+ ProgramStateRef State, const Summary &Summary,
+ llvm::raw_ostream &Out) const override;
+
+ bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State,
+ const Summary &Summary,
+ llvm::raw_ostream &Out) const override;
ValueConstraintPtr negate() const override {
RangeConstraint Tmp(*this);
- switch (Kind) {
- case OutOfRange:
- Tmp.Kind = WithinRange;
- break;
- case WithinRange:
- Tmp.Kind = OutOfRange;
- break;
- }
+ Tmp.Kind = negateKind(Kind);
return std::make_shared<RangeConstraint>(Tmp);
}
+ protected:
bool checkSpecificValidity(const FunctionDecl *FD) const override {
const bool ValidArg =
getArgType(FD, ArgN)->isIntegralType(FD->getASTContext());
@@ -228,14 +310,52 @@ class StdLibraryFunctionsChecker
"This constraint should be applied on an integral type");
return ValidArg;
}
+
+ private:
+ /// A callback function that is used when iterating over the range
+ /// intervals. It gets the begin and end (inclusive) of one interval.
+ /// This is used to make any kind of task possible that needs an iteration
+ /// over the intervals.
+ using RangeApplyFunction =
+ std::function<bool(const llvm::APSInt &Min, const llvm::APSInt &Max)>;
+
+ /// Call a function on the intervals of the range.
+ /// The function is called with all intervals in the range.
+ void applyOnWithinRange(BasicValueFactory &BVF, QualType ArgT,
+ const RangeApplyFunction &F) const;
+ /// Call a function on all intervals in the complementary range.
+ /// The function is called with all intervals that fall out of the range.
+ /// E.g. consider an interval list [A, B] and [C, D]
+ /// \code
+ /// -------+--------+------------------+------------+----------->
+ /// A B C D
+ /// \endcode
+ /// We get the ranges [-inf, A - 1], [D + 1, +inf], [B + 1, C - 1].
+ /// The \p ArgT is used to determine the min and max of the type that is
+ /// used as "-inf" and "+inf".
+ void applyOnOutOfRange(BasicValueFactory &BVF, QualType ArgT,
+ const RangeApplyFunction &F) const;
+ /// Call a function on the intervals of the range or the complementary
+ /// range.
+ void applyOnRange(RangeKind Kind, BasicValueFactory &BVF, QualType ArgT,
+ const RangeApplyFunction &F) const {
+ switch (Kind) {
+ case OutOfRange:
+ applyOnOutOfRange(BVF, ArgT, F);
+ break;
+ case WithinRange:
+ applyOnWithinRange(BVF, ArgT, F);
+ break;
+ };
+ }
};
+ /// Check relation of an argument to another.
class ComparisonConstraint : public ValueConstraint {
BinaryOperator::Opcode Opcode;
ArgNo OtherArgN;
public:
- virtual StringRef getName() const override { return "Comparison"; };
ComparisonConstraint(ArgNo ArgN, BinaryOperator::Opcode Opcode,
ArgNo OtherArgN)
: ValueConstraint(ArgN), Opcode(Opcode), OtherArgN(OtherArgN) {}
@@ -246,28 +366,27 @@ class StdLibraryFunctionsChecker
CheckerContext &C) const override;
};
+ /// Check null or non-null-ness of an argument that is of pointer type.
class NotNullConstraint : public ValueConstraint {
using ValueConstraint::ValueConstraint;
// This variable has a role when we negate the constraint.
bool CannotBeNull = true;
public:
- std::string describe(ProgramStateRef State,
- const Summary &Summary) const override;
- StringRef getName() const override { return "NonNull"; }
+ NotNullConstraint(ArgNo ArgN, bool CannotBeNull = true)
+ : ValueConstraint(ArgN), CannotBeNull(CannotBeNull) {}
+
ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
const Summary &Summary,
- CheckerContext &C) const override {
- SVal V = getArgSVal(Call, getArgNo());
- if (V.isUndef())
- return State;
+ CheckerContext &C) const override;
- DefinedOrUnknownSVal L = V.castAs<DefinedOrUnknownSVal>();
- if (!L.getAs<Loc>())
- return State;
+ void describe(DescriptionKind DK, const CallEvent &Call,
+ ProgramStateRef State, const Summary &Summary,
+ llvm::raw_ostream &Out) const override;
- return State->assume(L, CannotBeNull);
- }
+ bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State,
+ const Summary &Summary,
+ llvm::raw_ostream &Out) const override;
ValueConstraintPtr negate() const override {
NotNullConstraint Tmp(*this);
@@ -275,6 +394,54 @@ class StdLibraryFunctionsChecker
return std::make_shared<NotNullConstraint>(Tmp);
}
+ protected:
+ 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;
+ }
+ };
+
+ /// Check null or non-null-ness of an argument that is of pointer type.
+ /// The argument is meant to be a buffer that has a size constraint, and it
+ /// is allowed to have a NULL value if the size is 0. The size can depend on
+ /// 1 or 2 additional arguments, if one of these is 0 the buffer is allowed to
+ /// be NULL. This is useful for functions like `fread` which have this special
+ /// property.
+ class NotNullBufferConstraint : public ValueConstraint {
+ using ValueConstraint::ValueConstraint;
+ ArgNo SizeArg1N;
+ std::optional<ArgNo> SizeArg2N;
+ // This variable has a role when we negate the constraint.
+ bool CannotBeNull = true;
+
+ public:
+ NotNullBufferConstraint(ArgNo ArgN, ArgNo SizeArg1N,
+ std::optional<ArgNo> SizeArg2N,
+ bool CannotBeNull = true)
+ : ValueConstraint(ArgN), SizeArg1N(SizeArg1N), SizeArg2N(SizeArg2N),
+ CannotBeNull(CannotBeNull) {}
+
+ ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
+ const Summary &Summary,
+ CheckerContext &C) const override;
+
+ void describe(DescriptionKind DK, const CallEvent &Call,
+ ProgramStateRef State, const Summary &Summary,
+ llvm::raw_ostream &Out) const override;
+
+ bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State,
+ const Summary &Summary,
+ llvm::raw_ostream &Out) const override;
+
+ ValueConstraintPtr negate() const override {
+ NotNullBufferConstraint Tmp(*this);
+ Tmp.CannotBeNull = !this->CannotBeNull;
+ return std::make_shared<NotNullBufferConstraint>(Tmp);
+ }
+
+ protected:
bool checkSpecificValidity(const FunctionDecl *FD) const override {
const bool ValidArg = getArgType(FD, ArgN)->isPointerType();
assert(ValidArg &&
@@ -295,18 +462,17 @@ class StdLibraryFunctionsChecker
// // Here, ptr is the buffer, and its minimum size is `size * nmemb`.
class BufferSizeConstraint : public ValueConstraint {
// The concrete value which is the minimum size for the buffer.
- llvm::Optional<llvm::APSInt> ConcreteSize;
+ std::optional<llvm::APSInt> ConcreteSize;
// The argument which holds the size of the buffer.
- llvm::Optional<ArgNo> SizeArgN;
+ std::optional<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;
+ std::optional<ArgNo> SizeMultiplierArgN;
// The operator we use in apply. This is negated in negate().
BinaryOperator::Opcode Op = BO_LE;
public:
- StringRef getName() const override { return "BufferSize"; }
BufferSizeConstraint(ArgNo Buffer, llvm::APSInt BufMinSize)
: ValueConstraint(Buffer), ConcreteSize(BufMinSize) {}
BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize)
@@ -315,6 +481,18 @@ class StdLibraryFunctionsChecker
: ValueConstraint(Buffer), SizeArgN(BufSize),
SizeMultiplierArgN(BufSizeMultiplier) {}
+ ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
+ const Summary &Summary,
+ CheckerContext &C) const override;
+
+ void describe(DescriptionKind DK, const CallEvent &Call,
+ ProgramStateRef State, const Summary &Summary,
+ llvm::raw_ostream &Out) const override;
+
+ bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State,
+ const Summary &Summary,
+ llvm::raw_ostream &Out) const override;
+
std::vector<ArgNo> getArgsToTrack() const override {
std::vector<ArgNo> Result{ArgN};
if (SizeArgN)
@@ -324,57 +502,13 @@ class StdLibraryFunctionsChecker
return Result;
}
- std::string describe(ProgramStateRef State,
- const Summary &Summary) const override;
-
- 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());
-
- // Get the size constraint.
- const SVal SizeV = [this, &State, &Call, &Summary, &SvalBuilder]() {
- if (ConcreteSize) {
- return SVal(SvalBuilder.makeIntVal(*ConcreteSize));
- }
- assert(SizeArgN && "The constraint must be either a concrete value or "
- "encoded in an argument.");
- // 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));
- }
- return SizeV;
- }();
-
- // The dynamic size of the buffer argument, got from the analyzer engine.
- SVal BufDynSize = getDynamicExtentWithOffset(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);
}
+ protected:
bool checkSpecificValidity(const FunctionDecl *FD) const override {
const bool ValidArg = getArgType(FD, ArgN)->isPointerType();
assert(ValidArg &&
@@ -384,10 +518,162 @@ class StdLibraryFunctionsChecker
};
/// The complete list of constraints that defines a single branch.
- typedef std::vector<ValueConstraintPtr> ConstraintSet;
+ using ConstraintSet = std::vector<ValueConstraintPtr>;
+
+ /// Define how a function affects the system variable 'errno'.
+ /// This works together with the \c ErrnoModeling and \c ErrnoChecker classes.
+ /// Currently 3 use cases exist: success, failure, irrelevant.
+ /// In the future the failure case can be customized to set \c errno to a
+ /// more specific constraint (for example > 0), or new case can be added
+ /// for functions which require check of \c errno in both success and failure
+ /// case.
+ class ErrnoConstraintBase {
+ public:
+ /// Apply specific state changes related to the errno variable.
+ virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
+ const Summary &Summary,
+ CheckerContext &C) const = 0;
+ /// Get a description about what happens with 'errno' here and how it causes
+ /// a later bug report created by ErrnoChecker.
+ /// Empty return value means that 'errno' related bug may not happen from
+ /// the current analyzed function.
+ virtual const std::string describe(CheckerContext &C) const { return ""; }
+
+ virtual ~ErrnoConstraintBase() {}
+
+ protected:
+ ErrnoConstraintBase() = default;
+
+ /// This is used for conjure symbol for errno to differentiate from the
+ /// original call expression (same expression is used for the errno symbol).
+ static int Tag;
+ };
+
+ /// Reset errno constraints to irrelevant.
+ /// This is applicable to functions that may change 'errno' and are not
+ /// modeled elsewhere.
+ class ResetErrnoConstraint : public ErrnoConstraintBase {
+ public:
+ ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
+ const Summary &Summary,
+ CheckerContext &C) const override {
+ return errno_modeling::setErrnoState(State, errno_modeling::Irrelevant);
+ }
+ };
+
+ /// Do not change errno constraints.
+ /// This is applicable to functions that are modeled in another checker
+ /// and the already set errno constraints should not be changed in the
+ /// post-call event.
+ class NoErrnoConstraint : public ErrnoConstraintBase {
+ public:
+ ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
+ const Summary &Summary,
+ CheckerContext &C) const override {
+ return State;
+ }
+ };
+
+ /// Set errno constraint at failure cases of standard functions.
+ /// Failure case: 'errno' becomes not equal to 0 and may or may not be checked
+ /// by the program. \c ErrnoChecker does not emit a bug report after such a
+ /// function call.
+ class FailureErrnoConstraint : public ErrnoConstraintBase {
+ public:
+ ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
+ const Summary &Summary,
+ CheckerContext &C) const override {
+ SValBuilder &SVB = C.getSValBuilder();
+ NonLoc ErrnoSVal =
+ SVB.conjureSymbolVal(&Tag, Call.getOriginExpr(),
+ C.getLocationContext(), C.getASTContext().IntTy,
+ C.blockCount())
+ .castAs<NonLoc>();
+ return errno_modeling::setErrnoForStdFailure(State, C, ErrnoSVal);
+ }
+ };
+
+ /// Set errno constraint at success cases of standard functions.
+ /// Success case: 'errno' is not allowed to be used because the value is
+ /// undefined after successful call.
+ /// \c ErrnoChecker can emit bug report after such a function call if errno
+ /// is used.
+ class SuccessErrnoConstraint : public ErrnoConstraintBase {
+ public:
+ ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
+ const Summary &Summary,
+ CheckerContext &C) const override {
+ return errno_modeling::setErrnoForStdSuccess(State, C);
+ }
+
+ const std::string describe(CheckerContext &C) const override {
+ return "'errno' becomes undefined after the call";
+ }
+ };
+
+ /// Set errno constraint at functions that indicate failure only with 'errno'.
+ /// In this case 'errno' is required to be observed.
+ /// \c ErrnoChecker can emit bug report after such a function call if errno
+ /// is overwritten without a read before.
+ class ErrnoMustBeCheckedConstraint : public ErrnoConstraintBase {
+ public:
+ ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
+ const Summary &Summary,
+ CheckerContext &C) const override {
+ return errno_modeling::setErrnoStdMustBeChecked(State, C,
+ Call.getOriginExpr());
+ }
+
+ const std::string describe(CheckerContext &C) const override {
+ return "reading 'errno' is required to find out if the call has failed";
+ }
+ };
+
+ /// A single branch of a function summary.
+ ///
+ /// A branch is defined by a series of constraints - "assumptions" -
+ /// that together form a single possible outcome of invoking the function.
+ /// When static analyzer considers a branch, it tries to introduce
+ /// a child node in the Exploded Graph. The child node has to include
+ /// constraints that define the branch. If the constraints contradict
+ /// existing constraints in the state, the node is not created and the branch
+ /// is dropped; otherwise it's queued for future exploration.
+ /// The branch is accompanied by a note text that may be displayed
+ /// to the user when a bug is found on a path that takes this branch.
+ ///
+ /// 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])
+ /// and the note may say "Assuming the character is alphabetical"
+ /// Branch 2)
+ /// x is out-of-range ['A', 'Z'] and out-of-range ['a', 'z']
+ /// then the return value is 0
+ /// and the note may say "Assuming the character is non-alphabetical".
+ class SummaryCase {
+ ConstraintSet Constraints;
+ const ErrnoConstraintBase &ErrnoConstraint;
+ StringRef Note;
+
+ public:
+ SummaryCase(ConstraintSet &&Constraints, const ErrnoConstraintBase &ErrnoC,
+ StringRef Note)
+ : Constraints(std::move(Constraints)), ErrnoConstraint(ErrnoC),
+ Note(Note) {}
+
+ SummaryCase(const ConstraintSet &Constraints,
+ const ErrnoConstraintBase &ErrnoC, StringRef Note)
+ : Constraints(Constraints), ErrnoConstraint(ErrnoC), Note(Note) {}
+
+ const ConstraintSet &getConstraints() const { return Constraints; }
+ const ErrnoConstraintBase &getErrnoConstraint() const {
+ return ErrnoConstraint;
+ }
+ StringRef getNote() const { return Note; }
+ };
- using ArgTypes = std::vector<Optional<QualType>>;
- using RetType = Optional<QualType>;
+ using ArgTypes = std::vector<std::optional<QualType>>;
+ using RetType = std::optional<QualType>;
// A placeholder type, we use it whenever we do not care about the concrete
// type in a Signature.
@@ -409,7 +695,7 @@ class StdLibraryFunctionsChecker
// Construct a signature from optional types. If any of the optional types
// are not set then the signature will be invalid.
Signature(ArgTypes ArgTys, RetType RetTy) {
- for (Optional<QualType> Arg : ArgTys) {
+ for (std::optional<QualType> Arg : ArgTys) {
if (!Arg) {
Invalid = true;
return;
@@ -451,23 +737,12 @@ class StdLibraryFunctionsChecker
return T;
}
- using Cases = std::vector<ConstraintSet>;
+ using SummaryCases = std::vector<SummaryCase>;
/// 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 branches - so, a list of list of ranges,
/// * 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.
@@ -482,7 +757,7 @@ class StdLibraryFunctionsChecker
/// signature is matched.
class Summary {
const InvalidationKind InvalidationKd;
- Cases CaseConstraints;
+ SummaryCases Cases;
ConstraintSet ArgConstraints;
// The function to which the summary applies. This is set after lookup and
@@ -492,12 +767,14 @@ class StdLibraryFunctionsChecker
public:
Summary(InvalidationKind InvalidationKd) : InvalidationKd(InvalidationKd) {}
- Summary &Case(ConstraintSet &&CS) {
- CaseConstraints.push_back(std::move(CS));
+ Summary &Case(ConstraintSet &&CS, const ErrnoConstraintBase &ErrnoC,
+ StringRef Note = "") {
+ Cases.push_back(SummaryCase(std::move(CS), ErrnoC, Note));
return *this;
}
- Summary &Case(const ConstraintSet &CS) {
- CaseConstraints.push_back(CS);
+ Summary &Case(const ConstraintSet &CS, const ErrnoConstraintBase &ErrnoC,
+ StringRef Note = "") {
+ Cases.push_back(SummaryCase(CS, ErrnoC, Note));
return *this;
}
Summary &ArgConstraint(ValueConstraintPtr VC) {
@@ -508,7 +785,7 @@ class StdLibraryFunctionsChecker
}
InvalidationKind getInvalidationKd() const { return InvalidationKd; }
- const Cases &getCaseConstraints() const { return CaseConstraints; }
+ const SummaryCases &getCases() const { return Cases; }
const ConstraintSet &getArgConstraints() const { return ArgConstraints; }
QualType getArgType(ArgNo ArgN) const {
@@ -527,11 +804,11 @@ class StdLibraryFunctionsChecker
}
private:
- // Once we know the exact type of the function then do sanity check on all
- // the given constraints.
+ // Once we know the exact type of the function then do validation check on
+ // all the given constraints.
bool validateByConstraints(const FunctionDecl *FD) const {
- for (const ConstraintSet &Case : CaseConstraints)
- for (const ValueConstraintPtr &Constraint : Case)
+ for (const SummaryCase &Case : Cases)
+ for (const ValueConstraintPtr &Constraint : Case.getConstraints())
if (!Constraint->checkValidity(FD))
return false;
for (const ValueConstraintPtr &Constraint : ArgConstraints)
@@ -546,236 +823,310 @@ class StdLibraryFunctionsChecker
using FunctionSummaryMapType = llvm::DenseMap<const FunctionDecl *, Summary>;
mutable FunctionSummaryMapType FunctionSummaryMap;
- mutable std::unique_ptr<BugType> BT_InvalidArg;
+ const BugType BT_InvalidArg{this, "Function call with invalid argument"};
mutable bool SummariesInitialized = false;
static SVal getArgSVal(const CallEvent &Call, ArgNo ArgN) {
return ArgN == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgN);
}
+ static std::string getFunctionName(const CallEvent &Call) {
+ assert(Call.getDecl() &&
+ "Call was found by a summary, should have declaration");
+ return cast<NamedDecl>(Call.getDecl())->getNameAsString();
+ }
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;
- enum CheckKind {
- CK_StdCLibraryFunctionArgsChecker,
- CK_StdCLibraryFunctionsTesterChecker,
- CK_NumCheckKinds
- };
- DefaultBool ChecksEnabled[CK_NumCheckKinds];
- CheckerNameRef CheckNames[CK_NumCheckKinds];
+ CheckerNameRef CheckName;
+ bool AddTestFunctions = false;
bool DisplayLoadedSummaries = false;
bool ModelPOSIX = false;
+ bool ShouldAssumeControlledEnvironment = false;
private:
- Optional<Summary> findFunctionSummary(const FunctionDecl *FD,
- CheckerContext &C) const;
- Optional<Summary> findFunctionSummary(const CallEvent &Call,
- CheckerContext &C) const;
+ std::optional<Summary> findFunctionSummary(const FunctionDecl *FD,
+ CheckerContext &C) const;
+ std::optional<Summary> findFunctionSummary(const CallEvent &Call,
+ CheckerContext &C) const;
void initFunctionSummaries(CheckerContext &C) const;
void reportBug(const CallEvent &Call, ExplodedNode *N,
- const ValueConstraint *VC, const Summary &Summary,
- CheckerContext &C) const {
- if (!ChecksEnabled[CK_StdCLibraryFunctionArgsChecker])
- return;
- std::string Msg =
- (Twine("Function argument constraint is not satisfied, constraint: ") +
- VC->getName().data())
- .str();
- 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);
-
- for (ArgNo ArgN : VC->getArgsToTrack())
+ const ValueConstraint *VC, const ValueConstraint *NegatedVC,
+ const Summary &Summary, CheckerContext &C) const {
+ assert(Call.getDecl() &&
+ "Function found in summary must have a declaration available");
+ SmallString<256> Msg;
+ llvm::raw_svector_ostream MsgOs(Msg);
+
+ MsgOs << "The ";
+ printArgDesc(VC->getArgNo(), MsgOs);
+ MsgOs << " to '" << getFunctionName(Call) << "' ";
+ bool ValuesPrinted =
+ NegatedVC->describeArgumentValue(Call, N->getState(), Summary, MsgOs);
+ if (ValuesPrinted)
+ MsgOs << " but ";
+ else
+ MsgOs << "is out of the accepted range; It ";
+ VC->describe(ValueConstraint::Violation, Call, C.getState(), Summary,
+ MsgOs);
+ Msg[0] = toupper(Msg[0]);
+ auto R = std::make_unique<PathSensitiveBugReport>(BT_InvalidArg, Msg, N);
+
+ for (ArgNo ArgN : VC->getArgsToTrack()) {
bugreporter::trackExpressionValue(N, Call.getArgExpr(ArgN), *R);
-
- // Highlight the range of the argument that was violated.
- R->addRange(Call.getArgSourceRange(VC->getArgNo()));
-
- // Describe the argument constraint in a note.
- R->addNote(VC->describe(C.getState(), Summary), R->getLocation(),
- Call.getArgSourceRange(VC->getArgNo()));
+ R->markInteresting(Call.getArgSVal(ArgN));
+ // All tracked arguments are important, highlight them.
+ R->addRange(Call.getArgSourceRange(ArgN));
+ }
C.emitReport(std::move(R));
}
+
+ /// These are the errno constraints that can be passed to summary cases.
+ /// One of these should fit for a single summary case.
+ /// Usually if a failure return value exists for function, that function
+ /// needs different cases for success and failure with different errno
+ /// constraints (and different return value constraints).
+ const NoErrnoConstraint ErrnoUnchanged{};
+ const ResetErrnoConstraint ErrnoIrrelevant{};
+ const ErrnoMustBeCheckedConstraint ErrnoMustBeChecked{};
+ const SuccessErrnoConstraint ErrnoMustNotBeChecked{};
+ const FailureErrnoConstraint ErrnoNEZeroIrrelevant{};
};
+int StdLibraryFunctionsChecker::ErrnoConstraintBase::Tag = 0;
+
const StdLibraryFunctionsChecker::ArgNo StdLibraryFunctionsChecker::Ret =
std::numeric_limits<ArgNo>::max();
-} // end of anonymous namespace
-
static BasicValueFactory &getBVF(ProgramStateRef State) {
ProgramStateManager &Mgr = State->getStateManager();
SValBuilder &SVB = Mgr.getSValBuilder();
return SVB.getBasicValueFactory();
}
-std::string StdLibraryFunctionsChecker::NotNullConstraint::describe(
- ProgramStateRef State, const Summary &Summary) const {
- SmallString<48> Result;
- Result += "The ";
- Result += getArgDesc(ArgN);
- Result += " should not be NULL";
- return Result.c_str();
-}
+} // end of anonymous namespace
-std::string StdLibraryFunctionsChecker::RangeConstraint::describe(
- ProgramStateRef State, const Summary &Summary) const {
+void StdLibraryFunctionsChecker::printArgDesc(
+ StdLibraryFunctionsChecker::ArgNo ArgN, llvm::raw_ostream &Out) {
+ Out << std::to_string(ArgN + 1);
+ Out << llvm::getOrdinalSuffix(ArgN + 1);
+ Out << " argument";
+}
- BasicValueFactory &BVF = getBVF(State);
+void StdLibraryFunctionsChecker::printArgValueInfo(ArgNo ArgN,
+ ProgramStateRef State,
+ const CallEvent &Call,
+ llvm::raw_ostream &Out) {
+ if (const llvm::APSInt *Val =
+ State->getStateManager().getSValBuilder().getKnownValue(
+ State, getArgSVal(Call, ArgN)))
+ Out << " (which is " << *Val << ")";
+}
- QualType T = Summary.getArgType(getArgNo());
- SmallString<48> Result;
- Result += "The ";
- Result += getArgDesc(ArgN);
- Result += " should be ";
-
- // Range kind as a string.
- Kind == OutOfRange ? Result += "out of" : Result += "within";
-
- // Get the range values as a string.
- Result += " the range ";
- if (Ranges.size() > 1)
- Result += "[";
- unsigned I = Ranges.size();
- for (const std::pair<RangeInt, RangeInt> &R : Ranges) {
- Result += "[";
- const llvm::APSInt &Min = BVF.getValue(R.first, T);
- const llvm::APSInt &Max = BVF.getValue(R.second, T);
- Min.toString(Result);
- Result += ", ";
- Max.toString(Result);
- Result += "]";
- if (--I > 0)
- Result += ", ";
+void StdLibraryFunctionsChecker::appendInsideRangeDesc(llvm::APSInt RMin,
+ llvm::APSInt RMax,
+ QualType ArgT,
+ BasicValueFactory &BVF,
+ llvm::raw_ostream &Out) {
+ if (RMin.isZero() && RMax.isZero())
+ Out << "zero";
+ else if (RMin == RMax)
+ Out << RMin;
+ else if (RMin == BVF.getMinValue(ArgT)) {
+ if (RMax == -1)
+ Out << "< 0";
+ else
+ Out << "<= " << RMax;
+ } else if (RMax == BVF.getMaxValue(ArgT)) {
+ if (RMin.isOne())
+ Out << "> 0";
+ else
+ Out << ">= " << RMin;
+ } else if (RMin.isNegative() == RMax.isNegative() &&
+ RMin.getLimitedValue() == RMax.getLimitedValue() - 1) {
+ Out << RMin << " or " << RMax;
+ } else {
+ Out << "between " << RMin << " and " << RMax;
}
- if (Ranges.size() > 1)
- Result += "]";
-
- return Result.c_str();
}
-SmallString<8>
-StdLibraryFunctionsChecker::getArgDesc(StdLibraryFunctionsChecker::ArgNo ArgN) {
- SmallString<8> Result;
- Result += std::to_string(ArgN + 1);
- Result += llvm::getOrdinalSuffix(ArgN + 1);
- Result += " arg";
- return Result;
+void StdLibraryFunctionsChecker::appendOutOfRangeDesc(llvm::APSInt RMin,
+ llvm::APSInt RMax,
+ QualType ArgT,
+ BasicValueFactory &BVF,
+ llvm::raw_ostream &Out) {
+ if (RMin.isZero() && RMax.isZero())
+ Out << "nonzero";
+ else if (RMin == RMax) {
+ Out << "not equal to " << RMin;
+ } else if (RMin == BVF.getMinValue(ArgT)) {
+ if (RMax == -1)
+ Out << ">= 0";
+ else
+ Out << "> " << RMax;
+ } else if (RMax == BVF.getMaxValue(ArgT)) {
+ if (RMin.isOne())
+ Out << "<= 0";
+ else
+ Out << "< " << RMin;
+ } else if (RMin.isNegative() == RMax.isNegative() &&
+ RMin.getLimitedValue() == RMax.getLimitedValue() - 1) {
+ Out << "not " << RMin << " and not " << RMax;
+ } else {
+ Out << "not between " << RMin << " and " << RMax;
+ }
}
-std::string StdLibraryFunctionsChecker::BufferSizeConstraint::describe(
- ProgramStateRef State, const Summary &Summary) const {
- SmallString<96> Result;
- Result += "The size of the ";
- Result += getArgDesc(ArgN);
- Result += " should be equal to or less than the value of ";
- if (ConcreteSize) {
- ConcreteSize->toString(Result);
- } else if (SizeArgN) {
- Result += "the ";
- Result += getArgDesc(*SizeArgN);
- if (SizeMultiplierArgN) {
- Result += " times the ";
- Result += getArgDesc(*SizeMultiplierArgN);
- }
+void StdLibraryFunctionsChecker::RangeConstraint::applyOnWithinRange(
+ BasicValueFactory &BVF, QualType ArgT, const RangeApplyFunction &F) const {
+ if (Ranges.empty())
+ return;
+
+ for (auto [Start, End] : getRanges()) {
+ const llvm::APSInt &Min = BVF.getValue(Start, ArgT);
+ const llvm::APSInt &Max = BVF.getValue(End, ArgT);
+ assert(Min <= Max);
+ if (!F(Min, Max))
+ return;
}
- return Result.c_str();
}
-ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsOutOfRange(
- ProgramStateRef State, const CallEvent &Call,
- const Summary &Summary) const {
+void StdLibraryFunctionsChecker::RangeConstraint::applyOnOutOfRange(
+ BasicValueFactory &BVF, QualType ArgT, const RangeApplyFunction &F) const {
if (Ranges.empty())
- return State;
+ return;
- ProgramStateManager &Mgr = State->getStateManager();
- SValBuilder &SVB = Mgr.getSValBuilder();
- BasicValueFactory &BVF = SVB.getBasicValueFactory();
- ConstraintManager &CM = Mgr.getConstraintManager();
- QualType T = Summary.getArgType(getArgNo());
+ const IntRangeVector &R = getRanges();
+ size_t E = R.size();
+
+ const llvm::APSInt &MinusInf = BVF.getMinValue(ArgT);
+ const llvm::APSInt &PlusInf = BVF.getMaxValue(ArgT);
+
+ const llvm::APSInt &RangeLeft = BVF.getValue(R[0].first - 1ULL, ArgT);
+ const llvm::APSInt &RangeRight = BVF.getValue(R[E - 1].second + 1ULL, ArgT);
+
+ // Iterate over the "holes" between intervals.
+ for (size_t I = 1; I != E; ++I) {
+ const llvm::APSInt &Min = BVF.getValue(R[I - 1].second + 1ULL, ArgT);
+ const llvm::APSInt &Max = BVF.getValue(R[I].first - 1ULL, ArgT);
+ if (Min <= Max) {
+ if (!F(Min, Max))
+ return;
+ }
+ }
+ // Check the interval [T_MIN, min(R) - 1].
+ if (RangeLeft != PlusInf) {
+ assert(MinusInf <= RangeLeft);
+ if (!F(MinusInf, RangeLeft))
+ return;
+ }
+ // Check the interval [max(R) + 1, T_MAX],
+ if (RangeRight != MinusInf) {
+ assert(RangeRight <= PlusInf);
+ if (!F(RangeRight, PlusInf))
+ return;
+ }
+}
+
+ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::apply(
+ ProgramStateRef State, const CallEvent &Call, const Summary &Summary,
+ CheckerContext &C) const {
+ ConstraintManager &CM = C.getConstraintManager();
SVal V = getArgSVal(Call, getArgNo());
+ QualType T = Summary.getArgType(getArgNo());
if (auto N = V.getAs<NonLoc>()) {
- 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);
- const llvm::APSInt &Max = BVF.getValue(R[I].second, T);
- assert(Min <= Max);
+ auto ExcludeRangeFromArg = [&](const llvm::APSInt &Min,
+ const llvm::APSInt &Max) {
State = CM.assumeInclusiveRange(State, *N, Min, Max, false);
- if (!State)
- break;
- }
+ return static_cast<bool>(State);
+ };
+ // "OutOfRange R" is handled by excluding all ranges in R.
+ // "WithinRange R" is treated as "OutOfRange [T_MIN, T_MAX] \ R".
+ applyOnRange(negateKind(Kind), C.getSValBuilder().getBasicValueFactory(), T,
+ ExcludeRangeFromArg);
}
return State;
}
-ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsWithinRange(
- ProgramStateRef State, const CallEvent &Call,
- const Summary &Summary) const {
- if (Ranges.empty())
- return State;
+void StdLibraryFunctionsChecker::RangeConstraint::describe(
+ DescriptionKind DK, const CallEvent &Call, ProgramStateRef State,
+ const Summary &Summary, llvm::raw_ostream &Out) const {
+
+ BasicValueFactory &BVF = getBVF(State);
+ QualType T = Summary.getArgType(getArgNo());
+
+ Out << ((DK == Violation) ? "should be " : "is ");
+ if (!Description.empty()) {
+ Out << Description;
+ } else {
+ unsigned I = Ranges.size();
+ if (Kind == WithinRange) {
+ for (const std::pair<RangeInt, RangeInt> &R : Ranges) {
+ appendInsideRangeDesc(BVF.getValue(R.first, T),
+ BVF.getValue(R.second, T), T, BVF, Out);
+ if (--I > 0)
+ Out << " or ";
+ }
+ } else {
+ for (const std::pair<RangeInt, RangeInt> &R : Ranges) {
+ appendOutOfRangeDesc(BVF.getValue(R.first, T),
+ BVF.getValue(R.second, T), T, BVF, Out);
+ if (--I > 0)
+ Out << " and ";
+ }
+ }
+ }
+}
+
+bool StdLibraryFunctionsChecker::RangeConstraint::describeArgumentValue(
+ const CallEvent &Call, ProgramStateRef State, const Summary &Summary,
+ llvm::raw_ostream &Out) const {
+ unsigned int NRanges = 0;
+ bool HaveAllRanges = true;
ProgramStateManager &Mgr = State->getStateManager();
- SValBuilder &SVB = Mgr.getSValBuilder();
- BasicValueFactory &BVF = SVB.getBasicValueFactory();
+ BasicValueFactory &BVF = Mgr.getSValBuilder().getBasicValueFactory();
ConstraintManager &CM = Mgr.getConstraintManager();
- 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 IntRangeVector &R = getRanges();
- size_t E = R.size();
-
- const llvm::APSInt &MinusInf = BVF.getMinValue(T);
- const llvm::APSInt &PlusInf = BVF.getMaxValue(T);
-
- const llvm::APSInt &Left = BVF.getValue(R[0].first - 1ULL, T);
- if (Left != PlusInf) {
- assert(MinusInf <= Left);
- State = CM.assumeInclusiveRange(State, *N, MinusInf, Left, false);
- if (!State)
- return nullptr;
- }
-
- const llvm::APSInt &Right = BVF.getValue(R[E - 1].second + 1ULL, T);
- if (Right != MinusInf) {
- assert(Right <= PlusInf);
- State = CM.assumeInclusiveRange(State, *N, Right, PlusInf, false);
- if (!State)
- return nullptr;
+ if (const llvm::APSInt *Int = N->getAsInteger()) {
+ Out << "is ";
+ Out << *Int;
+ return true;
}
-
- 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);
- if (Min <= Max) {
- State = CM.assumeInclusiveRange(State, *N, Min, Max, false);
- if (!State)
- return nullptr;
+ QualType T = Summary.getArgType(getArgNo());
+ SmallString<128> MoreInfo;
+ llvm::raw_svector_ostream MoreInfoOs(MoreInfo);
+ auto ApplyF = [&](const llvm::APSInt &Min, const llvm::APSInt &Max) {
+ if (CM.assumeInclusiveRange(State, *N, Min, Max, true)) {
+ if (NRanges > 0)
+ MoreInfoOs << " or ";
+ appendInsideRangeDesc(Min, Max, T, BVF, MoreInfoOs);
+ ++NRanges;
+ } else {
+ HaveAllRanges = false;
}
+ return true;
+ };
+
+ applyOnRange(Kind, BVF, T, ApplyF);
+ assert(NRanges > 0);
+ if (!HaveAllRanges || NRanges == 1) {
+ Out << "is ";
+ Out << MoreInfo;
+ return true;
}
}
-
- return State;
+ return false;
}
ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply(
@@ -800,9 +1151,165 @@ ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply(
return State;
}
+ProgramStateRef StdLibraryFunctionsChecker::NotNullConstraint::apply(
+ ProgramStateRef State, const CallEvent &Call, const Summary &Summary,
+ CheckerContext &C) const {
+ SVal V = getArgSVal(Call, getArgNo());
+ if (V.isUndef())
+ return State;
+
+ DefinedOrUnknownSVal L = V.castAs<DefinedOrUnknownSVal>();
+ if (!isa<Loc>(L))
+ return State;
+
+ return State->assume(L, CannotBeNull);
+}
+
+void StdLibraryFunctionsChecker::NotNullConstraint::describe(
+ DescriptionKind DK, const CallEvent &Call, ProgramStateRef State,
+ const Summary &Summary, llvm::raw_ostream &Out) const {
+ assert(CannotBeNull &&
+ "Describe should not be used when the value must be NULL");
+ if (DK == Violation)
+ Out << "should not be NULL";
+ else
+ Out << "is not NULL";
+}
+
+bool StdLibraryFunctionsChecker::NotNullConstraint::describeArgumentValue(
+ const CallEvent &Call, ProgramStateRef State, const Summary &Summary,
+ llvm::raw_ostream &Out) const {
+ assert(!CannotBeNull && "This function is used when the value is NULL");
+ Out << "is NULL";
+ return true;
+}
+
+ProgramStateRef StdLibraryFunctionsChecker::NotNullBufferConstraint::apply(
+ ProgramStateRef State, const CallEvent &Call, const Summary &Summary,
+ CheckerContext &C) const {
+ SVal V = getArgSVal(Call, getArgNo());
+ if (V.isUndef())
+ return State;
+ DefinedOrUnknownSVal L = V.castAs<DefinedOrUnknownSVal>();
+ if (!isa<Loc>(L))
+ return State;
+
+ std::optional<DefinedOrUnknownSVal> SizeArg1 =
+ getArgSVal(Call, SizeArg1N).getAs<DefinedOrUnknownSVal>();
+ std::optional<DefinedOrUnknownSVal> SizeArg2;
+ if (SizeArg2N)
+ SizeArg2 = getArgSVal(Call, *SizeArg2N).getAs<DefinedOrUnknownSVal>();
+
+ auto IsArgZero = [State](std::optional<DefinedOrUnknownSVal> Val) {
+ if (!Val)
+ return false;
+ auto [IsNonNull, IsNull] = State->assume(*Val);
+ return IsNull && !IsNonNull;
+ };
+
+ if (IsArgZero(SizeArg1) || IsArgZero(SizeArg2))
+ return State;
+
+ return State->assume(L, CannotBeNull);
+}
+
+void StdLibraryFunctionsChecker::NotNullBufferConstraint::describe(
+ DescriptionKind DK, const CallEvent &Call, ProgramStateRef State,
+ const Summary &Summary, llvm::raw_ostream &Out) const {
+ assert(CannotBeNull &&
+ "Describe should not be used when the value must be NULL");
+ if (DK == Violation)
+ Out << "should not be NULL";
+ else
+ Out << "is not NULL";
+}
+
+bool StdLibraryFunctionsChecker::NotNullBufferConstraint::describeArgumentValue(
+ const CallEvent &Call, ProgramStateRef State, const Summary &Summary,
+ llvm::raw_ostream &Out) const {
+ assert(!CannotBeNull && "This function is used when the value is NULL");
+ Out << "is NULL";
+ return true;
+}
+
+ProgramStateRef StdLibraryFunctionsChecker::BufferSizeConstraint::apply(
+ ProgramStateRef State, const CallEvent &Call, const Summary &Summary,
+ CheckerContext &C) const {
+ SValBuilder &SvalBuilder = C.getSValBuilder();
+ // The buffer argument.
+ SVal BufV = getArgSVal(Call, getArgNo());
+
+ // Get the size constraint.
+ const SVal SizeV = [this, &State, &Call, &Summary, &SvalBuilder]() {
+ if (ConcreteSize) {
+ return SVal(SvalBuilder.makeIntVal(*ConcreteSize));
+ }
+ assert(SizeArgN && "The constraint must be either a concrete value or "
+ "encoded in an argument.");
+ // 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));
+ }
+ return SizeV;
+ }();
+
+ // The dynamic size of the buffer argument, got from the analyzer engine.
+ SVal BufDynSize = getDynamicExtentWithOffset(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");
+}
+
+void StdLibraryFunctionsChecker::BufferSizeConstraint::describe(
+ DescriptionKind DK, const CallEvent &Call, ProgramStateRef State,
+ const Summary &Summary, llvm::raw_ostream &Out) const {
+ Out << ((DK == Violation) ? "should be " : "is ");
+ Out << "a buffer with size equal to or greater than ";
+ if (ConcreteSize) {
+ Out << *ConcreteSize;
+ } else if (SizeArgN) {
+ Out << "the value of the ";
+ printArgDesc(*SizeArgN, Out);
+ printArgValueInfo(*SizeArgN, State, Call, Out);
+ if (SizeMultiplierArgN) {
+ Out << " times the ";
+ printArgDesc(*SizeMultiplierArgN, Out);
+ printArgValueInfo(*SizeMultiplierArgN, State, Call, Out);
+ }
+ }
+}
+
+bool StdLibraryFunctionsChecker::BufferSizeConstraint::describeArgumentValue(
+ const CallEvent &Call, ProgramStateRef State, const Summary &Summary,
+ llvm::raw_ostream &Out) const {
+ SVal BufV = getArgSVal(Call, getArgNo());
+ SVal BufDynSize = getDynamicExtentWithOffset(State, BufV);
+ if (const llvm::APSInt *Val =
+ State->getStateManager().getSValBuilder().getKnownValue(State,
+ BufDynSize)) {
+ Out << "is a buffer with size " << *Val;
+ return true;
+ }
+ return false;
+}
+
void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
- Optional<Summary> FoundSummary = findFunctionSummary(Call, C);
+ std::optional<Summary> FoundSummary = findFunctionSummary(Call, C);
if (!FoundSummary)
return;
@@ -810,55 +1317,155 @@ void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call,
ProgramStateRef State = C.getState();
ProgramStateRef NewState = State;
+ ExplodedNode *NewNode = C.getPredecessor();
for (const ValueConstraintPtr &Constraint : Summary.getArgConstraints()) {
+ ValueConstraintPtr NegatedConstraint = Constraint->negate();
ProgramStateRef SuccessSt = Constraint->apply(NewState, Call, Summary, C);
ProgramStateRef FailureSt =
- Constraint->negate()->apply(NewState, Call, Summary, C);
+ NegatedConstraint->apply(NewState, Call, Summary, C);
// The argument constraint is not satisfied.
if (FailureSt && !SuccessSt) {
- if (ExplodedNode *N = C.generateErrorNode(NewState))
- reportBug(Call, N, Constraint.get(), Summary, C);
+ if (ExplodedNode *N = C.generateErrorNode(State, NewNode))
+ reportBug(Call, N, Constraint.get(), NegatedConstraint.get(), Summary,
+ 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;
+ }
+ // 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 != State) {
+ SmallString<128> Msg;
+ llvm::raw_svector_ostream Os(Msg);
+ Os << "Assuming that the ";
+ printArgDesc(Constraint->getArgNo(), Os);
+ Os << " to '";
+ Os << getFunctionName(Call);
+ Os << "' ";
+ Constraint->describe(ValueConstraint::Assumption, Call, NewState, Summary,
+ Os);
+ const auto ArgSVal = Call.getArgSVal(Constraint->getArgNo());
+ NewNode = C.addTransition(
+ NewState, NewNode,
+ C.getNoteTag([Msg = std::move(Msg), ArgSVal](
+ PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
+ if (BR.isInteresting(ArgSVal))
+ OS << Msg;
+ }));
}
}
- if (NewState && NewState != State)
- C.addTransition(NewState);
}
void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
- Optional<Summary> FoundSummary = findFunctionSummary(Call, C);
+ std::optional<Summary> FoundSummary = findFunctionSummary(Call, C);
if (!FoundSummary)
return;
// Now apply the constraints.
const Summary &Summary = *FoundSummary;
ProgramStateRef State = C.getState();
+ ExplodedNode *Node = C.getPredecessor();
// Apply case/branch specifications.
- for (const ConstraintSet &Case : Summary.getCaseConstraints()) {
+ for (const SummaryCase &Case : Summary.getCases()) {
ProgramStateRef NewState = State;
- for (const ValueConstraintPtr &Constraint : Case) {
+ for (const ValueConstraintPtr &Constraint : Case.getConstraints()) {
NewState = Constraint->apply(NewState, Call, Summary, C);
if (!NewState)
break;
}
- if (NewState && NewState != State)
+ if (NewState)
+ NewState = Case.getErrnoConstraint().apply(NewState, Call, Summary, C);
+
+ if (!NewState)
+ continue;
+
+ // Here it's possible that NewState == State, e.g. when other checkers
+ // already applied the same constraints (or stricter ones).
+ // Still add these note tags, the other checker should add only its
+ // specialized note tags. These general note tags are handled always by
+ // StdLibraryFunctionsChecker.
+
+ ExplodedNode *Pred = Node;
+ DeclarationName FunctionName =
+ cast<NamedDecl>(Call.getDecl())->getDeclName();
+
+ std::string ErrnoNote = Case.getErrnoConstraint().describe(C);
+ std::string CaseNote;
+ if (Case.getNote().empty()) {
+ if (!ErrnoNote.empty())
+ ErrnoNote =
+ llvm::formatv("After calling '{0}' {1}", FunctionName, ErrnoNote);
+ } else {
+ CaseNote = llvm::formatv(Case.getNote().str().c_str(), FunctionName);
+ }
+ const SVal RV = Call.getReturnValue();
+
+ if (Summary.getInvalidationKd() == EvalCallAsPure) {
+ // Do not expect that errno is interesting (the "pure" functions do not
+ // affect it).
+ if (!CaseNote.empty()) {
+ const NoteTag *Tag = C.getNoteTag(
+ [Node, CaseNote, RV](PathSensitiveBugReport &BR) -> std::string {
+ // Try to omit the note if we know in advance which branch is
+ // taken (this means, only one branch exists).
+ // This check is performed inside the lambda, after other
+ // (or this) checkers had a chance to add other successors.
+ // Dereferencing the saved node object is valid because it's part
+ // of a bug report call sequence.
+ // FIXME: This check is not exact. We may be here after a state
+ // split that was performed by another checker (and can not find
+ // the successors). This is why this check is only used in the
+ // EvalCallAsPure case.
+ if (BR.isInteresting(RV) && Node->succ_size() > 1)
+ return CaseNote;
+ return "";
+ });
+ Pred = C.addTransition(NewState, Pred, Tag);
+ }
+ } else {
+ if (!CaseNote.empty() || !ErrnoNote.empty()) {
+ const NoteTag *Tag =
+ C.getNoteTag([CaseNote, ErrnoNote,
+ RV](PathSensitiveBugReport &BR) -> std::string {
+ // If 'errno' is interesting, show the user a note about the case
+ // (what happened at the function call) and about how 'errno'
+ // causes the problem. ErrnoChecker sets the errno (but not RV) to
+ // interesting.
+ // If only the return value is interesting, show only the case
+ // note.
+ std::optional<Loc> ErrnoLoc =
+ errno_modeling::getErrnoLoc(BR.getErrorNode()->getState());
+ bool ErrnoImportant = !ErrnoNote.empty() && ErrnoLoc &&
+ BR.isInteresting(ErrnoLoc->getAsRegion());
+ if (ErrnoImportant) {
+ BR.markNotInteresting(ErrnoLoc->getAsRegion());
+ if (CaseNote.empty())
+ return ErrnoNote;
+ return llvm::formatv("{0}; {1}", CaseNote, ErrnoNote);
+ } else {
+ if (BR.isInteresting(RV))
+ return CaseNote;
+ }
+ return "";
+ });
+ Pred = C.addTransition(NewState, Pred, Tag);
+ }
+ }
+
+ // Add the transition if no note tag was added.
+ if (Pred == Node && NewState != State)
C.addTransition(NewState);
}
}
bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call,
CheckerContext &C) const {
- Optional<Summary> FoundSummary = findFunctionSummary(Call, C);
+ std::optional<Summary> FoundSummary = findFunctionSummary(Call, C);
if (!FoundSummary)
return false;
@@ -871,7 +1478,9 @@ bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call,
SVal V = C.getSValBuilder().conjureSymbolVal(
CE, LC, CE->getType().getCanonicalType(), C.blockCount());
State = State->BindExpr(CE, LC, V);
+
C.addTransition(State);
+
return true;
}
case NoEvalCall:
@@ -910,12 +1519,11 @@ bool StdLibraryFunctionsChecker::Signature::matches(
}
// Check the argument types.
- for (size_t I = 0, E = ArgTys.size(); I != E; ++I) {
- QualType ArgTy = ArgTys[I];
+ for (auto [Idx, ArgTy] : llvm::enumerate(ArgTys)) {
if (isIrrelevant(ArgTy))
continue;
QualType FDArgTy =
- RemoveRestrict(FD->getParamDecl(I)->getType().getCanonicalType());
+ RemoveRestrict(FD->getParamDecl(Idx)->getType().getCanonicalType());
if (ArgTy != FDArgTy)
return false;
}
@@ -923,26 +1531,26 @@ bool StdLibraryFunctionsChecker::Signature::matches(
return true;
}
-Optional<StdLibraryFunctionsChecker::Summary>
+std::optional<StdLibraryFunctionsChecker::Summary>
StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD,
CheckerContext &C) const {
if (!FD)
- return None;
+ return std::nullopt;
initFunctionSummaries(C);
auto FSMI = FunctionSummaryMap.find(FD->getCanonicalDecl());
if (FSMI == FunctionSummaryMap.end())
- return None;
+ return std::nullopt;
return FSMI->second;
}
-Optional<StdLibraryFunctionsChecker::Summary>
+std::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 std::nullopt;
return findFunctionSummary(FD, C);
}
@@ -950,10 +1558,12 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
CheckerContext &C) const {
if (SummariesInitialized)
return;
+ SummariesInitialized = true;
SValBuilder &SVB = C.getSValBuilder();
BasicValueFactory &BVF = SVB.getBasicValueFactory();
const ASTContext &ACtx = BVF.getContext();
+ Preprocessor &PP = C.getPreprocessor();
// Helper class to lookup a type by its name.
class LookupType {
@@ -963,11 +1573,11 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
LookupType(const ASTContext &ACtx) : ACtx(ACtx) {}
// Find the type. If not found then the optional is not set.
- llvm::Optional<QualType> operator()(StringRef Name) {
+ std::optional<QualType> operator()(StringRef Name) {
IdentifierInfo &II = ACtx.Idents.get(Name);
auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
if (LookupRes.empty())
- return None;
+ return std::nullopt;
// Prioritze typedef declarations.
// This is needed in case of C struct typedefs. E.g.:
@@ -985,7 +1595,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
for (Decl *D : LookupRes)
if (auto *TD = dyn_cast<TypeDecl>(D))
return ACtx.getTypeDeclType(TD).getCanonicalType();
- return None;
+ return std::nullopt;
}
} lookupTy(ACtx);
@@ -999,10 +1609,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
QualType operator()(QualType Ty) {
return ACtx.getLangOpts().C99 ? ACtx.getRestrictType(Ty) : Ty;
}
- Optional<QualType> operator()(Optional<QualType> Ty) {
+ std::optional<QualType> operator()(std::optional<QualType> Ty) {
if (Ty)
return operator()(*Ty);
- return None;
+ return std::nullopt;
}
} getRestrictTy(ACtx);
class GetPointerTy {
@@ -1011,16 +1621,16 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
public:
GetPointerTy(const ASTContext &ACtx) : ACtx(ACtx) {}
QualType operator()(QualType Ty) { return ACtx.getPointerType(Ty); }
- Optional<QualType> operator()(Optional<QualType> Ty) {
+ std::optional<QualType> operator()(std::optional<QualType> Ty) {
if (Ty)
return operator()(*Ty);
- return None;
+ return std::nullopt;
}
} getPointerTy(ACtx);
class {
public:
- Optional<QualType> operator()(Optional<QualType> Ty) {
- return Ty ? Optional<QualType>(Ty->withConst()) : None;
+ std::optional<QualType> operator()(std::optional<QualType> Ty) {
+ return Ty ? std::optional<QualType>(Ty->withConst()) : std::nullopt;
}
QualType operator()(QualType Ty) { return Ty.withConst(); }
} getConstTy;
@@ -1029,14 +1639,14 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
public:
GetMaxValue(BasicValueFactory &BVF) : BVF(BVF) {}
- Optional<RangeInt> operator()(QualType Ty) {
+ std::optional<RangeInt> operator()(QualType Ty) {
return BVF.getMaxValue(Ty).getLimitedValue();
}
- Optional<RangeInt> operator()(Optional<QualType> Ty) {
+ std::optional<RangeInt> operator()(std::optional<QualType> Ty) {
if (Ty) {
return operator()(*Ty);
}
- return None;
+ return std::nullopt;
}
} getMaxValue(BVF);
@@ -1089,14 +1699,11 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
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;
- }();
+ // Get platform dependent values of some macros.
+ // Try our best to parse this from the Preprocessor, otherwise fallback to a
+ // default value (what is found in a library header).
+ const auto EOFv = tryExpandAsInteger("EOF", PP).value_or(-1);
+ const auto AT_FDCWDv = tryExpandAsInteger("AT_FDCWD", PP).value_or(-100);
// Auxiliary class to aid adding summaries to the summary map.
struct AddToFunctionSummaryMap {
@@ -1146,9 +1753,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
} addToFunctionSummaryMap(ACtx, FunctionSummaryMap, DisplayLoadedSummaries);
// 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 ArgumentCondition = [](ArgNo ArgN, RangeKind Kind, IntRangeVector Ranges,
+ StringRef Desc = "") {
+ return std::make_shared<RangeConstraint>(ArgN, Kind, Ranges, Desc);
};
auto BufferSize = [](auto... Args) {
return std::make_shared<BufferSizeConstraint>(Args...);
@@ -1165,13 +1772,13 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
auto operator()(RangeInt b, RangeInt e) {
return IntRangeVector{std::pair<RangeInt, RangeInt>{b, e}};
}
- auto operator()(RangeInt b, Optional<RangeInt> e) {
+ auto operator()(RangeInt b, std::optional<RangeInt> e) {
if (e)
return IntRangeVector{std::pair<RangeInt, RangeInt>{b, *e}};
return IntRangeVector{};
}
auto operator()(std::pair<RangeInt, RangeInt> i0,
- std::pair<RangeInt, Optional<RangeInt>> i1) {
+ std::pair<RangeInt, std::optional<RangeInt>> i1) {
if (i1.second)
return IntRangeVector{i0, {i1.first, *(i1.second)}};
return IntRangeVector{i0};
@@ -1184,10 +1791,26 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
auto NotNull = [&](ArgNo ArgN) {
return std::make_shared<NotNullConstraint>(ArgN);
};
+ auto IsNull = [&](ArgNo ArgN) {
+ return std::make_shared<NotNullConstraint>(ArgN, false);
+ };
+ auto NotNullBuffer = [&](ArgNo ArgN, ArgNo SizeArg1N, ArgNo SizeArg2N) {
+ return std::make_shared<NotNullBufferConstraint>(ArgN, SizeArg1N,
+ SizeArg2N);
+ };
- Optional<QualType> FileTy = lookupTy("FILE");
- Optional<QualType> FilePtrTy = getPointerTy(FileTy);
- Optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy);
+ std::optional<QualType> FileTy = lookupTy("FILE");
+ std::optional<QualType> FilePtrTy = getPointerTy(FileTy);
+ std::optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy);
+
+ std::optional<QualType> FPosTTy = lookupTy("fpos_t");
+ std::optional<QualType> FPosTPtrTy = getPointerTy(FPosTTy);
+ std::optional<QualType> ConstFPosTPtrTy = getPointerTy(getConstTy(FPosTTy));
+ std::optional<QualType> FPosTPtrRestrictTy = getRestrictTy(FPosTPtrTy);
+
+ constexpr llvm::StringLiteral GenericSuccessMsg(
+ "Assuming that '{0}' is successful");
+ constexpr llvm::StringLiteral GenericFailureMsg("Assuming that '{0}' fails");
// We are finally ready to define specifications for all supported functions.
//
@@ -1210,163 +1833,227 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
// Boils down to isupper() or islower() or isdigit().
.Case({ArgumentCondition(0U, WithinRange,
{{'0', '9'}, {'A', 'Z'}, {'a', 'z'}}),
- ReturnValueCondition(OutOfRange, SingleValue(0))})
+ ReturnValueCondition(OutOfRange, SingleValue(0))},
+ ErrnoIrrelevant, "Assuming the character is alphanumeric")
// 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, WithinRange, {{128, UCharRangeMax}})},
+ ErrnoIrrelevant)
.Case(
{ArgumentCondition(
0U, OutOfRange,
{{'0', '9'}, {'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}),
- ReturnValueCondition(WithinRange, SingleValue(0))})
- .ArgConstraint(ArgumentCondition(
- 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})));
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoIrrelevant, "Assuming the character is non-alphanumeric")
+ .ArgConstraint(ArgumentCondition(0U, WithinRange,
+ {{EOFv, EOFv}, {0, UCharRangeMax}},
+ "an unsigned char value or EOF")));
addToFunctionSummaryMap(
"isalpha", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
.Case({ArgumentCondition(0U, WithinRange, {{'A', 'Z'}, {'a', 'z'}}),
- ReturnValueCondition(OutOfRange, SingleValue(0))})
+ ReturnValueCondition(OutOfRange, SingleValue(0))},
+ ErrnoIrrelevant, "Assuming the character is alphabetical")
// The locale-specific range.
- .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})})
+ .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})},
+ ErrnoIrrelevant)
.Case({ArgumentCondition(
0U, OutOfRange,
{{'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}),
- ReturnValueCondition(WithinRange, SingleValue(0))}));
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoIrrelevant, "Assuming the character is non-alphabetical"));
addToFunctionSummaryMap(
"isascii", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
.Case({ArgumentCondition(0U, WithinRange, Range(0, 127)),
- ReturnValueCondition(OutOfRange, SingleValue(0))})
+ ReturnValueCondition(OutOfRange, SingleValue(0))},
+ ErrnoIrrelevant, "Assuming the character is an ASCII character")
.Case({ArgumentCondition(0U, OutOfRange, Range(0, 127)),
- ReturnValueCondition(WithinRange, SingleValue(0))}));
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoIrrelevant,
+ "Assuming the character is not an ASCII character"));
addToFunctionSummaryMap(
"isblank", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
.Case({ArgumentCondition(0U, WithinRange, {{'\t', '\t'}, {' ', ' '}}),
- ReturnValueCondition(OutOfRange, SingleValue(0))})
+ ReturnValueCondition(OutOfRange, SingleValue(0))},
+ ErrnoIrrelevant, "Assuming the character is a blank character")
.Case({ArgumentCondition(0U, OutOfRange, {{'\t', '\t'}, {' ', ' '}}),
- ReturnValueCondition(WithinRange, SingleValue(0))}));
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoIrrelevant,
+ "Assuming the character is not a blank character"));
addToFunctionSummaryMap(
"iscntrl", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
.Case({ArgumentCondition(0U, WithinRange, {{0, 32}, {127, 127}}),
- ReturnValueCondition(OutOfRange, SingleValue(0))})
+ ReturnValueCondition(OutOfRange, SingleValue(0))},
+ ErrnoIrrelevant,
+ "Assuming the character is a control character")
.Case({ArgumentCondition(0U, OutOfRange, {{0, 32}, {127, 127}}),
- ReturnValueCondition(WithinRange, SingleValue(0))}));
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoIrrelevant,
+ "Assuming the character is not a control character"));
addToFunctionSummaryMap(
"isdigit", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
.Case({ArgumentCondition(0U, WithinRange, Range('0', '9')),
- ReturnValueCondition(OutOfRange, SingleValue(0))})
+ ReturnValueCondition(OutOfRange, SingleValue(0))},
+ ErrnoIrrelevant, "Assuming the character is a digit")
.Case({ArgumentCondition(0U, OutOfRange, Range('0', '9')),
- ReturnValueCondition(WithinRange, SingleValue(0))}));
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoIrrelevant, "Assuming the character is not a digit"));
addToFunctionSummaryMap(
"isgraph", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
.Case({ArgumentCondition(0U, WithinRange, Range(33, 126)),
- ReturnValueCondition(OutOfRange, SingleValue(0))})
- .Case({ArgumentCondition(0U, OutOfRange, Range(33, 126)),
- ReturnValueCondition(WithinRange, SingleValue(0))}));
+ ReturnValueCondition(OutOfRange, SingleValue(0))},
+ ErrnoIrrelevant,
+ "Assuming the character has graphical representation")
+ .Case(
+ {ArgumentCondition(0U, OutOfRange, Range(33, 126)),
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoIrrelevant,
+ "Assuming the character does not have graphical representation"));
addToFunctionSummaryMap(
"islower", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
// Is certainly lowercase.
.Case({ArgumentCondition(0U, WithinRange, Range('a', 'z')),
- ReturnValueCondition(OutOfRange, SingleValue(0))})
+ ReturnValueCondition(OutOfRange, SingleValue(0))},
+ ErrnoIrrelevant, "Assuming the character is a lowercase letter")
// Is ascii but not lowercase.
.Case({ArgumentCondition(0U, WithinRange, Range(0, 127)),
ArgumentCondition(0U, OutOfRange, Range('a', 'z')),
- ReturnValueCondition(WithinRange, SingleValue(0))})
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoIrrelevant,
+ "Assuming the character is not a lowercase letter")
// The locale-specific range.
- .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})})
+ .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})},
+ ErrnoIrrelevant)
// Is not an unsigned char.
.Case({ArgumentCondition(0U, OutOfRange, Range(0, UCharRangeMax)),
- ReturnValueCondition(WithinRange, SingleValue(0))}));
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoIrrelevant));
addToFunctionSummaryMap(
"isprint", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
.Case({ArgumentCondition(0U, WithinRange, Range(32, 126)),
- ReturnValueCondition(OutOfRange, SingleValue(0))})
+ ReturnValueCondition(OutOfRange, SingleValue(0))},
+ ErrnoIrrelevant, "Assuming the character is printable")
.Case({ArgumentCondition(0U, OutOfRange, Range(32, 126)),
- ReturnValueCondition(WithinRange, SingleValue(0))}));
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoIrrelevant, "Assuming the character is non-printable"));
addToFunctionSummaryMap(
"ispunct", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
.Case({ArgumentCondition(
0U, WithinRange,
{{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}),
- ReturnValueCondition(OutOfRange, SingleValue(0))})
+ ReturnValueCondition(OutOfRange, SingleValue(0))},
+ ErrnoIrrelevant, "Assuming the character is a punctuation mark")
.Case({ArgumentCondition(
0U, OutOfRange,
{{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}),
- ReturnValueCondition(WithinRange, SingleValue(0))}));
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoIrrelevant,
+ "Assuming the character is not a punctuation mark"));
addToFunctionSummaryMap(
"isspace", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
// Space, '\f', '\n', '\r', '\t', '\v'.
.Case({ArgumentCondition(0U, WithinRange, {{9, 13}, {' ', ' '}}),
- ReturnValueCondition(OutOfRange, SingleValue(0))})
+ ReturnValueCondition(OutOfRange, SingleValue(0))},
+ ErrnoIrrelevant,
+ "Assuming the character is a whitespace character")
// The locale-specific range.
- .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})})
+ .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})},
+ ErrnoIrrelevant)
.Case({ArgumentCondition(0U, OutOfRange,
{{9, 13}, {' ', ' '}, {128, UCharRangeMax}}),
- ReturnValueCondition(WithinRange, SingleValue(0))}));
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoIrrelevant,
+ "Assuming the character is not a whitespace character"));
addToFunctionSummaryMap(
"isupper", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
// Is certainly uppercase.
.Case({ArgumentCondition(0U, WithinRange, Range('A', 'Z')),
- ReturnValueCondition(OutOfRange, SingleValue(0))})
+ ReturnValueCondition(OutOfRange, SingleValue(0))},
+ ErrnoIrrelevant,
+ "Assuming the character is an uppercase letter")
// The locale-specific range.
- .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})})
+ .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})},
+ ErrnoIrrelevant)
// Other.
.Case({ArgumentCondition(0U, OutOfRange,
{{'A', 'Z'}, {128, UCharRangeMax}}),
- ReturnValueCondition(WithinRange, SingleValue(0))}));
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoIrrelevant,
+ "Assuming the character is not an uppercase letter"));
addToFunctionSummaryMap(
"isxdigit", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
.Case({ArgumentCondition(0U, WithinRange,
{{'0', '9'}, {'A', 'F'}, {'a', 'f'}}),
- ReturnValueCondition(OutOfRange, SingleValue(0))})
+ ReturnValueCondition(OutOfRange, SingleValue(0))},
+ ErrnoIrrelevant,
+ "Assuming the character is a hexadecimal digit")
.Case({ArgumentCondition(0U, OutOfRange,
{{'0', '9'}, {'A', 'F'}, {'a', 'f'}}),
- ReturnValueCondition(WithinRange, SingleValue(0))}));
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoIrrelevant,
+ "Assuming the character is not a hexadecimal digit"));
addToFunctionSummaryMap(
"toupper", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
- .ArgConstraint(ArgumentCondition(
- 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})));
+ .ArgConstraint(ArgumentCondition(0U, WithinRange,
+ {{EOFv, EOFv}, {0, UCharRangeMax}},
+ "an unsigned char value or EOF")));
addToFunctionSummaryMap(
"tolower", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
- .ArgConstraint(ArgumentCondition(
- 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})));
+ .ArgConstraint(ArgumentCondition(0U, WithinRange,
+ {{EOFv, EOFv}, {0, UCharRangeMax}},
+ "an unsigned char value or EOF")));
addToFunctionSummaryMap(
"toascii", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
- .ArgConstraint(ArgumentCondition(
- 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})));
+ .ArgConstraint(ArgumentCondition(0U, WithinRange,
+ {{EOFv, EOFv}, {0, UCharRangeMax}},
+ "an unsigned char value or EOF")));
// The getc() family of functions that returns either a char or an EOF.
addToFunctionSummaryMap(
{"getc", "fgetc"}, Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
Summary(NoEvalCall)
.Case({ReturnValueCondition(WithinRange,
- {{EOFv, EOFv}, {0, UCharRangeMax}})}));
+ {{EOFv, EOFv}, {0, UCharRangeMax}})},
+ ErrnoIrrelevant));
addToFunctionSummaryMap(
"getchar", Signature(ArgTypes{}, RetType{IntTy}),
Summary(NoEvalCall)
.Case({ReturnValueCondition(WithinRange,
- {{EOFv, EOFv}, {0, UCharRangeMax}})}));
+ {{EOFv, EOFv}, {0, UCharRangeMax}})},
+ ErrnoIrrelevant));
// read()-like functions that never return more than buffer size.
auto FreadSummary =
Summary(NoEvalCall)
- .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
- ReturnValueCondition(WithinRange, Range(0, SizeMax))})
- .ArgConstraint(NotNull(ArgNo(0)))
+ .Case({ArgumentCondition(1U, WithinRange, Range(1, SizeMax)),
+ ArgumentCondition(2U, WithinRange, Range(1, SizeMax)),
+ ReturnValueCondition(BO_LT, ArgNo(2)),
+ ReturnValueCondition(WithinRange, Range(0, SizeMax))},
+ ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .Case({ArgumentCondition(1U, WithinRange, Range(1, SizeMax)),
+ ReturnValueCondition(BO_EQ, ArgNo(2)),
+ ReturnValueCondition(WithinRange, Range(0, SizeMax))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({ArgumentCondition(1U, WithinRange, SingleValue(0)),
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoMustNotBeChecked,
+ "Assuming that argument 'size' to '{0}' is 0")
+ .ArgConstraint(NotNullBuffer(ArgNo(0), ArgNo(1), ArgNo(2)))
.ArgConstraint(NotNull(ArgNo(3)))
.ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1),
/*BufSizeMultiplier=*/ArgNo(2)));
@@ -1386,13 +2073,14 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
RetType{SizeTy}),
FreadSummary);
- Optional<QualType> Ssize_tTy = lookupTy("ssize_t");
- Optional<RangeInt> Ssize_tMax = getMaxValue(Ssize_tTy);
+ std::optional<QualType> Ssize_tTy = lookupTy("ssize_t");
+ std::optional<RangeInt> Ssize_tMax = getMaxValue(Ssize_tTy);
auto ReadSummary =
Summary(NoEvalCall)
.Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
- ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))});
+ ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))},
+ ErrnoIrrelevant);
// 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.
@@ -1409,7 +2097,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
auto GetLineSummary =
Summary(NoEvalCall)
.Case({ReturnValueCondition(WithinRange,
- Range({-1, -1}, {1, Ssize_tMax}))});
+ Range({-1, -1}, {1, Ssize_tMax}))},
+ ErrnoIrrelevant);
QualType CharPtrPtrRestrictTy = getRestrictTy(getPointerTy(CharPtrTy));
@@ -1433,7 +2122,226 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
RetType{Ssize_tTy}),
GetLineSummary);
+ {
+ Summary GetenvSummary =
+ Summary(NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .Case({NotNull(Ret)}, ErrnoIrrelevant,
+ "Assuming the environment variable exists");
+ // In untrusted environments the envvar might not exist.
+ if (!ShouldAssumeControlledEnvironment)
+ GetenvSummary.Case({NotNull(Ret)->negate()}, ErrnoIrrelevant,
+ "Assuming the environment variable does not exist");
+
+ // char *getenv(const char *name);
+ addToFunctionSummaryMap(
+ "getenv", Signature(ArgTypes{ConstCharPtrTy}, RetType{CharPtrTy}),
+ std::move(GetenvSummary));
+ }
+
if (ModelPOSIX) {
+ const auto ReturnsZeroOrMinusOne =
+ ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, 0))};
+ const auto ReturnsZero =
+ ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(0))};
+ const auto ReturnsMinusOne =
+ ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(-1))};
+ const auto ReturnsEOF =
+ ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(EOFv))};
+ const auto ReturnsNonnegative =
+ ConstraintSet{ReturnValueCondition(WithinRange, Range(0, IntMax))};
+ const auto ReturnsNonZero =
+ ConstraintSet{ReturnValueCondition(OutOfRange, SingleValue(0))};
+ const auto ReturnsFileDescriptor =
+ ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, IntMax))};
+ const auto &ReturnsValidFileDescriptor = ReturnsNonnegative;
+
+ auto ValidFileDescriptorOrAtFdcwd = [&](ArgNo ArgN) {
+ return std::make_shared<RangeConstraint>(
+ ArgN, WithinRange, Range({AT_FDCWDv, AT_FDCWDv}, {0, IntMax}),
+ "a valid file descriptor or AT_FDCWD");
+ };
+
+ // FILE *fopen(const char *restrict pathname, const char *restrict mode);
+ addToFunctionSummaryMap(
+ "fopen",
+ Signature(ArgTypes{ConstCharPtrRestrictTy, ConstCharPtrRestrictTy},
+ RetType{FilePtrTy}),
+ Summary(NoEvalCall)
+ .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // FILE *fdopen(int fd, const char *mode);
+ addToFunctionSummaryMap(
+ "fdopen",
+ Signature(ArgTypes{IntTy, ConstCharPtrTy}, RetType{FilePtrTy}),
+ Summary(NoEvalCall)
+ .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // FILE *tmpfile(void);
+ addToFunctionSummaryMap(
+ "tmpfile", Signature(ArgTypes{}, RetType{FilePtrTy}),
+ Summary(NoEvalCall)
+ .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg));
+
+ // FILE *freopen(const char *restrict pathname, const char *restrict mode,
+ // FILE *restrict stream);
+ addToFunctionSummaryMap(
+ "freopen",
+ Signature(ArgTypes{ConstCharPtrRestrictTy, ConstCharPtrRestrictTy,
+ FilePtrRestrictTy},
+ RetType{FilePtrTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(BO_EQ, ArgNo(2))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(NotNull(ArgNo(2))));
+
+ // int fclose(FILE *stream);
+ addToFunctionSummaryMap(
+ "fclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsEOF, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int ungetc(int c, FILE *stream);
+ addToFunctionSummaryMap(
+ "ungetc", Signature(ArgTypes{IntTy, FilePtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(BO_EQ, ArgNo(0)),
+ ArgumentCondition(0, WithinRange, {{0, UCharRangeMax}})},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({ReturnValueCondition(WithinRange, SingleValue(EOFv)),
+ ArgumentCondition(0, WithinRange, SingleValue(EOFv))},
+ ErrnoNEZeroIrrelevant,
+ "Assuming that 'ungetc' fails because EOF was passed as "
+ "character")
+ .Case({ReturnValueCondition(WithinRange, SingleValue(EOFv)),
+ ArgumentCondition(0, WithinRange, {{0, UCharRangeMax}})},
+ ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(ArgumentCondition(
+ 0, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ std::optional<QualType> Off_tTy = lookupTy("off_t");
+ std::optional<RangeInt> Off_tMax = getMaxValue(Off_tTy);
+
+ // int fseek(FILE *stream, long offset, int whence);
+ // FIXME: It can be possible to get the 'SEEK_' values (like EOFv) and use
+ // these for condition of arg 2.
+ // Now the range [0,2] is used (the `SEEK_*` constants are usually 0,1,2).
+ addToFunctionSummaryMap(
+ "fseek", Signature(ArgTypes{FilePtrTy, LongTy, IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(ArgumentCondition(2, WithinRange, {{0, 2}})));
+
+ // int fseeko(FILE *stream, off_t offset, int whence);
+ addToFunctionSummaryMap(
+ "fseeko",
+ Signature(ArgTypes{FilePtrTy, Off_tTy, IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(ArgumentCondition(2, WithinRange, {{0, 2}})));
+
+ // int fgetpos(FILE *restrict stream, fpos_t *restrict pos);
+ // From 'The Open Group Base Specifications Issue 7, 2018 edition':
+ // "The fgetpos() function shall not change the setting of errno if
+ // successful."
+ addToFunctionSummaryMap(
+ "fgetpos",
+ Signature(ArgTypes{FilePtrRestrictTy, FPosTPtrRestrictTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoUnchanged, GenericSuccessMsg)
+ .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int fsetpos(FILE *stream, const fpos_t *pos);
+ // From 'The Open Group Base Specifications Issue 7, 2018 edition':
+ // "The fsetpos() function shall not change the setting of errno if
+ // successful."
+ addToFunctionSummaryMap(
+ "fsetpos",
+ Signature(ArgTypes{FilePtrTy, ConstFPosTPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoUnchanged, GenericSuccessMsg)
+ .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int fflush(FILE *stream);
+ addToFunctionSummaryMap(
+ "fflush", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsEOF, ErrnoNEZeroIrrelevant, GenericFailureMsg));
+
+ // long ftell(FILE *stream);
+ // From 'The Open Group Base Specifications Issue 7, 2018 edition':
+ // "The ftell() function shall not change the setting of errno if
+ // successful."
+ addToFunctionSummaryMap(
+ "ftell", Signature(ArgTypes{FilePtrTy}, RetType{LongTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(WithinRange, Range(0, LongMax))},
+ ErrnoUnchanged, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // off_t ftello(FILE *stream);
+ addToFunctionSummaryMap(
+ "ftello", Signature(ArgTypes{FilePtrTy}, RetType{Off_tTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(WithinRange, Range(0, Off_tMax))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int fileno(FILE *stream);
+ addToFunctionSummaryMap(
+ "fileno", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked,
+ GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // void rewind(FILE *stream);
+ // This function indicates error only by setting of 'errno'.
+ addToFunctionSummaryMap("rewind",
+ Signature(ArgTypes{FilePtrTy}, RetType{VoidTy}),
+ Summary(NoEvalCall)
+ .Case({}, ErrnoMustBeChecked)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // void clearerr(FILE *stream);
+ addToFunctionSummaryMap(
+ "clearerr", Signature(ArgTypes{FilePtrTy}, RetType{VoidTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
+
+ // int feof(FILE *stream);
+ addToFunctionSummaryMap(
+ "feof", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
+
+ // int ferror(FILE *stream);
+ addToFunctionSummaryMap(
+ "ferror", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
// long a64l(const char *str64);
addToFunctionSummaryMap(
@@ -1447,16 +2355,32 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(ArgumentCondition(
0, WithinRange, Range(0, LongMax))));
- const auto ReturnsZeroOrMinusOne =
- ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, 0))};
- const auto ReturnsFileDescriptor =
- ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, IntMax))};
+ // int open(const char *path, int oflag, ...);
+ addToFunctionSummaryMap(
+ "open", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked,
+ GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int openat(int fd, const char *path, int oflag, ...);
+ addToFunctionSummaryMap(
+ "openat",
+ Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked,
+ GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
// int access(const char *pathname, int amode);
addToFunctionSummaryMap(
"access", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));
// int faccessat(int dirfd, const char *pathname, int mode, int flags);
@@ -1465,57 +2389,66 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, IntTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));
// int dup(int fildes);
- addToFunctionSummaryMap("dup", Signature(ArgTypes{IntTy}, RetType{IntTy}),
- Summary(NoEvalCall)
- .Case(ReturnsFileDescriptor)
- .ArgConstraint(ArgumentCondition(
- 0, WithinRange, Range(0, IntMax))));
+ addToFunctionSummaryMap(
+ "dup", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked,
+ GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
// int dup2(int fildes1, int filedes2);
addToFunctionSummaryMap(
"dup2", Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsFileDescriptor)
+ .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked,
+ GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(
ArgumentCondition(1, WithinRange, Range(0, IntMax))));
// int fdatasync(int fildes);
- addToFunctionSummaryMap("fdatasync",
- Signature(ArgTypes{IntTy}, RetType{IntTy}),
- Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
- .ArgConstraint(ArgumentCondition(
- 0, WithinRange, Range(0, IntMax))));
+ addToFunctionSummaryMap(
+ "fdatasync", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
// int fnmatch(const char *pattern, const char *string, int flags);
addToFunctionSummaryMap(
"fnmatch",
Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, IntTy},
RetType{IntTy}),
- Summary(EvalCallAsPure)
+ Summary(NoEvalCall)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));
// int fsync(int fildes);
- addToFunctionSummaryMap("fsync", Signature(ArgTypes{IntTy}, RetType{IntTy}),
- Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
- .ArgConstraint(ArgumentCondition(
- 0, WithinRange, Range(0, IntMax))));
-
- Optional<QualType> Off_tTy = lookupTy("off_t");
+ addToFunctionSummaryMap(
+ "fsync", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
// int truncate(const char *path, off_t length);
addToFunctionSummaryMap(
"truncate",
Signature(ArgTypes{ConstCharPtrTy, Off_tTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));
// int symlink(const char *oldpath, const char *newpath);
@@ -1523,7 +2456,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"symlink",
Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));
@@ -1533,26 +2467,30 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{ConstCharPtrTy, IntTy, ConstCharPtrTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0)))
- .ArgConstraint(ArgumentCondition(1, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(1)))
.ArgConstraint(NotNull(ArgNo(2))));
// int lockf(int fd, int cmd, off_t len);
addToFunctionSummaryMap(
"lockf", Signature(ArgTypes{IntTy, IntTy, Off_tTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax))));
- Optional<QualType> Mode_tTy = lookupTy("mode_t");
+ std::optional<QualType> Mode_tTy = lookupTy("mode_t");
// int creat(const char *pathname, mode_t mode);
addToFunctionSummaryMap(
"creat", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsFileDescriptor)
+ .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked,
+ GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));
// unsigned int sleep(unsigned int seconds);
@@ -1562,15 +2500,17 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax))));
- Optional<QualType> DirTy = lookupTy("DIR");
- Optional<QualType> DirPtrTy = getPointerTy(DirTy);
+ std::optional<QualType> DirTy = lookupTy("DIR");
+ std::optional<QualType> DirPtrTy = getPointerTy(DirTy);
// int dirfd(DIR *dirp);
- addToFunctionSummaryMap("dirfd",
- Signature(ArgTypes{DirPtrTy}, RetType{IntTy}),
- Summary(NoEvalCall)
- .Case(ReturnsFileDescriptor)
- .ArgConstraint(NotNull(ArgNo(0))));
+ addToFunctionSummaryMap(
+ "dirfd", Signature(ArgTypes{DirPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked,
+ GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0))));
// unsigned int alarm(unsigned int seconds);
addToFunctionSummaryMap(
@@ -1580,11 +2520,12 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax))));
// int closedir(DIR *dir);
- addToFunctionSummaryMap("closedir",
- Signature(ArgTypes{DirPtrTy}, RetType{IntTy}),
- Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
- .ArgConstraint(NotNull(ArgNo(0))));
+ addToFunctionSummaryMap(
+ "closedir", Signature(ArgTypes{DirPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0))));
// char *strdup(const char *s);
addToFunctionSummaryMap(
@@ -1606,21 +2547,39 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
// int mkstemp(char *template);
- addToFunctionSummaryMap("mkstemp",
- Signature(ArgTypes{CharPtrTy}, RetType{IntTy}),
- Summary(NoEvalCall)
- .Case(ReturnsFileDescriptor)
- .ArgConstraint(NotNull(ArgNo(0))));
+ addToFunctionSummaryMap(
+ "mkstemp", Signature(ArgTypes{CharPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked,
+ GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0))));
// char *mkdtemp(char *template);
addToFunctionSummaryMap(
"mkdtemp", Signature(ArgTypes{CharPtrTy}, RetType{CharPtrTy}),
- Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(BO_EQ, ArgNo(0))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0))));
// char *getcwd(char *buf, size_t size);
addToFunctionSummaryMap(
"getcwd", Signature(ArgTypes{CharPtrTy, SizeTy}, RetType{CharPtrTy}),
Summary(NoEvalCall)
+ .Case({ArgumentCondition(1, WithinRange, Range(1, SizeMax)),
+ ReturnValueCondition(BO_EQ, ArgNo(0))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({ArgumentCondition(1, WithinRange, SingleValue(0)),
+ IsNull(Ret)},
+ ErrnoNEZeroIrrelevant, "Assuming that argument 'size' is 0")
+ .Case({ArgumentCondition(1, WithinRange, Range(1, SizeMax)),
+ IsNull(Ret)},
+ ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(
+ BufferSize(/*Buffer*/ ArgNo(0), /*BufSize*/ ArgNo(1)))
.ArgConstraint(
ArgumentCondition(1, WithinRange, Range(0, SizeMax))));
@@ -1628,7 +2587,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
addToFunctionSummaryMap(
"mkdir", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));
// int mkdirat(int dirfd, const char *pathname, mode_t mode);
@@ -1636,17 +2596,20 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"mkdirat",
Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));
- Optional<QualType> Dev_tTy = lookupTy("dev_t");
+ std::optional<QualType> Dev_tTy = lookupTy("dev_t");
// int mknod(const char *pathname, mode_t mode, dev_t dev);
addToFunctionSummaryMap(
"mknod",
Signature(ArgTypes{ConstCharPtrTy, Mode_tTy, Dev_tTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));
// int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev);
@@ -1655,14 +2618,17 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy, Dev_tTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));
// int chmod(const char *path, mode_t mode);
addToFunctionSummaryMap(
"chmod", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));
// int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags);
@@ -1671,20 +2637,22 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy, IntTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
- .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));
// int fchmod(int fildes, mode_t mode);
addToFunctionSummaryMap(
"fchmod", Signature(ArgTypes{IntTy, Mode_tTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax))));
- Optional<QualType> Uid_tTy = lookupTy("uid_t");
- Optional<QualType> Gid_tTy = lookupTy("gid_t");
+ std::optional<QualType> Uid_tTy = lookupTy("uid_t");
+ std::optional<QualType> Gid_tTy = lookupTy("gid_t");
// int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group,
// int flags);
@@ -1693,8 +2661,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{IntTy, ConstCharPtrTy, Uid_tTy, Gid_tTy, IntTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
- .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));
// int chown(const char *path, uid_t owner, gid_t group);
@@ -1702,7 +2671,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"chown",
Signature(ArgTypes{ConstCharPtrTy, Uid_tTy, Gid_tTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));
// int lchown(const char *path, uid_t owner, gid_t group);
@@ -1710,37 +2680,42 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"lchown",
Signature(ArgTypes{ConstCharPtrTy, Uid_tTy, Gid_tTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));
// int fchown(int fildes, uid_t owner, gid_t group);
addToFunctionSummaryMap(
"fchown", Signature(ArgTypes{IntTy, Uid_tTy, Gid_tTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax))));
// int rmdir(const char *pathname);
- addToFunctionSummaryMap("rmdir",
- Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}),
- Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
- .ArgConstraint(NotNull(ArgNo(0))));
+ addToFunctionSummaryMap(
+ "rmdir", Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0))));
// int chdir(const char *path);
- addToFunctionSummaryMap("chdir",
- Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}),
- Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
- .ArgConstraint(NotNull(ArgNo(0))));
+ addToFunctionSummaryMap(
+ "chdir", Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0))));
// int link(const char *oldpath, const char *newpath);
addToFunctionSummaryMap(
"link",
Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));
@@ -1751,37 +2726,42 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy, IntTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
- .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1)))
- .ArgConstraint(ArgumentCondition(2, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(2)))
.ArgConstraint(NotNull(ArgNo(3))));
// int unlink(const char *pathname);
- addToFunctionSummaryMap("unlink",
- Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}),
- Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
- .ArgConstraint(NotNull(ArgNo(0))));
+ addToFunctionSummaryMap(
+ "unlink", Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0))));
// int unlinkat(int fd, const char *path, int flag);
addToFunctionSummaryMap(
"unlinkat",
Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
- .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));
- Optional<QualType> StructStatTy = lookupTy("stat");
- Optional<QualType> StructStatPtrTy = getPointerTy(StructStatTy);
- Optional<QualType> StructStatPtrRestrictTy = getRestrictTy(StructStatPtrTy);
+ std::optional<QualType> StructStatTy = lookupTy("stat");
+ std::optional<QualType> StructStatPtrTy = getPointerTy(StructStatTy);
+ std::optional<QualType> StructStatPtrRestrictTy =
+ getRestrictTy(StructStatPtrTy);
// int fstat(int fd, struct stat *statbuf);
addToFunctionSummaryMap(
"fstat", Signature(ArgTypes{IntTy, StructStatPtrTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(NotNull(ArgNo(1))));
@@ -1791,7 +2771,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{ConstCharPtrRestrictTy, StructStatPtrRestrictTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));
@@ -1801,7 +2782,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{ConstCharPtrRestrictTy, StructStatPtrRestrictTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));
@@ -1813,32 +2795,40 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
StructStatPtrRestrictTy, IntTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
- .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1)))
.ArgConstraint(NotNull(ArgNo(2))));
// DIR *opendir(const char *name);
addToFunctionSummaryMap(
"opendir", Signature(ArgTypes{ConstCharPtrTy}, RetType{DirPtrTy}),
- Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
+ Summary(NoEvalCall)
+ .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0))));
// DIR *fdopendir(int fd);
- addToFunctionSummaryMap("fdopendir",
- Signature(ArgTypes{IntTy}, RetType{DirPtrTy}),
- Summary(NoEvalCall)
- .ArgConstraint(ArgumentCondition(
- 0, WithinRange, Range(0, IntMax))));
+ addToFunctionSummaryMap(
+ "fdopendir", Signature(ArgTypes{IntTy}, RetType{DirPtrTy}),
+ Summary(NoEvalCall)
+ .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
// int isatty(int fildes);
addToFunctionSummaryMap(
"isatty", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case({ReturnValueCondition(WithinRange, Range(0, 1))})
+ .Case({ReturnValueCondition(WithinRange, Range(0, 1))},
+ ErrnoIrrelevant)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax))));
// FILE *popen(const char *command, const char *type);
+ // FIXME: Improve for errno modeling.
addToFunctionSummaryMap(
"popen",
Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{FilePtrTy}),
@@ -1847,16 +2837,19 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(NotNull(ArgNo(1))));
// int pclose(FILE *stream);
+ // FIXME: Improve for errno modeling.
addToFunctionSummaryMap(
"pclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
// int close(int fildes);
- addToFunctionSummaryMap("close", Signature(ArgTypes{IntTy}, RetType{IntTy}),
- Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
- .ArgConstraint(ArgumentCondition(
- 0, WithinRange, Range(-1, IntMax))));
+ addToFunctionSummaryMap(
+ "close", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(-1, IntMax))));
// long fpathconf(int fildes, int name);
addToFunctionSummaryMap("fpathconf",
@@ -1870,14 +2863,6 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"pathconf", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{LongTy}),
Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
- // FILE *fdopen(int fd, const char *mode);
- addToFunctionSummaryMap(
- "fdopen",
- Signature(ArgTypes{IntTy, ConstCharPtrTy}, RetType{FilePtrTy}),
- Summary(NoEvalCall)
- .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
- .ArgConstraint(NotNull(ArgNo(1))));
-
// void rewinddir(DIR *dir);
addToFunctionSummaryMap(
"rewinddir", Signature(ArgTypes{DirPtrTy}, RetType{VoidTy}),
@@ -1893,28 +2878,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"rand_r", Signature(ArgTypes{UnsignedIntPtrTy}, RetType{IntTy}),
Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
- // int fileno(FILE *stream);
- addToFunctionSummaryMap("fileno",
- Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
- Summary(NoEvalCall)
- .Case(ReturnsFileDescriptor)
- .ArgConstraint(NotNull(ArgNo(0))));
-
- // int fseeko(FILE *stream, off_t offset, int whence);
- addToFunctionSummaryMap(
- "fseeko",
- Signature(ArgTypes{FilePtrTy, Off_tTy, IntTy}, RetType{IntTy}),
- Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
- .ArgConstraint(NotNull(ArgNo(0))));
-
- // off_t ftello(FILE *stream);
- addToFunctionSummaryMap(
- "ftello", Signature(ArgTypes{FilePtrTy}, RetType{Off_tTy}),
- Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
-
// void *mmap(void *addr, size_t length, int prot, int flags, int fd,
// off_t offset);
+ // FIXME: Improve for errno modeling.
addToFunctionSummaryMap(
"mmap",
Signature(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, Off_tTy},
@@ -1924,9 +2890,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(
ArgumentCondition(4, WithinRange, Range(-1, IntMax))));
- Optional<QualType> Off64_tTy = lookupTy("off64_t");
+ std::optional<QualType> Off64_tTy = lookupTy("off64_t");
// void *mmap64(void *addr, size_t length, int prot, int flags, int fd,
// off64_t offset);
+ // FIXME: Improve for errno modeling.
addToFunctionSummaryMap(
"mmap64",
Signature(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, Off64_tTy},
@@ -1937,16 +2904,23 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
ArgumentCondition(4, WithinRange, Range(-1, IntMax))));
// int pipe(int fildes[2]);
- addToFunctionSummaryMap("pipe",
- Signature(ArgTypes{IntPtrTy}, RetType{IntTy}),
- Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
- .ArgConstraint(NotNull(ArgNo(0))));
+ addToFunctionSummaryMap(
+ "pipe", Signature(ArgTypes{IntPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(0))));
// off_t lseek(int fildes, off_t offset, int whence);
+ // In the first case we can not tell for sure if it failed or not.
+ // A return value different from of the expected offset (that is unknown
+ // here) may indicate failure. For this reason we do not enforce the errno
+ // check (can cause false positive).
addToFunctionSummaryMap(
"lseek", Signature(ArgTypes{IntTy, Off_tTy, IntTy}, RetType{Off_tTy}),
Summary(NoEvalCall)
+ .Case(ReturnsNonnegative, ErrnoIrrelevant)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax))));
@@ -1957,8 +2931,15 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy},
RetType{Ssize_tTy}),
Summary(NoEvalCall)
- .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
- ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))})
+ .Case({ArgumentCondition(2, WithinRange, Range(1, IntMax)),
+ ReturnValueCondition(LessThanOrEq, ArgNo(2)),
+ ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({ArgumentCondition(2, WithinRange, SingleValue(0)),
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoMustNotBeChecked,
+ "Assuming that argument 'bufsize' is 0")
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1)))
.ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1),
@@ -1974,9 +2955,16 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
ArgTypes{IntTy, ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy},
RetType{Ssize_tTy}),
Summary(NoEvalCall)
- .Case({ReturnValueCondition(LessThanOrEq, ArgNo(3)),
- ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))})
- .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .Case({ArgumentCondition(3, WithinRange, Range(1, IntMax)),
+ ReturnValueCondition(LessThanOrEq, ArgNo(3)),
+ ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({ArgumentCondition(3, WithinRange, SingleValue(0)),
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoMustNotBeChecked,
+ "Assuming that argument 'bufsize' is 0")
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1)))
.ArgConstraint(NotNull(ArgNo(2)))
.ArgConstraint(BufferSize(/*Buffer=*/ArgNo(2),
@@ -1991,12 +2979,16 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(2)))
.ArgConstraint(NotNull(ArgNo(3))));
// char *realpath(const char *restrict file_name,
// char *restrict resolved_name);
+ // FIXME: Improve for errno modeling.
addToFunctionSummaryMap(
"realpath",
Signature(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy},
@@ -2010,7 +3002,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"execv",
Signature(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case({ReturnValueCondition(WithinRange, SingleValue(-1))})
+ .Case(ReturnsMinusOne, ErrnoIrrelevant)
.ArgConstraint(NotNull(ArgNo(0))));
// int execvp(const char *file, char *const argv[]);
@@ -2018,7 +3010,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"execvp",
Signature(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case({ReturnValueCondition(WithinRange, SingleValue(-1))})
+ .Case(ReturnsMinusOne, ErrnoIrrelevant)
.ArgConstraint(NotNull(ArgNo(0))));
// int getopt(int argc, char * const argv[], const char *optstring);
@@ -2027,23 +3019,26 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{IntTy, CharPtrConstPtr, ConstCharPtrTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case({ReturnValueCondition(WithinRange, Range(-1, UCharRangeMax))})
+ .Case({ReturnValueCondition(WithinRange, Range(-1, UCharRangeMax))},
+ ErrnoIrrelevant)
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(NotNull(ArgNo(1)))
.ArgConstraint(NotNull(ArgNo(2))));
- Optional<QualType> StructSockaddrTy = lookupTy("sockaddr");
- Optional<QualType> StructSockaddrPtrTy = getPointerTy(StructSockaddrTy);
- Optional<QualType> ConstStructSockaddrPtrTy =
+ std::optional<QualType> StructSockaddrTy = lookupTy("sockaddr");
+ std::optional<QualType> StructSockaddrPtrTy =
+ getPointerTy(StructSockaddrTy);
+ std::optional<QualType> ConstStructSockaddrPtrTy =
getPointerTy(getConstTy(StructSockaddrTy));
- Optional<QualType> StructSockaddrPtrRestrictTy =
+ std::optional<QualType> StructSockaddrPtrRestrictTy =
getRestrictTy(StructSockaddrPtrTy);
- Optional<QualType> ConstStructSockaddrPtrRestrictTy =
+ std::optional<QualType> ConstStructSockaddrPtrRestrictTy =
getRestrictTy(ConstStructSockaddrPtrTy);
- Optional<QualType> Socklen_tTy = lookupTy("socklen_t");
- Optional<QualType> Socklen_tPtrTy = getPointerTy(Socklen_tTy);
- Optional<QualType> Socklen_tPtrRestrictTy = getRestrictTy(Socklen_tPtrTy);
- Optional<RangeInt> Socklen_tMax = getMaxValue(Socklen_tTy);
+ std::optional<QualType> Socklen_tTy = lookupTy("socklen_t");
+ std::optional<QualType> Socklen_tPtrTy = getPointerTy(Socklen_tTy);
+ std::optional<QualType> Socklen_tPtrRestrictTy =
+ getRestrictTy(Socklen_tPtrTy);
+ std::optional<RangeInt> Socklen_tMax = getMaxValue(Socklen_tTy);
// In 'socket.h' of some libc implementations with C99, sockaddr parameter
// is a transparent union of the underlying sockaddr_ family of pointers
@@ -2051,9 +3046,20 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
// standardized signature will not match, thus we try to match with another
// signature that has the joker Irrelevant type. We also remove those
// constraints which require pointer types for the sockaddr param.
+
+ // int socket(int domain, int type, int protocol);
+ addToFunctionSummaryMap(
+ "socket", Signature(ArgTypes{IntTy, IntTy, IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked,
+ GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg));
+
auto Accept =
Summary(NoEvalCall)
- .Case(ReturnsFileDescriptor)
+ .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked,
+ GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)));
if (!addToFunctionSummaryMap(
"accept",
@@ -2076,7 +3082,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{IntTy, ConstStructSockaddrPtrTy, Socklen_tTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(NotNull(ArgNo(1)))
@@ -2089,7 +3096,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"bind",
Signature(ArgTypes{IntTy, Irrelevant, Socklen_tTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(
@@ -2103,7 +3111,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Socklen_tPtrRestrictTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(NotNull(ArgNo(1)))
@@ -2113,7 +3122,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax))));
@@ -2125,7 +3135,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Socklen_tPtrRestrictTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(NotNull(ArgNo(1)))
@@ -2135,7 +3146,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax))));
@@ -2146,7 +3158,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{IntTy, ConstStructSockaddrPtrTy, Socklen_tTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(NotNull(ArgNo(1)))))
@@ -2154,14 +3167,20 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"connect",
Signature(ArgTypes{IntTy, Irrelevant, Socklen_tTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax))));
auto Recvfrom =
Summary(NoEvalCall)
.Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
- ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))})
+ ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({ReturnValueCondition(WithinRange, SingleValue(0)),
+ ArgumentCondition(2, WithinRange, SingleValue(0))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1),
/*BufSize=*/ArgNo(2)));
@@ -2186,7 +3205,12 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
auto Sendto =
Summary(NoEvalCall)
.Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
- ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))})
+ ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({ReturnValueCondition(WithinRange, SingleValue(0)),
+ ArgumentCondition(2, WithinRange, SingleValue(0))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1),
/*BufSize=*/ArgNo(2)));
@@ -2207,12 +3231,13 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Sendto);
// int listen(int sockfd, int backlog);
- addToFunctionSummaryMap("listen",
- Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}),
- Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
- .ArgConstraint(ArgumentCondition(
- 0, WithinRange, Range(0, IntMax))));
+ addToFunctionSummaryMap(
+ "listen", Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
// ssize_t recv(int sockfd, void *buf, size_t len, int flags);
addToFunctionSummaryMap(
@@ -2221,14 +3246,19 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
RetType{Ssize_tTy}),
Summary(NoEvalCall)
.Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
- ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))})
+ ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({ReturnValueCondition(WithinRange, SingleValue(0)),
+ ArgumentCondition(2, WithinRange, SingleValue(0))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1),
/*BufSize=*/ArgNo(2))));
- Optional<QualType> StructMsghdrTy = lookupTy("msghdr");
- Optional<QualType> StructMsghdrPtrTy = getPointerTy(StructMsghdrTy);
- Optional<QualType> ConstStructMsghdrPtrTy =
+ std::optional<QualType> StructMsghdrTy = lookupTy("msghdr");
+ std::optional<QualType> StructMsghdrPtrTy = getPointerTy(StructMsghdrTy);
+ std::optional<QualType> ConstStructMsghdrPtrTy =
getPointerTy(getConstTy(StructMsghdrTy));
// ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
@@ -2237,7 +3267,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{IntTy, StructMsghdrPtrTy, IntTy},
RetType{Ssize_tTy}),
Summary(NoEvalCall)
- .Case({ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))})
+ .Case({ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax))));
@@ -2247,7 +3279,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{IntTy, ConstStructMsghdrPtrTy, IntTy},
RetType{Ssize_tTy}),
Summary(NoEvalCall)
- .Case({ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))})
+ .Case({ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax))));
@@ -2258,7 +3292,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{IntTy, IntTy, IntTy, ConstVoidPtrTy, Socklen_tTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(3)))
.ArgConstraint(
BufferSize(/*Buffer=*/ArgNo(3), /*BufSize=*/ArgNo(4)))
@@ -2274,7 +3309,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Socklen_tPtrRestrictTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(3)))
.ArgConstraint(NotNull(ArgNo(4))));
@@ -2285,7 +3321,12 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
RetType{Ssize_tTy}),
Summary(NoEvalCall)
.Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
- ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))})
+ ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({ReturnValueCondition(WithinRange, SingleValue(0)),
+ ArgumentCondition(2, WithinRange, SingleValue(0))},
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1),
/*BufSize=*/ArgNo(2))));
@@ -2295,9 +3336,19 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"socketpair",
Signature(ArgTypes{IntTy, IntTy, IntTy, IntPtrTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(3))));
+ // int shutdown(int socket, int how);
+ addToFunctionSummaryMap(
+ "shutdown", Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
// int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen,
// char *restrict node, socklen_t nodelen,
// char *restrict service,
@@ -2325,20 +3376,22 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(
ArgumentCondition(5, WithinRange, Range(0, Socklen_tMax))));
- Optional<QualType> StructUtimbufTy = lookupTy("utimbuf");
- Optional<QualType> StructUtimbufPtrTy = getPointerTy(StructUtimbufTy);
+ std::optional<QualType> StructUtimbufTy = lookupTy("utimbuf");
+ std::optional<QualType> StructUtimbufPtrTy = getPointerTy(StructUtimbufTy);
// int utime(const char *filename, struct utimbuf *buf);
addToFunctionSummaryMap(
"utime",
Signature(ArgTypes{ConstCharPtrTy, StructUtimbufPtrTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));
- Optional<QualType> StructTimespecTy = lookupTy("timespec");
- Optional<QualType> StructTimespecPtrTy = getPointerTy(StructTimespecTy);
- Optional<QualType> ConstStructTimespecPtrTy =
+ std::optional<QualType> StructTimespecTy = lookupTy("timespec");
+ std::optional<QualType> StructTimespecPtrTy =
+ getPointerTy(StructTimespecTy);
+ std::optional<QualType> ConstStructTimespecPtrTy =
getPointerTy(getConstTy(StructTimespecTy));
// int futimens(int fd, const struct timespec times[2]);
@@ -2346,22 +3399,25 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"futimens",
Signature(ArgTypes{IntTy, ConstStructTimespecPtrTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax))));
// int utimensat(int dirfd, const char *pathname,
// const struct timespec times[2], int flags);
- addToFunctionSummaryMap("utimensat",
- Signature(ArgTypes{IntTy, ConstCharPtrTy,
- ConstStructTimespecPtrTy, IntTy},
- RetType{IntTy}),
- Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
- .ArgConstraint(NotNull(ArgNo(1))));
+ addToFunctionSummaryMap(
+ "utimensat",
+ Signature(
+ ArgTypes{IntTy, ConstCharPtrTy, ConstStructTimespecPtrTy, IntTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
+ .ArgConstraint(NotNull(ArgNo(1))));
- Optional<QualType> StructTimevalTy = lookupTy("timeval");
- Optional<QualType> ConstStructTimevalPtrTy =
+ std::optional<QualType> StructTimevalTy = lookupTy("timeval");
+ std::optional<QualType> ConstStructTimevalPtrTy =
getPointerTy(getConstTy(StructTimevalTy));
// int utimes(const char *filename, const struct timeval times[2]);
@@ -2370,7 +3426,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{ConstCharPtrTy, ConstStructTimevalPtrTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));
// int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
@@ -2379,20 +3436,23 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{ConstStructTimespecPtrTy, StructTimespecPtrTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));
- Optional<QualType> Time_tTy = lookupTy("time_t");
- Optional<QualType> ConstTime_tPtrTy = getPointerTy(getConstTy(Time_tTy));
- Optional<QualType> ConstTime_tPtrRestrictTy =
+ std::optional<QualType> Time_tTy = lookupTy("time_t");
+ std::optional<QualType> ConstTime_tPtrTy =
+ getPointerTy(getConstTy(Time_tTy));
+ std::optional<QualType> ConstTime_tPtrRestrictTy =
getRestrictTy(ConstTime_tPtrTy);
- Optional<QualType> StructTmTy = lookupTy("tm");
- Optional<QualType> StructTmPtrTy = getPointerTy(StructTmTy);
- Optional<QualType> StructTmPtrRestrictTy = getRestrictTy(StructTmPtrTy);
- Optional<QualType> ConstStructTmPtrTy =
+ std::optional<QualType> StructTmTy = lookupTy("tm");
+ std::optional<QualType> StructTmPtrTy = getPointerTy(StructTmTy);
+ std::optional<QualType> StructTmPtrRestrictTy =
+ getRestrictTy(StructTmPtrTy);
+ std::optional<QualType> ConstStructTmPtrTy =
getPointerTy(getConstTy(StructTmTy));
- Optional<QualType> ConstStructTmPtrRestrictTy =
+ std::optional<QualType> ConstStructTmPtrRestrictTy =
getRestrictTy(ConstStructTmPtrTy);
// struct tm * localtime(const time_t *tp);
@@ -2448,46 +3508,54 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"gmtime", Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}),
Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
- Optional<QualType> Clockid_tTy = lookupTy("clockid_t");
+ std::optional<QualType> Clockid_tTy = lookupTy("clockid_t");
// int clock_gettime(clockid_t clock_id, struct timespec *tp);
addToFunctionSummaryMap(
"clock_gettime",
Signature(ArgTypes{Clockid_tTy, StructTimespecPtrTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(1))));
- Optional<QualType> StructItimervalTy = lookupTy("itimerval");
- Optional<QualType> StructItimervalPtrTy = getPointerTy(StructItimervalTy);
+ std::optional<QualType> StructItimervalTy = lookupTy("itimerval");
+ std::optional<QualType> StructItimervalPtrTy =
+ getPointerTy(StructItimervalTy);
// int getitimer(int which, struct itimerval *curr_value);
addToFunctionSummaryMap(
"getitimer",
Signature(ArgTypes{IntTy, StructItimervalPtrTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZeroOrMinusOne)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(1))));
- Optional<QualType> Pthread_cond_tTy = lookupTy("pthread_cond_t");
- Optional<QualType> Pthread_cond_tPtrTy = getPointerTy(Pthread_cond_tTy);
- Optional<QualType> Pthread_tTy = lookupTy("pthread_t");
- Optional<QualType> Pthread_tPtrTy = getPointerTy(Pthread_tTy);
- Optional<QualType> Pthread_tPtrRestrictTy = getRestrictTy(Pthread_tPtrTy);
- Optional<QualType> Pthread_mutex_tTy = lookupTy("pthread_mutex_t");
- Optional<QualType> Pthread_mutex_tPtrTy = getPointerTy(Pthread_mutex_tTy);
- Optional<QualType> Pthread_mutex_tPtrRestrictTy =
+ std::optional<QualType> Pthread_cond_tTy = lookupTy("pthread_cond_t");
+ std::optional<QualType> Pthread_cond_tPtrTy =
+ getPointerTy(Pthread_cond_tTy);
+ std::optional<QualType> Pthread_tTy = lookupTy("pthread_t");
+ std::optional<QualType> Pthread_tPtrTy = getPointerTy(Pthread_tTy);
+ std::optional<QualType> Pthread_tPtrRestrictTy =
+ getRestrictTy(Pthread_tPtrTy);
+ std::optional<QualType> Pthread_mutex_tTy = lookupTy("pthread_mutex_t");
+ std::optional<QualType> Pthread_mutex_tPtrTy =
+ getPointerTy(Pthread_mutex_tTy);
+ std::optional<QualType> Pthread_mutex_tPtrRestrictTy =
getRestrictTy(Pthread_mutex_tPtrTy);
- Optional<QualType> Pthread_attr_tTy = lookupTy("pthread_attr_t");
- Optional<QualType> Pthread_attr_tPtrTy = getPointerTy(Pthread_attr_tTy);
- Optional<QualType> ConstPthread_attr_tPtrTy =
+ std::optional<QualType> Pthread_attr_tTy = lookupTy("pthread_attr_t");
+ std::optional<QualType> Pthread_attr_tPtrTy =
+ getPointerTy(Pthread_attr_tTy);
+ std::optional<QualType> ConstPthread_attr_tPtrTy =
getPointerTy(getConstTy(Pthread_attr_tTy));
- Optional<QualType> ConstPthread_attr_tPtrRestrictTy =
+ std::optional<QualType> ConstPthread_attr_tPtrRestrictTy =
getRestrictTy(ConstPthread_attr_tPtrTy);
- Optional<QualType> Pthread_mutexattr_tTy = lookupTy("pthread_mutexattr_t");
- Optional<QualType> ConstPthread_mutexattr_tPtrTy =
+ std::optional<QualType> Pthread_mutexattr_tTy =
+ lookupTy("pthread_mutexattr_t");
+ std::optional<QualType> ConstPthread_mutexattr_tPtrTy =
getPointerTy(getConstTy(Pthread_mutexattr_tTy));
- Optional<QualType> ConstPthread_mutexattr_tPtrRestrictTy =
+ std::optional<QualType> ConstPthread_mutexattr_tPtrRestrictTy =
getRestrictTy(ConstPthread_mutexattr_tPtrTy);
QualType PthreadStartRoutineTy = getPointerTy(
@@ -2564,12 +3632,24 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
}
// Functions for testing.
- if (ChecksEnabled[CK_StdCLibraryFunctionsTesterChecker]) {
+ if (AddTestFunctions) {
+ const RangeInt IntMin = BVF.getMinValue(IntTy).getLimitedValue();
+
addToFunctionSummaryMap(
"__not_null", Signature(ArgTypes{IntPtrTy}, RetType{IntTy}),
Summary(EvalCallAsPure).ArgConstraint(NotNull(ArgNo(0))));
- // Test range values.
+ addToFunctionSummaryMap(
+ "__not_null_buffer",
+ Signature(ArgTypes{VoidPtrTy, IntTy, IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(NotNullBuffer(ArgNo(0), ArgNo(1), ArgNo(2))));
+
+ // Test inside range constraints.
+ addToFunctionSummaryMap(
+ "__single_val_0", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(0))));
addToFunctionSummaryMap(
"__single_val_1", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
@@ -2578,11 +3658,124 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"__range_1_2", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
.ArgConstraint(ArgumentCondition(0U, WithinRange, Range(1, 2))));
- addToFunctionSummaryMap("__range_1_2__4_5",
+ addToFunctionSummaryMap(
+ "__range_m1_1", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(-1, 1))));
+ addToFunctionSummaryMap(
+ "__range_m2_m1", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(-2, -1))));
+ addToFunctionSummaryMap(
+ "__range_m10_10", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(-10, 10))));
+ addToFunctionSummaryMap("__range_m1_inf",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, Range(-1, IntMax))));
+ addToFunctionSummaryMap("__range_0_inf",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, Range(0, IntMax))));
+ addToFunctionSummaryMap("__range_1_inf",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, Range(1, IntMax))));
+ addToFunctionSummaryMap("__range_minf_m1",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, Range(IntMin, -1))));
+ addToFunctionSummaryMap("__range_minf_0",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, Range(IntMin, 0))));
+ addToFunctionSummaryMap("__range_minf_1",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, Range(IntMin, 1))));
+ addToFunctionSummaryMap("__range_1_2__4_6",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, Range({1, 2}, {4, 6}))));
+ addToFunctionSummaryMap(
+ "__range_1_2__4_inf", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, WithinRange,
+ Range({1, 2}, {4, IntMax}))));
+
+ // Test out of range constraints.
+ addToFunctionSummaryMap(
+ "__single_val_out_0", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(0))));
+ addToFunctionSummaryMap(
+ "__single_val_out_1", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(1))));
+ addToFunctionSummaryMap(
+ "__range_out_1_2", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(1, 2))));
+ addToFunctionSummaryMap(
+ "__range_out_m1_1", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(-1, 1))));
+ addToFunctionSummaryMap(
+ "__range_out_m2_m1", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(-2, -1))));
+ addToFunctionSummaryMap(
+ "__range_out_m10_10", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(-10, 10))));
+ addToFunctionSummaryMap("__range_out_m1_inf",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, OutOfRange, Range(-1, IntMax))));
+ addToFunctionSummaryMap("__range_out_0_inf",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, OutOfRange, Range(0, IntMax))));
+ addToFunctionSummaryMap("__range_out_1_inf",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, OutOfRange, Range(1, IntMax))));
+ addToFunctionSummaryMap("__range_out_minf_m1",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, OutOfRange, Range(IntMin, -1))));
+ addToFunctionSummaryMap("__range_out_minf_0",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, OutOfRange, Range(IntMin, 0))));
+ addToFunctionSummaryMap("__range_out_minf_1",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, OutOfRange, Range(IntMin, 1))));
+ addToFunctionSummaryMap("__range_out_1_2__4_6",
Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
.ArgConstraint(ArgumentCondition(
- 0U, WithinRange, Range({1, 2}, {4, 5}))));
+ 0U, OutOfRange, Range({1, 2}, {4, 6}))));
+ addToFunctionSummaryMap(
+ "__range_out_1_2__4_inf", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(
+ ArgumentCondition(0U, OutOfRange, Range({1, 2}, {4, IntMax}))));
// Test range kind.
addToFunctionSummaryMap(
@@ -2638,18 +3831,48 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"__test_restrict_param_2"},
Signature(ArgTypes{VoidPtrRestrictTy}, RetType{VoidTy}),
Summary(EvalCallAsPure));
- }
- SummariesInitialized = true;
+ // Test the application of cases.
+ addToFunctionSummaryMap(
+ "__test_case_note", Signature(ArgTypes{}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .Case({ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoIrrelevant, "Function returns 0")
+ .Case({ReturnValueCondition(WithinRange, SingleValue(1))},
+ ErrnoIrrelevant, "Function returns 1"));
+ addToFunctionSummaryMap(
+ "__test_case_range_1_2__4_6",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .Case({ArgumentCondition(0U, WithinRange,
+ IntRangeVector{{IntMin, 0}, {3, 3}}),
+ ReturnValueCondition(WithinRange, SingleValue(1))},
+ ErrnoIrrelevant)
+ .Case({ArgumentCondition(0U, WithinRange,
+ IntRangeVector{{3, 3}, {7, IntMax}}),
+ ReturnValueCondition(WithinRange, SingleValue(2))},
+ ErrnoIrrelevant)
+ .Case({ArgumentCondition(0U, WithinRange,
+ IntRangeVector{{IntMin, 0}, {7, IntMax}}),
+ ReturnValueCondition(WithinRange, SingleValue(3))},
+ ErrnoIrrelevant)
+ .Case({ArgumentCondition(
+ 0U, WithinRange,
+ IntRangeVector{{IntMin, 0}, {3, 3}, {7, IntMax}}),
+ ReturnValueCondition(WithinRange, SingleValue(4))},
+ ErrnoIrrelevant));
+ }
}
void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) {
auto *Checker = mgr.registerChecker<StdLibraryFunctionsChecker>();
+ Checker->CheckName = mgr.getCurrentCheckerName();
+ const AnalyzerOptions &Opts = mgr.getAnalyzerOptions();
Checker->DisplayLoadedSummaries =
- mgr.getAnalyzerOptions().getCheckerBooleanOption(
- Checker, "DisplayLoadedSummaries");
- Checker->ModelPOSIX =
- mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, "ModelPOSIX");
+ Opts.getCheckerBooleanOption(Checker, "DisplayLoadedSummaries");
+ Checker->ModelPOSIX = Opts.getCheckerBooleanOption(Checker, "ModelPOSIX");
+ Checker->ShouldAssumeControlledEnvironment =
+ Opts.ShouldAssumeControlledEnvironment;
}
bool ento::shouldRegisterStdCLibraryFunctionsChecker(
@@ -2657,16 +3880,12 @@ bool ento::shouldRegisterStdCLibraryFunctionsChecker(
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)
+void ento::registerStdCLibraryFunctionsTesterChecker(CheckerManager &mgr) {
+ auto *Checker = mgr.getChecker<StdLibraryFunctionsChecker>();
+ Checker->AddTestFunctions = true;
+}
+
+bool ento::shouldRegisterStdCLibraryFunctionsTesterChecker(
+ const CheckerManager &mgr) {
+ return true;
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
new file mode 100644
index 000000000000..f7b7befe28ee
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
@@ -0,0 +1,298 @@
+//===- StdVariantChecker.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 "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/CallDescription.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include <optional>
+#include <string_view>
+
+#include "TaggedUnionModeling.h"
+
+using namespace clang;
+using namespace ento;
+using namespace tagged_union_modeling;
+
+REGISTER_MAP_WITH_PROGRAMSTATE(VariantHeldTypeMap, const MemRegion *, QualType)
+
+namespace clang::ento::tagged_union_modeling {
+
+const CXXConstructorDecl *
+getConstructorDeclarationForCall(const CallEvent &Call) {
+ const auto *ConstructorCall = dyn_cast<CXXConstructorCall>(&Call);
+ if (!ConstructorCall)
+ return nullptr;
+
+ return ConstructorCall->getDecl();
+}
+
+bool isCopyConstructorCall(const CallEvent &Call) {
+ if (const CXXConstructorDecl *ConstructorDecl =
+ getConstructorDeclarationForCall(Call))
+ return ConstructorDecl->isCopyConstructor();
+ return false;
+}
+
+bool isCopyAssignmentCall(const CallEvent &Call) {
+ const Decl *CopyAssignmentDecl = Call.getDecl();
+
+ if (const auto *AsMethodDecl =
+ dyn_cast_or_null<CXXMethodDecl>(CopyAssignmentDecl))
+ return AsMethodDecl->isCopyAssignmentOperator();
+ return false;
+}
+
+bool isMoveConstructorCall(const CallEvent &Call) {
+ const CXXConstructorDecl *ConstructorDecl =
+ getConstructorDeclarationForCall(Call);
+ if (!ConstructorDecl)
+ return false;
+
+ return ConstructorDecl->isMoveConstructor();
+}
+
+bool isMoveAssignmentCall(const CallEvent &Call) {
+ const Decl *CopyAssignmentDecl = Call.getDecl();
+
+ const auto *AsMethodDecl =
+ dyn_cast_or_null<CXXMethodDecl>(CopyAssignmentDecl);
+ if (!AsMethodDecl)
+ return false;
+
+ return AsMethodDecl->isMoveAssignmentOperator();
+}
+
+bool isStdType(const Type *Type, llvm::StringRef TypeName) {
+ auto *Decl = Type->getAsRecordDecl();
+ if (!Decl)
+ return false;
+ return (Decl->getName() == TypeName) && Decl->isInStdNamespace();
+}
+
+bool isStdVariant(const Type *Type) {
+ return isStdType(Type, llvm::StringLiteral("variant"));
+}
+
+} // end of namespace clang::ento::tagged_union_modeling
+
+static std::optional<ArrayRef<TemplateArgument>>
+getTemplateArgsFromVariant(const Type *VariantType) {
+ const auto *TempSpecType = VariantType->getAs<TemplateSpecializationType>();
+ if (!TempSpecType)
+ return {};
+
+ return TempSpecType->template_arguments();
+}
+
+static std::optional<QualType>
+getNthTemplateTypeArgFromVariant(const Type *varType, unsigned i) {
+ std::optional<ArrayRef<TemplateArgument>> VariantTemplates =
+ getTemplateArgsFromVariant(varType);
+ if (!VariantTemplates)
+ return {};
+
+ return (*VariantTemplates)[i].getAsType();
+}
+
+static bool isVowel(char a) {
+ switch (a) {
+ case 'a':
+ case 'e':
+ case 'i':
+ case 'o':
+ case 'u':
+ return true;
+ default:
+ return false;
+ }
+}
+
+static llvm::StringRef indefiniteArticleBasedOnVowel(char a) {
+ if (isVowel(a))
+ return "an";
+ return "a";
+}
+
+class StdVariantChecker : public Checker<eval::Call, check::RegionChanges> {
+ // Call descriptors to find relevant calls
+ CallDescription VariantConstructor{{"std", "variant", "variant"}};
+ CallDescription VariantAssignmentOperator{{"std", "variant", "operator="}};
+ CallDescription StdGet{{"std", "get"}, 1, 1};
+
+ BugType BadVariantType{this, "BadVariantType", "BadVariantType"};
+
+public:
+ ProgramStateRef checkRegionChanges(ProgramStateRef State,
+ const InvalidatedSymbols *,
+ ArrayRef<const MemRegion *>,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *,
+ const CallEvent *Call) const {
+ if (!Call)
+ return State;
+
+ return removeInformationStoredForDeadInstances<VariantHeldTypeMap>(
+ *Call, State, Regions);
+ }
+
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const {
+ // Check if the call was not made from a system header. If it was then
+ // we do an early return because it is part of the implementation.
+ if (Call.isCalledFromSystemHeader())
+ return false;
+
+ if (StdGet.matches(Call))
+ return handleStdGetCall(Call, C);
+
+ // First check if a constructor call is happening. If it is a
+ // constructor call, check if it is an std::variant constructor call.
+ bool IsVariantConstructor =
+ isa<CXXConstructorCall>(Call) && VariantConstructor.matches(Call);
+ bool IsVariantAssignmentOperatorCall =
+ isa<CXXMemberOperatorCall>(Call) &&
+ VariantAssignmentOperator.matches(Call);
+
+ if (IsVariantConstructor || IsVariantAssignmentOperatorCall) {
+ if (Call.getNumArgs() == 0 && IsVariantConstructor) {
+ handleDefaultConstructor(cast<CXXConstructorCall>(&Call), C);
+ return true;
+ }
+
+ // FIXME Later this checker should be extended to handle constructors
+ // with multiple arguments.
+ if (Call.getNumArgs() != 1)
+ return false;
+
+ SVal ThisSVal;
+ if (IsVariantConstructor) {
+ const auto &AsConstructorCall = cast<CXXConstructorCall>(Call);
+ ThisSVal = AsConstructorCall.getCXXThisVal();
+ } else if (IsVariantAssignmentOperatorCall) {
+ const auto &AsMemberOpCall = cast<CXXMemberOperatorCall>(Call);
+ ThisSVal = AsMemberOpCall.getCXXThisVal();
+ } else {
+ return false;
+ }
+
+ handleConstructorAndAssignment<VariantHeldTypeMap>(Call, C, ThisSVal);
+ return true;
+ }
+ return false;
+ }
+
+private:
+ // The default constructed std::variant must be handled separately
+ // by default the std::variant is going to hold a default constructed instance
+ // of the first type of the possible types
+ void handleDefaultConstructor(const CXXConstructorCall *ConstructorCall,
+ CheckerContext &C) const {
+ SVal ThisSVal = ConstructorCall->getCXXThisVal();
+
+ const auto *const ThisMemRegion = ThisSVal.getAsRegion();
+ if (!ThisMemRegion)
+ return;
+
+ std::optional<QualType> DefaultType = getNthTemplateTypeArgFromVariant(
+ ThisSVal.getType(C.getASTContext())->getPointeeType().getTypePtr(), 0);
+ if (!DefaultType)
+ return;
+
+ ProgramStateRef State = ConstructorCall->getState();
+ State = State->set<VariantHeldTypeMap>(ThisMemRegion, *DefaultType);
+ C.addTransition(State);
+ }
+
+ bool handleStdGetCall(const CallEvent &Call, CheckerContext &C) const {
+ ProgramStateRef State = Call.getState();
+
+ const auto &ArgType = Call.getArgSVal(0)
+ .getType(C.getASTContext())
+ ->getPointeeType()
+ .getTypePtr();
+ // We have to make sure that the argument is an std::variant.
+ // There is another std::get with std::pair argument
+ if (!isStdVariant(ArgType))
+ return false;
+
+ // Get the mem region of the argument std::variant and look up the type
+ // information that we know about it.
+ const MemRegion *ArgMemRegion = Call.getArgSVal(0).getAsRegion();
+ const QualType *StoredType = State->get<VariantHeldTypeMap>(ArgMemRegion);
+ if (!StoredType)
+ return false;
+
+ const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr());
+ const FunctionDecl *FD = CE->getDirectCallee();
+ if (FD->getTemplateSpecializationArgs()->size() < 1)
+ return false;
+
+ const auto &TypeOut = FD->getTemplateSpecializationArgs()->asArray()[0];
+ // std::get's first template parameter can be the type we want to get
+ // out of the std::variant or a natural number which is the position of
+ // the requested type in the argument type list of the std::variant's
+ // argument.
+ QualType RetrievedType;
+ switch (TypeOut.getKind()) {
+ case TemplateArgument::ArgKind::Type:
+ RetrievedType = TypeOut.getAsType();
+ break;
+ case TemplateArgument::ArgKind::Integral:
+ // In the natural number case we look up which type corresponds to the
+ // number.
+ if (std::optional<QualType> NthTemplate =
+ getNthTemplateTypeArgFromVariant(
+ ArgType, TypeOut.getAsIntegral().getSExtValue())) {
+ RetrievedType = *NthTemplate;
+ break;
+ }
+ [[fallthrough]];
+ default:
+ return false;
+ }
+
+ QualType RetrievedCanonicalType = RetrievedType.getCanonicalType();
+ QualType StoredCanonicalType = StoredType->getCanonicalType();
+ if (RetrievedCanonicalType == StoredCanonicalType)
+ return true;
+
+ ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
+ if (!ErrNode)
+ return false;
+ llvm::SmallString<128> Str;
+ llvm::raw_svector_ostream OS(Str);
+ std::string StoredTypeName = StoredType->getAsString();
+ std::string RetrievedTypeName = RetrievedType.getAsString();
+ OS << "std::variant " << ArgMemRegion->getDescriptiveName() << " held "
+ << indefiniteArticleBasedOnVowel(StoredTypeName[0]) << " \'"
+ << StoredTypeName << "\', not "
+ << indefiniteArticleBasedOnVowel(RetrievedTypeName[0]) << " \'"
+ << RetrievedTypeName << "\'";
+ auto R = std::make_unique<PathSensitiveBugReport>(BadVariantType, OS.str(),
+ ErrNode);
+ C.emitReport(std::move(R));
+ return true;
+ }
+};
+
+bool clang::ento::shouldRegisterStdVariantChecker(
+ clang::ento::CheckerManager const &mgr) {
+ return true;
+}
+
+void clang::ento::registerStdVariantChecker(clang::ento::CheckerManager &mgr) {
+ mgr.registerChecker<StdVariantChecker>();
+} \ No newline at end of file
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index dd65f8c035aa..07727b339d96 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -14,12 +14,15 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include <functional>
+#include <optional>
using namespace clang;
using namespace ento;
@@ -84,10 +87,10 @@ const StreamErrorState ErrorFError{false, false, true};
/// Full state information about a stream pointer.
struct StreamState {
/// The last file operation called in the stream.
+ /// Can be nullptr.
const FnDescription *LastOperation;
/// 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).
@@ -145,7 +148,7 @@ struct StreamState {
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddPointer(LastOperation);
ID.AddInteger(State);
- ID.AddInteger(ErrorState);
+ ErrorState.Profile(ID);
ID.AddBoolean(FilePositionIndeterminate);
}
};
@@ -201,7 +204,7 @@ ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C,
ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State,
CheckerContext &C, const CallExpr *CE) {
State = State->BindExpr(CE, C.getLocationContext(),
- C.getSValBuilder().makeIntVal(Value, false));
+ C.getSValBuilder().makeIntVal(Value, CE->getType()));
return State;
}
@@ -235,48 +238,96 @@ public:
private:
CallDescriptionMap<FnDescription> FnDescriptions = {
- {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
- {{"freopen", 3},
+ {{{"fopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
+ {{{"fdopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
+ {{{"freopen"}, 3},
{&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
- {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
- {{"fclose", 1},
+ {{{"tmpfile"}, 0}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
+ {{{"fclose"}, 1},
{&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
- {{"fread", 4},
- {&StreamChecker::preFread,
+ {{{"fread"}, 4},
+ {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},
- {{"fwrite", 4},
- {&StreamChecker::preFwrite,
+ {{{"fwrite"}, 4},
+ {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
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},
+ {{{"fgetc"}, 1},
+ {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
+ std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},
+ {{{"fgets"}, 3},
+ {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
+ std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}},
+ {{{"fputc"}, 2},
+ {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
+ std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},
+ {{{"fputs"}, 2},
+ {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
+ std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}},
+ {{{"fprintf"}},
+ {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
+ std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}},
+ {{{"fscanf"}},
+ {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
+ std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}},
+ {{{"ungetc"}, 2},
+ {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false),
+ std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}},
+ {{{"getdelim"}, 4},
+ {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
+ std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 3}},
+ {{{"getline"}, 3},
+ {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true),
+ std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 2}},
+ {{{"fseek"}, 3},
+ {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
+ {{{"fseeko"}, 3},
+ {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
+ {{{"ftell"}, 1},
+ {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
+ {{{"ftello"}, 1},
+ {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
+ {{{"fflush"}, 1},
+ {&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}},
+ {{{"rewind"}, 1},
+ {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
+ {{{"fgetpos"}, 2},
+ {&StreamChecker::preDefault, &StreamChecker::evalFgetpos, 0}},
+ {{{"fsetpos"}, 2},
+ {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
+ {{{"clearerr"}, 1},
{&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
- {{"feof", 1},
+ {{{"feof"}, 1},
{&StreamChecker::preDefault,
std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
0}},
- {{"ferror", 1},
+ {{{"ferror"}, 1},
{&StreamChecker::preDefault,
std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
0}},
- {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0}},
+ {{{"fileno"}, 1}, {&StreamChecker::preDefault, nullptr, 0}},
};
CallDescriptionMap<FnDescription> FnTestDescriptions = {
- {{"StreamTesterChecker_make_feof_stream", 1},
+ {{{"StreamTesterChecker_make_feof_stream"}, 1},
{nullptr,
std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof),
0}},
- {{"StreamTesterChecker_make_ferror_stream", 1},
+ {{{"StreamTesterChecker_make_ferror_stream"}, 1},
{nullptr,
std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
ErrorFError),
0}},
};
+ /// Expanded value of EOF, empty before initialization.
+ mutable std::optional<int> EofVal;
+ /// Expanded value of SEEK_SET, 0 if not found.
+ mutable int SeekSetVal = 0;
+ /// Expanded value of SEEK_CUR, 1 if not found.
+ mutable int SeekCurVal = 1;
+ /// Expanded value of SEEK_END, 2 if not found.
+ mutable int SeekEndVal = 2;
+
void evalFopen(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
@@ -288,20 +339,47 @@ private:
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 preReadWrite(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C, bool IsRead) const;
void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C, bool IsFread) const;
+ void evalFgetx(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C, bool SingleChar) const;
+
+ void evalFputx(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C, bool IsSingleChar) const;
+
+ void evalFprintf(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void evalFscanf(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void evalUngetc(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void evalGetdelim(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
void preFseek(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
void evalFseek(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
+ void evalFgetpos(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void evalFsetpos(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void evalFtell(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void evalRewind(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
void preDefault(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
@@ -316,6 +394,12 @@ private:
CheckerContext &C,
const StreamErrorState &ErrorKind) const;
+ void preFflush(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void evalFflush(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) 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
@@ -367,7 +451,7 @@ private:
// (and matching name) as stream functions.
if (!Call.isGlobalCFunction())
return nullptr;
- for (auto P : Call.parameters()) {
+ for (auto *P : Call.parameters()) {
QualType T = P->getType();
if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
return nullptr;
@@ -378,23 +462,14 @@ private:
/// Generate a message for BugReporterVisitor if the stored symbol is
/// marked as interesting by the actual bug report.
- // FIXME: Use lambda instead.
- struct NoteFn {
- const BugType *BT_ResourceLeak;
- SymbolRef StreamSym;
- std::string Message;
-
- std::string operator()(PathSensitiveBugReport &BR) const {
- if (BR.isInteresting(StreamSym) && &BR.getBugType() == BT_ResourceLeak)
- return Message;
-
- return "";
- }
- };
-
const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym,
const std::string &Message) const {
- return C.getNoteTag(NoteFn{&BT_ResourceLeak, StreamSym, Message});
+ return C.getNoteTag([this, StreamSym,
+ Message](PathSensitiveBugReport &BR) -> std::string {
+ if (BR.isInteresting(StreamSym) && &BR.getBugType() == &BT_ResourceLeak)
+ return Message;
+ return "";
+ });
}
const NoteTag *constructSetEofNoteTag(CheckerContext &C,
@@ -410,6 +485,26 @@ private:
});
}
+ void initMacroValues(CheckerContext &C) const {
+ if (EofVal)
+ return;
+
+ if (const std::optional<int> OptInt =
+ tryExpandAsInteger("EOF", C.getPreprocessor()))
+ EofVal = *OptInt;
+ else
+ EofVal = -1;
+ if (const std::optional<int> OptInt =
+ tryExpandAsInteger("SEEK_SET", C.getPreprocessor()))
+ SeekSetVal = *OptInt;
+ if (const std::optional<int> OptInt =
+ tryExpandAsInteger("SEEK_END", C.getPreprocessor()))
+ SeekEndVal = *OptInt;
+ if (const std::optional<int> OptInt =
+ tryExpandAsInteger("SEEK_CUR", C.getPreprocessor()))
+ SeekCurVal = *OptInt;
+ }
+
/// Searches for the ExplodedNode where the file descriptor was acquired for
/// StreamSym.
static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,
@@ -425,8 +520,7 @@ private:
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?");
+ assert(SS->isOpened() && "Stream is expected to be opened");
}
const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
@@ -456,6 +550,8 @@ const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
void StreamChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
+ initMacroValues(C);
+
const FnDescription *Desc = lookupFn(Call);
if (!Desc || !Desc->PreFn)
return;
@@ -525,7 +621,7 @@ void StreamChecker::evalFreopen(const FnDescription *Desc,
if (!CE)
return;
- Optional<DefinedSVal> StreamVal =
+ std::optional<DefinedSVal> StreamVal =
getStreamArg(Desc, Call).getAs<DefinedSVal>();
if (!StreamVal)
return;
@@ -548,8 +644,9 @@ void StreamChecker::evalFreopen(const FnDescription *Desc,
State->BindExpr(CE, C.getLocationContext(), *StreamVal);
// Generate state for NULL return value.
// Stream switches to OpenFailed state.
- ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(),
- C.getSValBuilder().makeNull());
+ ProgramStateRef StateRetNull =
+ State->BindExpr(CE, C.getLocationContext(),
+ C.getSValBuilder().makeNullWithType(CE->getType()));
StateRetNotNull =
StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
@@ -572,6 +669,10 @@ void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
if (!SS)
return;
+ auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
assertStreamStateOpened(SS);
// Close the File Descriptor.
@@ -579,11 +680,21 @@ void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
// and can not be used any more.
State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc));
- C.addTransition(State);
+ // Return 0 on success, EOF on failure.
+ SValBuilder &SVB = C.getSValBuilder();
+ ProgramStateRef StateSuccess = State->BindExpr(
+ CE, C.getLocationContext(), SVB.makeIntVal(0, C.getASTContext().IntTy));
+ ProgramStateRef StateFailure =
+ State->BindExpr(CE, C.getLocationContext(),
+ SVB.makeIntVal(*EofVal, C.getASTContext().IntTy));
+
+ C.addTransition(StateSuccess);
+ C.addTransition(StateFailure);
}
-void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call,
- CheckerContext &C) const {
+void StreamChecker::preReadWrite(const FnDescription *Desc,
+ const CallEvent &Call, CheckerContext &C,
+ bool IsRead) const {
ProgramStateRef State = C.getState();
SVal StreamVal = getStreamArg(Desc, Call);
State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
@@ -597,6 +708,11 @@ void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call,
if (!State)
return;
+ if (!IsRead) {
+ C.addTransition(State);
+ return;
+ }
+
SymbolRef Sym = StreamVal.getAsSymbol();
if (Sym && State->get<StreamMap>(Sym)) {
const StreamState *SS = State->get<StreamMap>(Sym);
@@ -607,24 +723,6 @@ void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call,
}
}
-void StreamChecker::preFwrite(const FnDescription *Desc, const CallEvent &Call,
- CheckerContext &C) const {
- ProgramStateRef State = C.getState();
- SVal StreamVal = getStreamArg(Desc, Call);
- State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), 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 {
@@ -637,10 +735,10 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
if (!CE)
return;
- Optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
+ std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
if (!SizeVal)
return;
- Optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
+ std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
if (!NMembVal)
return;
@@ -670,24 +768,19 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
if (!IsFread || (OldSS->ErrorState != ErrorFEof)) {
ProgramStateRef StateNotFailed =
State->BindExpr(CE, C.getLocationContext(), *NMembVal);
- if (StateNotFailed) {
- StateNotFailed = StateNotFailed->set<StreamMap>(
- StreamSym, StreamState::getOpened(Desc));
- C.addTransition(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.");
+ NonLoc RetVal = makeRetVal(C, CE).castAs<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>();
+ State->BindExpr(CE, C.getLocationContext(), RetVal);
+ SValBuilder &SVB = C.getSValBuilder();
+ auto Cond =
+ SVB.evalBinOpNN(State, BO_LT, RetVal, *NMembVal, SVB.getConditionType())
+ .getAs<DefinedOrUnknownSVal>();
if (!Cond)
return;
StateFailed = StateFailed->assume(*Cond, true);
@@ -710,6 +803,351 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
C.addTransition(StateFailed);
}
+void StreamChecker::evalFgetx(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C, bool SingleChar) 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 *OldSS = State->get<StreamMap>(StreamSym);
+ if (!OldSS)
+ return;
+
+ assertStreamStateOpened(OldSS);
+
+ // `fgetc` returns the read character on success, otherwise returns EOF.
+ // `fgets` returns the read buffer address on success, otherwise returns NULL.
+
+ if (OldSS->ErrorState != ErrorFEof) {
+ if (SingleChar) {
+ // Generate a transition for the success state of `fgetc`.
+ NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
+ ProgramStateRef StateNotFailed =
+ State->BindExpr(CE, C.getLocationContext(), RetVal);
+ SValBuilder &SVB = C.getSValBuilder();
+ ASTContext &ASTC = C.getASTContext();
+ // The returned 'unsigned char' of `fgetc` is converted to 'int',
+ // so we need to check if it is in range [0, 255].
+ auto CondLow =
+ SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy),
+ SVB.getConditionType())
+ .getAs<DefinedOrUnknownSVal>();
+ auto CondHigh =
+ SVB.evalBinOp(State, BO_LE, RetVal,
+ SVB.makeIntVal(SVB.getBasicValueFactory()
+ .getMaxValue(ASTC.UnsignedCharTy)
+ .getLimitedValue(),
+ ASTC.IntTy),
+ SVB.getConditionType())
+ .getAs<DefinedOrUnknownSVal>();
+ if (!CondLow || !CondHigh)
+ return;
+ StateNotFailed = StateNotFailed->assume(*CondLow, true);
+ if (!StateNotFailed)
+ return;
+ StateNotFailed = StateNotFailed->assume(*CondHigh, true);
+ if (!StateNotFailed)
+ return;
+ C.addTransition(StateNotFailed);
+ } else {
+ // Generate a transition for the success state of `fgets`.
+ std::optional<DefinedSVal> GetBuf =
+ Call.getArgSVal(0).getAs<DefinedSVal>();
+ if (!GetBuf)
+ return;
+ ProgramStateRef StateNotFailed =
+ State->BindExpr(CE, C.getLocationContext(), *GetBuf);
+ StateNotFailed = StateNotFailed->set<StreamMap>(
+ StreamSym, StreamState::getOpened(Desc));
+ C.addTransition(StateNotFailed);
+ }
+ }
+
+ // Add transition for the failed state.
+ ProgramStateRef StateFailed;
+ if (SingleChar)
+ StateFailed = bindInt(*EofVal, State, C, CE);
+ else
+ StateFailed =
+ State->BindExpr(CE, C.getLocationContext(),
+ C.getSValBuilder().makeNullWithType(CE->getType()));
+
+ // If a (non-EOF) error occurs, the resulting value of the file position
+ // indicator for the stream is indeterminate.
+ StreamErrorState NewES =
+ OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError;
+ StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
+ StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
+ if (OldSS->ErrorState != ErrorFEof)
+ C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
+ else
+ C.addTransition(StateFailed);
+}
+
+void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C, bool IsSingleChar) 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 *OldSS = State->get<StreamMap>(StreamSym);
+ if (!OldSS)
+ return;
+
+ assertStreamStateOpened(OldSS);
+
+ // `fputc` returns the written character on success, otherwise returns EOF.
+ // `fputs` returns a non negative value on sucecess, otherwise returns EOF.
+
+ if (IsSingleChar) {
+ // Generate a transition for the success state of `fputc`.
+ std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>();
+ if (!PutVal)
+ return;
+ ProgramStateRef StateNotFailed =
+ State->BindExpr(CE, C.getLocationContext(), *PutVal);
+ StateNotFailed =
+ StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
+ C.addTransition(StateNotFailed);
+ } else {
+ // Generate a transition for the success state of `fputs`.
+ NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
+ ProgramStateRef StateNotFailed =
+ State->BindExpr(CE, C.getLocationContext(), RetVal);
+ SValBuilder &SVB = C.getSValBuilder();
+ auto &ASTC = C.getASTContext();
+ auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy),
+ SVB.getConditionType())
+ .getAs<DefinedOrUnknownSVal>();
+ if (!Cond)
+ return;
+ StateNotFailed = StateNotFailed->assume(*Cond, true);
+ if (!StateNotFailed)
+ return;
+ StateNotFailed =
+ StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
+ C.addTransition(StateNotFailed);
+ }
+
+ // Add transition for the failed state. The resulting value of the file
+ // position indicator for the stream is indeterminate.
+ ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE);
+ StreamState NewSS = StreamState::getOpened(Desc, ErrorFError, true);
+ StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
+ C.addTransition(StateFailed);
+}
+
+void StreamChecker::evalFprintf(const FnDescription *Desc,
+ const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ if (Call.getNumArgs() < 2)
+ return;
+ SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
+ if (!StreamSym)
+ return;
+
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ const StreamState *OldSS = State->get<StreamMap>(StreamSym);
+ if (!OldSS)
+ return;
+
+ assertStreamStateOpened(OldSS);
+
+ NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
+ State = State->BindExpr(CE, C.getLocationContext(), RetVal);
+ SValBuilder &SVB = C.getSValBuilder();
+ auto &ACtx = C.getASTContext();
+ auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ACtx.IntTy),
+ SVB.getConditionType())
+ .getAs<DefinedOrUnknownSVal>();
+ if (!Cond)
+ return;
+ ProgramStateRef StateNotFailed, StateFailed;
+ std::tie(StateNotFailed, StateFailed) = State->assume(*Cond);
+
+ StateNotFailed =
+ StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
+ C.addTransition(StateNotFailed);
+
+ // Add transition for the failed state. The resulting value of the file
+ // position indicator for the stream is indeterminate.
+ StateFailed = StateFailed->set<StreamMap>(
+ StreamSym, StreamState::getOpened(Desc, ErrorFError, true));
+ C.addTransition(StateFailed);
+}
+
+void StreamChecker::evalFscanf(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ if (Call.getNumArgs() < 2)
+ return;
+ SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
+ if (!StreamSym)
+ return;
+
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ const StreamState *OldSS = State->get<StreamMap>(StreamSym);
+ if (!OldSS)
+ return;
+
+ assertStreamStateOpened(OldSS);
+
+ SValBuilder &SVB = C.getSValBuilder();
+ ASTContext &ACtx = C.getASTContext();
+
+ // Add the success state.
+ // In this context "success" means there is not an EOF or other read error
+ // before any item is matched in 'fscanf'. But there may be match failure,
+ // therefore return value can be 0 or greater.
+ // It is not specified what happens if some items (not all) are matched and
+ // then EOF or read error happens. Now this case is handled like a "success"
+ // case, and no error flags are set on the stream. This is probably not
+ // accurate, and the POSIX documentation does not tell more.
+ if (OldSS->ErrorState != ErrorFEof) {
+ NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
+ ProgramStateRef StateNotFailed =
+ State->BindExpr(CE, C.getLocationContext(), RetVal);
+ auto RetGeZero =
+ SVB.evalBinOp(StateNotFailed, BO_GE, RetVal,
+ SVB.makeZeroVal(ACtx.IntTy), SVB.getConditionType())
+ .getAs<DefinedOrUnknownSVal>();
+ if (!RetGeZero)
+ return;
+ StateNotFailed = StateNotFailed->assume(*RetGeZero, true);
+
+ C.addTransition(StateNotFailed);
+ }
+
+ // Add transition for the failed state.
+ // Error occurs if nothing is matched yet and reading the input fails.
+ // Error can be EOF, or other error. At "other error" FERROR or 'errno' can
+ // be set but it is not further specified if all are required to be set.
+ // Documentation does not mention, but file position will be set to
+ // indeterminate similarly as at 'fread'.
+ ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE);
+ StreamErrorState NewES = (OldSS->ErrorState == ErrorFEof)
+ ? ErrorFEof
+ : ErrorNone | ErrorFEof | ErrorFError;
+ StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
+ StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
+ if (OldSS->ErrorState != ErrorFEof)
+ C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
+ else
+ C.addTransition(StateFailed);
+}
+
+void StreamChecker::evalUngetc(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;
+
+ const StreamState *OldSS = State->get<StreamMap>(StreamSym);
+ if (!OldSS)
+ return;
+
+ assertStreamStateOpened(OldSS);
+
+ // Generate a transition for the success state.
+ std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>();
+ if (!PutVal)
+ return;
+ ProgramStateRef StateNotFailed =
+ State->BindExpr(CE, C.getLocationContext(), *PutVal);
+ StateNotFailed =
+ StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
+ C.addTransition(StateNotFailed);
+
+ // Add transition for the failed state.
+ // Failure of 'ungetc' does not result in feof or ferror state.
+ // If the PutVal has value of EofVal the function should "fail", but this is
+ // the same transition as the success state.
+ // In this case only one state transition is added by the analyzer (the two
+ // new states may be similar).
+ ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE);
+ StateFailed =
+ StateFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
+ C.addTransition(StateFailed);
+}
+
+void StreamChecker::evalGetdelim(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;
+
+ const StreamState *OldSS = State->get<StreamMap>(StreamSym);
+ if (!OldSS)
+ return;
+
+ assertStreamStateOpened(OldSS);
+
+ // Upon successful completion, the getline() and getdelim() functions shall
+ // return the number of bytes written into the buffer.
+ // If the end-of-file indicator for the stream is set, the function shall
+ // return -1.
+ // If an error occurs, the function shall return -1 and set 'errno'.
+
+ // Add transition for the successful state.
+ if (OldSS->ErrorState != ErrorFEof) {
+ NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
+ ProgramStateRef StateNotFailed =
+ State->BindExpr(CE, C.getLocationContext(), RetVal);
+ SValBuilder &SVB = C.getSValBuilder();
+ auto Cond =
+ SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(CE->getType()),
+ SVB.getConditionType())
+ .getAs<DefinedOrUnknownSVal>();
+ if (!Cond)
+ return;
+ StateNotFailed = StateNotFailed->assume(*Cond, true);
+ if (!StateNotFailed)
+ return;
+ C.addTransition(StateNotFailed);
+ }
+
+ // Add transition for the failed state.
+ // If a (non-EOF) error occurs, the resulting value of the file position
+ // indicator for the stream is indeterminate.
+ ProgramStateRef StateFailed = bindInt(-1, State, C, CE);
+ StreamErrorState NewES =
+ OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError;
+ StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
+ StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
+ if (OldSS->ErrorState != ErrorFEof)
+ C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
+ else
+ C.addTransition(StateFailed);
+}
+
void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
@@ -743,6 +1181,11 @@ void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
if (!State->get<StreamMap>(StreamSym))
return;
+ const llvm::APSInt *PosV =
+ C.getSValBuilder().getKnownValue(State, Call.getArgSVal(1));
+ const llvm::APSInt *WhenceV =
+ C.getSValBuilder().getKnownValue(State, Call.getArgSVal(2));
+
DefinedSVal RetVal = makeRetVal(C, CE);
// Make expression result.
@@ -761,14 +1204,145 @@ void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
// 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.
+ StreamErrorState NewErrS = ErrorNone | ErrorFError;
+ // Setting the position to start of file never produces EOF error.
+ if (!(PosV && *PosV == 0 && WhenceV && *WhenceV == SeekSetVal))
+ NewErrS = NewErrS | ErrorFEof;
StateFailed = StateFailed->set<StreamMap>(
- StreamSym,
- StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError, true));
+ StreamSym, StreamState::getOpened(Desc, NewErrS, true));
C.addTransition(StateNotFailed);
C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
}
+void StreamChecker::evalFgetpos(const FnDescription *Desc,
+ const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
+ if (!Sym)
+ return;
+
+ // Do not evaluate if stream is not found.
+ if (!State->get<StreamMap>(Sym))
+ return;
+
+ auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ DefinedSVal RetVal = makeRetVal(C, CE);
+ State = State->BindExpr(CE, C.getLocationContext(), RetVal);
+ ProgramStateRef StateNotFailed, StateFailed;
+ std::tie(StateFailed, StateNotFailed) =
+ C.getConstraintManager().assumeDual(State, RetVal);
+
+ // This function does not affect the stream state.
+ // Still we add success and failure state with the appropriate return value.
+ // StdLibraryFunctionsChecker can change these states (set the 'errno' state).
+ C.addTransition(StateNotFailed);
+ C.addTransition(StateFailed);
+}
+
+void StreamChecker::evalFsetpos(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;
+
+ auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ assertStreamStateOpened(SS);
+
+ DefinedSVal RetVal = makeRetVal(C, CE);
+ State = State->BindExpr(CE, C.getLocationContext(), RetVal);
+ ProgramStateRef StateNotFailed, StateFailed;
+ std::tie(StateFailed, StateNotFailed) =
+ C.getConstraintManager().assumeDual(State, RetVal);
+
+ StateNotFailed = StateNotFailed->set<StreamMap>(
+ StreamSym, StreamState::getOpened(Desc, ErrorNone, false));
+
+ // At failure ferror could be set.
+ // The standards do not tell what happens with the file position at failure.
+ // But we can assume that it is dangerous to make a next I/O operation after
+ // the position was not set correctly (similar to 'fseek').
+ StateFailed = StateFailed->set<StreamMap>(
+ StreamSym, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));
+
+ C.addTransition(StateNotFailed);
+ C.addTransition(StateFailed);
+}
+
+void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
+ if (!Sym)
+ return;
+
+ if (!State->get<StreamMap>(Sym))
+ return;
+
+ auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ SValBuilder &SVB = C.getSValBuilder();
+ NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
+ ProgramStateRef StateNotFailed =
+ State->BindExpr(CE, C.getLocationContext(), RetVal);
+ auto Cond =
+ SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(Call.getResultType()),
+ SVB.getConditionType())
+ .getAs<DefinedOrUnknownSVal>();
+ if (!Cond)
+ return;
+ StateNotFailed = StateNotFailed->assume(*Cond, true);
+ if (!StateNotFailed)
+ return;
+
+ ProgramStateRef StateFailed = State->BindExpr(
+ CE, C.getLocationContext(), SVB.makeIntVal(-1, Call.getResultType()));
+
+ // This function does not affect the stream state.
+ // Still we add success and failure state with the appropriate return value.
+ // StdLibraryFunctionsChecker can change these states (set the 'errno' state).
+ C.addTransition(StateNotFailed);
+ C.addTransition(StateFailed);
+}
+
+void StreamChecker::evalRewind(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;
+
+ auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ assertStreamStateOpened(SS);
+
+ State = State->set<StreamMap>(StreamSym,
+ StreamState::getOpened(Desc, ErrorNone, false));
+
+ C.addTransition(State);
+}
+
void StreamChecker::evalClearerr(const FnDescription *Desc,
const CallEvent &Call,
CheckerContext &C) const {
@@ -858,6 +1432,84 @@ void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
C.addTransition(State);
}
+void StreamChecker::preFflush(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SVal StreamVal = getStreamArg(Desc, Call);
+ std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>();
+ if (!Stream)
+ return;
+
+ ProgramStateRef StateNotNull, StateNull;
+ std::tie(StateNotNull, StateNull) =
+ C.getConstraintManager().assumeDual(State, *Stream);
+ if (StateNotNull && !StateNull)
+ ensureStreamOpened(StreamVal, C, StateNotNull);
+}
+
+void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SVal StreamVal = getStreamArg(Desc, Call);
+ std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>();
+ if (!Stream)
+ return;
+
+ // Skip if the stream can be both NULL and non-NULL.
+ ProgramStateRef StateNotNull, StateNull;
+ std::tie(StateNotNull, StateNull) =
+ C.getConstraintManager().assumeDual(State, *Stream);
+ if (StateNotNull && StateNull)
+ return;
+ if (StateNotNull && !StateNull)
+ State = StateNotNull;
+ else
+ State = StateNull;
+
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ // `fflush` returns EOF on failure, otherwise returns 0.
+ ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE);
+ ProgramStateRef StateNotFailed = bindInt(0, State, C, CE);
+
+ // Clear error states if `fflush` returns 0, but retain their EOF flags.
+ auto ClearErrorInNotFailed = [&StateNotFailed, Desc](SymbolRef Sym,
+ const StreamState *SS) {
+ if (SS->ErrorState & ErrorFError) {
+ StreamErrorState NewES =
+ (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone;
+ StreamState NewSS = StreamState::getOpened(Desc, NewES, false);
+ StateNotFailed = StateNotFailed->set<StreamMap>(Sym, NewSS);
+ }
+ };
+
+ if (StateNotNull && !StateNull) {
+ // Skip if the input stream's state is unknown, open-failed or closed.
+ if (SymbolRef StreamSym = StreamVal.getAsSymbol()) {
+ const StreamState *SS = State->get<StreamMap>(StreamSym);
+ if (SS) {
+ assert(SS->isOpened() && "Stream is expected to be opened");
+ ClearErrorInNotFailed(StreamSym, SS);
+ } else
+ return;
+ }
+ } else {
+ // Clear error states for all streams.
+ const StreamMapTy &Map = StateNotFailed->get<StreamMap>();
+ for (const auto &I : Map) {
+ SymbolRef Sym = I.first;
+ const StreamState &SS = I.second;
+ if (SS.isOpened())
+ ClearErrorInNotFailed(Sym, &SS);
+ }
+ }
+
+ C.addTransition(StateNotFailed);
+ C.addTransition(StateFailed);
+}
+
ProgramStateRef
StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
CheckerContext &C,
@@ -869,7 +1521,7 @@ StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
ConstraintManager &CM = C.getConstraintManager();
ProgramStateRef StateNotNull, StateNull;
- std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream);
+ std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream);
if (!StateNotNull && StateNull) {
if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
@@ -925,7 +1577,6 @@ ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,
N));
return nullptr;
}
- return State;
}
return State;
@@ -979,12 +1630,13 @@ ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(
ProgramStateRef
StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
ProgramStateRef State) const {
- Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>();
+ std::optional<nonloc::ConcreteInt> CI =
+ WhenceVal.getAs<nonloc::ConcreteInt>();
if (!CI)
return State;
int64_t X = CI->getValue().getSExtValue();
- if (X >= 0 && X <= 2)
+ if (X == SeekSetVal || X == SeekCurVal || X == SeekEndVal)
return State;
if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
@@ -1035,10 +1687,12 @@ StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
// FIXME: Add a checker option to turn this uniqueing feature off.
const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C);
assert(StreamOpenNode && "Could not find place of stream opening.");
- PathDiagnosticLocation LocUsedForUniqueing =
- PathDiagnosticLocation::createBegin(
- StreamOpenNode->getStmtForDiagnostics(), C.getSourceManager(),
- StreamOpenNode->getLocationContext());
+
+ PathDiagnosticLocation LocUsedForUniqueing;
+ if (const Stmt *StreamStmt = StreamOpenNode->getStmtForDiagnostics())
+ LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
+ StreamStmt, C.getSourceManager(),
+ StreamOpenNode->getLocationContext());
std::unique_ptr<PathSensitiveBugReport> R =
std::make_unique<PathSensitiveBugReport>(
@@ -1118,4 +1772,4 @@ void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {
return true;
-} \ No newline at end of file
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp
new file mode 100644
index 000000000000..2dc9e29ca906
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp
@@ -0,0 +1,105 @@
+//=== StringChecker.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 implements the modeling of the std::basic_string type.
+// This involves checking preconditions of the operations and applying the
+// effects of the operations, e.g. their post-conditions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class StringChecker : public Checker<check::PreCall> {
+ BugType BT_Null{this, "Dereference of null pointer", categories::LogicError};
+ mutable const FunctionDecl *StringConstCharPtrCtor = nullptr;
+ mutable CanQualType SizeTypeTy;
+ const CallDescription TwoParamStdStringCtor = {
+ {"std", "basic_string", "basic_string"}, 2, 2};
+
+ bool isCharToStringCtor(const CallEvent &Call, const ASTContext &ACtx) const;
+
+public:
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+};
+
+bool StringChecker::isCharToStringCtor(const CallEvent &Call,
+ const ASTContext &ACtx) const {
+ if (!TwoParamStdStringCtor.matches(Call))
+ return false;
+ const auto *FD = dyn_cast<FunctionDecl>(Call.getDecl());
+ assert(FD);
+
+ // See if we already cached it.
+ if (StringConstCharPtrCtor && StringConstCharPtrCtor == FD)
+ return true;
+
+ // Verify that the parameters have the expected types:
+ // - arg 1: `const CharT *`
+ // - arg 2: some allocator - which is definately not `size_t`.
+ const QualType Arg1Ty = Call.getArgExpr(0)->getType().getCanonicalType();
+ const QualType Arg2Ty = Call.getArgExpr(1)->getType().getCanonicalType();
+
+ if (!Arg1Ty->isPointerType())
+ return false;
+
+ // It makes sure that we don't select the `string(const char* p, size_t len)`
+ // overload accidentally.
+ if (Arg2Ty.getCanonicalType() == ACtx.getSizeType())
+ return false;
+
+ StringConstCharPtrCtor = FD; // Cache the decl of the right overload.
+ return true;
+}
+
+void StringChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (!isCharToStringCtor(Call, C.getASTContext()))
+ return;
+ const auto Param = Call.getArgSVal(0).getAs<Loc>();
+ if (!Param)
+ return;
+
+ // We managed to constrain the parameter to non-null.
+ ProgramStateRef NotNull, Null;
+ std::tie(NotNull, Null) = C.getState()->assume(*Param);
+
+ if (NotNull) {
+ const auto Callback = [Param](PathSensitiveBugReport &BR) -> std::string {
+ return BR.isInteresting(*Param) ? "Assuming the pointer is not null."
+ : "";
+ };
+
+ // Emit note only if this operation constrained the pointer to be null.
+ C.addTransition(NotNull, Null ? C.getNoteTag(Callback) : nullptr);
+ return;
+ }
+
+ // We found a path on which the parameter is NULL.
+ if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
+ auto R = std::make_unique<PathSensitiveBugReport>(
+ BT_Null, "The parameter must not be null", N);
+ bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *R);
+ C.emitReport(std::move(R));
+ }
+}
+
+} // end anonymous namespace
+
+void ento::registerStringChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<StringChecker>();
+}
+
+bool ento::shouldRegisterStringChecker(const CheckerManager &) { return true; }
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h
new file mode 100644
index 000000000000..6de33da107a3
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h
@@ -0,0 +1,99 @@
+//===- TaggedUnionModeling.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_LIB_STATICANALYZER_CHECKERS_TAGGEDUNIONMODELING_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAGGEDUNIONMODELING_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/CallDescription.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/FoldingSet.h"
+#include <numeric>
+
+namespace clang::ento::tagged_union_modeling {
+
+// The implementation of all these functions can be found in the file
+// StdVariantChecker.cpp under the same directory as this file.
+
+bool isCopyConstructorCall(const CallEvent &Call);
+bool isCopyAssignmentCall(const CallEvent &Call);
+bool isMoveAssignmentCall(const CallEvent &Call);
+bool isMoveConstructorCall(const CallEvent &Call);
+bool isStdType(const Type *Type, const std::string &TypeName);
+bool isStdVariant(const Type *Type);
+
+// When invalidating regions, we also have to follow that by invalidating the
+// corresponding custom data in the program state.
+template <class TypeMap>
+ProgramStateRef
+removeInformationStoredForDeadInstances(const CallEvent &Call,
+ ProgramStateRef State,
+ ArrayRef<const MemRegion *> Regions) {
+ // If we do not know anything about the call we shall not continue.
+ // If the call is happens within a system header it is implementation detail.
+ // We should not take it into consideration.
+ if (Call.isInSystemHeader())
+ return State;
+
+ for (const MemRegion *Region : Regions)
+ State = State->remove<TypeMap>(Region);
+
+ return State;
+}
+
+template <class TypeMap>
+void handleConstructorAndAssignment(const CallEvent &Call, CheckerContext &C,
+ SVal ThisSVal) {
+ ProgramStateRef State = Call.getState();
+
+ if (!State)
+ return;
+
+ auto ArgSVal = Call.getArgSVal(0);
+ const auto *ThisRegion = ThisSVal.getAsRegion();
+ const auto *ArgMemRegion = ArgSVal.getAsRegion();
+
+ // Make changes to the state according to type of constructor/assignment
+ bool IsCopy = isCopyConstructorCall(Call) || isCopyAssignmentCall(Call);
+ bool IsMove = isMoveConstructorCall(Call) || isMoveAssignmentCall(Call);
+ // First we handle copy and move operations
+ if (IsCopy || IsMove) {
+ const QualType *OtherQType = State->get<TypeMap>(ArgMemRegion);
+
+ // If the argument of a copy constructor or assignment is unknown then
+ // we will not know the argument of the copied to object.
+ if (!OtherQType) {
+ State = State->remove<TypeMap>(ThisRegion);
+ } else {
+ // When move semantics is used we can only know that the moved from
+ // object must be in a destructible state. Other usage of the object
+ // than destruction is undefined.
+ if (IsMove)
+ State = State->remove<TypeMap>(ArgMemRegion);
+
+ State = State->set<TypeMap>(ThisRegion, *OtherQType);
+ }
+ } else {
+ // Value constructor
+ auto ArgQType = ArgSVal.getType(C.getASTContext());
+ const Type *ArgTypePtr = ArgQType.getTypePtr();
+
+ QualType WoPointer = ArgTypePtr->getPointeeType();
+ State = State->set<TypeMap>(ThisRegion, WoPointer);
+ }
+
+ C.addTransition(State);
+}
+
+} // namespace clang::ento::tagged_union_modeling
+
+#endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAGGEDUNIONMODELING_H \ No newline at end of file
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp
index 71b2ab834a07..4edb671753bf 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp
@@ -10,9 +10,10 @@
//
//===----------------------------------------------------------------------===//
-#include "Taint.h"
+#include "clang/StaticAnalyzer/Checkers/Taint.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -37,7 +38,9 @@ void taint::printTaint(ProgramStateRef State, raw_ostream &Out, const char *NL,
Out << I.first << " : " << I.second << NL;
}
-void dumpTaint(ProgramStateRef State) { printTaint(State, llvm::errs()); }
+void taint::dumpTaint(ProgramStateRef State) {
+ printTaint(State, llvm::errs());
+}
ProgramStateRef taint::addTaint(ProgramStateRef State, const Stmt *S,
const LocationContext *LCtx,
@@ -61,7 +64,7 @@ ProgramStateRef taint::addTaint(ProgramStateRef State, SVal V,
// their parent region, which is a conjured symbol default-bound to the base
// region of the parent region.
if (auto LCV = V.getAs<nonloc::LazyCompoundVal>()) {
- if (Optional<SVal> binding =
+ if (std::optional<SVal> binding =
State->getStateManager().getStoreManager().getDefaultBinding(
*LCV)) {
if (SymbolRef Sym = binding->getAsSymbol())
@@ -143,62 +146,142 @@ ProgramStateRef taint::addPartialTaint(ProgramStateRef State,
bool taint::isTainted(ProgramStateRef State, const Stmt *S,
const LocationContext *LCtx, TaintTagType Kind) {
- SVal val = State->getSVal(S, LCtx);
- return isTainted(State, val, Kind);
+ return !getTaintedSymbolsImpl(State, S, LCtx, Kind, /*ReturnFirstOnly=*/true)
+ .empty();
}
bool taint::isTainted(ProgramStateRef State, SVal V, TaintTagType Kind) {
- if (SymbolRef Sym = V.getAsSymbol())
- return isTainted(State, Sym, Kind);
- if (const MemRegion *Reg = V.getAsRegion())
- return isTainted(State, Reg, Kind);
- return false;
+ return !getTaintedSymbolsImpl(State, V, Kind, /*ReturnFirstOnly=*/true)
+ .empty();
}
bool taint::isTainted(ProgramStateRef State, const MemRegion *Reg,
TaintTagType K) {
- if (!Reg)
- return false;
+ return !getTaintedSymbolsImpl(State, Reg, K, /*ReturnFirstOnly=*/true)
+ .empty();
+}
+
+bool taint::isTainted(ProgramStateRef State, SymbolRef Sym, TaintTagType Kind) {
+ return !getTaintedSymbolsImpl(State, Sym, Kind, /*ReturnFirstOnly=*/true)
+ .empty();
+}
+
+std::vector<SymbolRef> taint::getTaintedSymbols(ProgramStateRef State,
+ const Stmt *S,
+ const LocationContext *LCtx,
+ TaintTagType Kind) {
+ return getTaintedSymbolsImpl(State, S, LCtx, Kind, /*ReturnFirstOnly=*/false);
+}
+
+std::vector<SymbolRef> taint::getTaintedSymbols(ProgramStateRef State, SVal V,
+ TaintTagType Kind) {
+ return getTaintedSymbolsImpl(State, V, Kind, /*ReturnFirstOnly=*/false);
+}
+
+std::vector<SymbolRef> taint::getTaintedSymbols(ProgramStateRef State,
+ SymbolRef Sym,
+ TaintTagType Kind) {
+ return getTaintedSymbolsImpl(State, Sym, Kind, /*ReturnFirstOnly=*/false);
+}
+
+std::vector<SymbolRef> taint::getTaintedSymbols(ProgramStateRef State,
+ const MemRegion *Reg,
+ TaintTagType Kind) {
+ return getTaintedSymbolsImpl(State, Reg, Kind, /*ReturnFirstOnly=*/false);
+}
+
+std::vector<SymbolRef> taint::getTaintedSymbolsImpl(ProgramStateRef State,
+ const Stmt *S,
+ const LocationContext *LCtx,
+ TaintTagType Kind,
+ bool returnFirstOnly) {
+ SVal val = State->getSVal(S, LCtx);
+ return getTaintedSymbolsImpl(State, val, Kind, returnFirstOnly);
+}
+
+std::vector<SymbolRef> taint::getTaintedSymbolsImpl(ProgramStateRef State,
+ SVal V, TaintTagType Kind,
+ bool returnFirstOnly) {
+ if (SymbolRef Sym = V.getAsSymbol())
+ return getTaintedSymbolsImpl(State, Sym, Kind, returnFirstOnly);
+ if (const MemRegion *Reg = V.getAsRegion())
+ return getTaintedSymbolsImpl(State, Reg, Kind, returnFirstOnly);
+ return {};
+}
+std::vector<SymbolRef> taint::getTaintedSymbolsImpl(ProgramStateRef State,
+ const MemRegion *Reg,
+ TaintTagType K,
+ bool returnFirstOnly) {
+ std::vector<SymbolRef> TaintedSymbols;
+ if (!Reg)
+ return TaintedSymbols;
// Element region (array element) is tainted if either the base or the offset
// are tainted.
- if (const ElementRegion *ER = dyn_cast<ElementRegion>(Reg))
- return isTainted(State, ER->getSuperRegion(), K) ||
- isTainted(State, ER->getIndex(), K);
+ if (const ElementRegion *ER = dyn_cast<ElementRegion>(Reg)) {
+ std::vector<SymbolRef> TaintedIndex =
+ getTaintedSymbolsImpl(State, ER->getIndex(), K, returnFirstOnly);
+ llvm::append_range(TaintedSymbols, TaintedIndex);
+ if (returnFirstOnly && !TaintedSymbols.empty())
+ return TaintedSymbols; // return early if needed
+ std::vector<SymbolRef> TaintedSuperRegion =
+ getTaintedSymbolsImpl(State, ER->getSuperRegion(), K, returnFirstOnly);
+ llvm::append_range(TaintedSymbols, TaintedSuperRegion);
+ if (returnFirstOnly && !TaintedSymbols.empty())
+ return TaintedSymbols; // return early if needed
+ }
- if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg))
- return isTainted(State, SR->getSymbol(), K);
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) {
+ std::vector<SymbolRef> TaintedRegions =
+ getTaintedSymbolsImpl(State, SR->getSymbol(), K, returnFirstOnly);
+ llvm::append_range(TaintedSymbols, TaintedRegions);
+ if (returnFirstOnly && !TaintedSymbols.empty())
+ return TaintedSymbols; // return early if needed
+ }
- if (const SubRegion *ER = dyn_cast<SubRegion>(Reg))
- return isTainted(State, ER->getSuperRegion(), K);
+ if (const SubRegion *ER = dyn_cast<SubRegion>(Reg)) {
+ std::vector<SymbolRef> TaintedSubRegions =
+ getTaintedSymbolsImpl(State, ER->getSuperRegion(), K, returnFirstOnly);
+ llvm::append_range(TaintedSymbols, TaintedSubRegions);
+ if (returnFirstOnly && !TaintedSymbols.empty())
+ return TaintedSymbols; // return early if needed
+ }
- return false;
+ return TaintedSymbols;
}
-bool taint::isTainted(ProgramStateRef State, SymbolRef Sym, TaintTagType Kind) {
+std::vector<SymbolRef> taint::getTaintedSymbolsImpl(ProgramStateRef State,
+ SymbolRef Sym,
+ TaintTagType Kind,
+ bool returnFirstOnly) {
+ std::vector<SymbolRef> TaintedSymbols;
if (!Sym)
- return false;
+ return TaintedSymbols;
// Traverse all the symbols this symbol depends on to see if any are tainted.
- for (SymExpr::symbol_iterator SI = Sym->symbol_begin(),
- SE = Sym->symbol_end();
- SI != SE; ++SI) {
- if (!isa<SymbolData>(*SI))
+ for (SymbolRef SubSym : Sym->symbols()) {
+ if (!isa<SymbolData>(SubSym))
continue;
- if (const TaintTagType *Tag = State->get<TaintMap>(*SI)) {
- if (*Tag == Kind)
- return true;
+ if (const TaintTagType *Tag = State->get<TaintMap>(SubSym)) {
+ if (*Tag == Kind) {
+ TaintedSymbols.push_back(SubSym);
+ if (returnFirstOnly)
+ return TaintedSymbols; // return early if needed
+ }
}
- if (const auto *SD = dyn_cast<SymbolDerived>(*SI)) {
+ if (const auto *SD = dyn_cast<SymbolDerived>(SubSym)) {
// If this is a SymbolDerived with a tainted parent, it's also tainted.
- if (isTainted(State, SD->getParentSymbol(), Kind))
- return true;
+ std::vector<SymbolRef> TaintedParents = getTaintedSymbolsImpl(
+ State, SD->getParentSymbol(), Kind, returnFirstOnly);
+ llvm::append_range(TaintedSymbols, TaintedParents);
+ if (returnFirstOnly && !TaintedSymbols.empty())
+ return TaintedSymbols; // return early if needed
// If this is a SymbolDerived with the same parent symbol as another
- // tainted SymbolDerived and a region that's a sub-region of that tainted
- // symbol, it's also tainted.
+ // tainted SymbolDerived and a region that's a sub-region of that
+ // tainted symbol, it's also tainted.
if (const TaintedSubRegions *Regs =
State->get<DerivedSymTaint>(SD->getParentSymbol())) {
const TypedValueRegion *R = SD->getRegion();
@@ -207,46 +290,32 @@ bool taint::isTainted(ProgramStateRef State, SymbolRef Sym, TaintTagType Kind) {
// complete. For example, this would not currently identify
// overlapping fields in a union as tainted. To identify this we can
// check for overlapping/nested byte offsets.
- if (Kind == I.second && R->isSubRegionOf(I.first))
- return true;
+ if (Kind == I.second && R->isSubRegionOf(I.first)) {
+ TaintedSymbols.push_back(SD->getParentSymbol());
+ if (returnFirstOnly && !TaintedSymbols.empty())
+ return TaintedSymbols; // return early if needed
+ }
}
}
}
// If memory region is tainted, data is also tainted.
- if (const auto *SRV = dyn_cast<SymbolRegionValue>(*SI)) {
- if (isTainted(State, SRV->getRegion(), Kind))
- return true;
+ if (const auto *SRV = dyn_cast<SymbolRegionValue>(SubSym)) {
+ std::vector<SymbolRef> TaintedRegions =
+ getTaintedSymbolsImpl(State, SRV->getRegion(), Kind, returnFirstOnly);
+ llvm::append_range(TaintedSymbols, TaintedRegions);
+ if (returnFirstOnly && !TaintedSymbols.empty())
+ return TaintedSymbols; // return early if needed
}
// If this is a SymbolCast from a tainted value, it's also tainted.
- if (const auto *SC = dyn_cast<SymbolCast>(*SI)) {
- if (isTainted(State, SC->getOperand(), Kind))
- return true;
+ if (const auto *SC = dyn_cast<SymbolCast>(SubSym)) {
+ std::vector<SymbolRef> TaintedCasts =
+ getTaintedSymbolsImpl(State, SC->getOperand(), Kind, returnFirstOnly);
+ llvm::append_range(TaintedSymbols, TaintedCasts);
+ if (returnFirstOnly && !TaintedSymbols.empty())
+ return TaintedSymbols; // return early if needed
}
}
-
- return false;
-}
-
-PathDiagnosticPieceRef TaintBugVisitor::VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- PathSensitiveBugReport &BR) {
-
- // Find the ExplodedNode where the taint was first introduced
- if (!isTainted(N->getState(), V) ||
- isTainted(N->getFirstPred()->getState(), V))
- return nullptr;
-
- const Stmt *S = N->getStmtForDiagnostics();
- if (!S)
- return nullptr;
-
- const LocationContext *NCtx = N->getLocationContext();
- PathDiagnosticLocation L =
- PathDiagnosticLocation::createBegin(S, BRC.getSourceManager(), NCtx);
- if (!L.isValid() || !L.asLocation().isValid())
- return nullptr;
-
- return std::make_shared<PathDiagnosticEventPiece>(L, "Taint originated here");
-}
+ return TaintedSymbols;
+} \ No newline at end of file
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.h
deleted file mode 100644
index 659a3c898d56..000000000000
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.h
+++ /dev/null
@@ -1,106 +0,0 @@
-//=== Taint.h - Taint tracking and basic propagation rules. --------*- C++ -*-//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// Defines basic, non-domain-specific mechanisms for tracking tainted values.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAINT_H
-#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAINT_H
-
-#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
-
-namespace clang {
-namespace ento {
-namespace taint {
-
-/// The type of taint, which helps to differentiate between different types of
-/// taint.
-using TaintTagType = unsigned;
-
-static constexpr TaintTagType TaintTagGeneric = 0;
-
-/// Create a new state in which the value of the statement is marked as tainted.
-LLVM_NODISCARD ProgramStateRef addTaint(ProgramStateRef State, const Stmt *S,
- const LocationContext *LCtx,
- TaintTagType Kind = TaintTagGeneric);
-
-/// Create a new state in which the value is marked as tainted.
-LLVM_NODISCARD ProgramStateRef addTaint(ProgramStateRef State, SVal V,
- TaintTagType Kind = TaintTagGeneric);
-
-/// Create a new state in which the symbol is marked as tainted.
-LLVM_NODISCARD ProgramStateRef addTaint(ProgramStateRef State, SymbolRef Sym,
- TaintTagType Kind = TaintTagGeneric);
-
-/// Create a new state in which the pointer represented by the region
-/// is marked as tainted.
-LLVM_NODISCARD ProgramStateRef addTaint(ProgramStateRef State,
- const MemRegion *R,
- TaintTagType Kind = TaintTagGeneric);
-
-LLVM_NODISCARD ProgramStateRef removeTaint(ProgramStateRef State, SVal V);
-
-LLVM_NODISCARD ProgramStateRef removeTaint(ProgramStateRef State,
- const MemRegion *R);
-
-LLVM_NODISCARD ProgramStateRef removeTaint(ProgramStateRef State,
- SymbolRef Sym);
-
-/// Create a new state in a which a sub-region of a given symbol is tainted.
-/// This might be necessary when referring to regions that can not have an
-/// individual symbol, e.g. if they are represented by the default binding of
-/// a LazyCompoundVal.
-LLVM_NODISCARD ProgramStateRef addPartialTaint(
- ProgramStateRef State, SymbolRef ParentSym, const SubRegion *SubRegion,
- TaintTagType Kind = TaintTagGeneric);
-
-/// Check if the statement has a tainted value in the given state.
-bool isTainted(ProgramStateRef State, const Stmt *S,
- const LocationContext *LCtx,
- TaintTagType Kind = TaintTagGeneric);
-
-/// Check if the value is tainted in the given state.
-bool isTainted(ProgramStateRef State, SVal V,
- TaintTagType Kind = TaintTagGeneric);
-
-/// Check if the symbol is tainted in the given state.
-bool isTainted(ProgramStateRef State, SymbolRef Sym,
- TaintTagType Kind = TaintTagGeneric);
-
-/// Check if the pointer represented by the region is tainted in the given
-/// state.
-bool isTainted(ProgramStateRef State, const MemRegion *Reg,
- TaintTagType Kind = TaintTagGeneric);
-
-void printTaint(ProgramStateRef State, raw_ostream &Out, const char *nl = "\n",
- const char *sep = "");
-
-LLVM_DUMP_METHOD void dumpTaint(ProgramStateRef State);
-
-/// The bug visitor prints a diagnostic message at the location where a given
-/// variable was tainted.
-class TaintBugVisitor final : public BugReporterVisitor {
-private:
- const SVal V;
-
-public:
- TaintBugVisitor(const SVal V) : V(V) {}
- void Profile(llvm::FoldingSetNodeID &ID) const override { ID.Add(V); }
-
- PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- PathSensitiveBugReport &BR) override;
-};
-
-} // namespace taint
-} // namespace ento
-} // namespace clang
-
-#endif
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
index 916977c10c0c..acf4e833095b 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
@@ -10,8 +10,8 @@
//
//===----------------------------------------------------------------------===//
-#include "Taint.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Checkers/Taint.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -22,27 +22,14 @@ using namespace ento;
using namespace taint;
namespace {
-class TaintTesterChecker : public Checker< check::PostStmt<Expr> > {
-
- mutable std::unique_ptr<BugType> BT;
- void initBugType() const;
-
- /// Given a pointer argument, get the symbol of the value it contains
- /// (points to).
- SymbolRef getPointedToSymbol(CheckerContext &C,
- const Expr* Arg,
- bool IssueWarning = true) const;
+class TaintTesterChecker : public Checker<check::PostStmt<Expr>> {
+ const BugType BT{this, "Tainted data", "General"};
public:
void checkPostStmt(const Expr *E, CheckerContext &C) const;
};
}
-inline void TaintTesterChecker::initBugType() const {
- if (!BT)
- BT.reset(new BugType(this, "Tainted data", "General"));
-}
-
void TaintTesterChecker::checkPostStmt(const Expr *E,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
@@ -51,8 +38,7 @@ void TaintTesterChecker::checkPostStmt(const Expr *E,
if (isTainted(State, E, C.getLocationContext())) {
if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
- initBugType();
- auto report = std::make_unique<PathSensitiveBugReport>(*BT, "tainted", N);
+ auto report = std::make_unique<PathSensitiveBugReport>(BT, "tainted", N);
report->addRange(E->getSourceRange());
C.emitReport(std::move(report));
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
index eeec807ccee4..667b19f8120e 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/FoldingSet.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -77,7 +78,7 @@ public:
class TestAfterDivZeroChecker
: public Checker<check::PreStmt<BinaryOperator>, check::BranchCondition,
check::EndFunction> {
- mutable std::unique_ptr<BuiltinBug> DivZeroBug;
+ const BugType DivZeroBug{this, "Division by zero"};
void reportBug(SVal Val, CheckerContext &C) const;
public:
@@ -100,7 +101,7 @@ DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC,
const Expr *E = nullptr;
- if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>())
+ if (std::optional<PostStmt> P = Succ->getLocationAs<PostStmt>())
if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) {
BinaryOperator::Opcode Op = BO->getOpcode();
if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign ||
@@ -132,7 +133,7 @@ DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC,
}
bool TestAfterDivZeroChecker::isZero(SVal S, CheckerContext &C) const {
- Optional<DefinedSVal> DSV = S.getAs<DefinedSVal>();
+ std::optional<DefinedSVal> DSV = S.getAs<DefinedSVal>();
if (!DSV)
return false;
@@ -164,12 +165,10 @@ bool TestAfterDivZeroChecker::hasDivZeroMap(SVal Var,
void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const {
if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
- if (!DivZeroBug)
- DivZeroBug.reset(new BuiltinBug(this, "Division by zero"));
-
auto R = std::make_unique<PathSensitiveBugReport>(
- *DivZeroBug, "Value being compared against zero has already been used "
- "for division",
+ DivZeroBug,
+ "Value being compared against zero has already been used "
+ "for division",
N);
R->addVisitor(std::make_unique<DivisionBRVisitor>(Val.getAsSymbol(),
@@ -187,10 +186,7 @@ void TestAfterDivZeroChecker::checkEndFunction(const ReturnStmt *,
return;
DivZeroMapTy::Factory &F = State->get_context<DivZeroMap>();
- for (llvm::ImmutableSet<ZeroState>::iterator I = DivZeroes.begin(),
- E = DivZeroes.end();
- I != E; ++I) {
- ZeroState ZS = *I;
+ for (const ZeroState &ZS : DivZeroes) {
if (ZS.getStackFrameContext() == C.getStackFrame())
DivZeroes = F.remove(DivZeroes, ZS);
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp
index 5cc713172527..e2f8bd541c96 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp
@@ -69,8 +69,7 @@ public:
if (!CondS || CondS->computeComplexity() > ComplexityThreshold)
return State;
- for (auto B=CondS->symbol_begin(), E=CondS->symbol_end(); B != E; ++B) {
- const SymbolRef Antecedent = *B;
+ for (SymbolRef Antecedent : CondS->symbols()) {
State = addImplication(Antecedent, State, true);
State = addImplication(Antecedent, State, false);
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustReturnsNonnullChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustReturnsNonnullChecker.cpp
new file mode 100644
index 000000000000..d80559c6a915
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustReturnsNonnullChecker.cpp
@@ -0,0 +1,60 @@
+//== TrustReturnsNonnullChecker.cpp -- API nullability modeling -*- 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 checker adds nullability-related assumptions to methods annotated with
+// returns_nonnull attribute.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Attr.h"
+#include "clang/AST/Decl.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class TrustReturnsNonnullChecker : public Checker<check::PostCall> {
+
+public:
+ TrustReturnsNonnullChecker(ASTContext &Ctx) {}
+
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ if (isNonNullPtr(Call))
+ if (auto L = Call.getReturnValue().getAs<Loc>())
+ State = State->assume(*L, /*assumption=*/true);
+
+ C.addTransition(State);
+ }
+
+private:
+ /// \returns Whether the method declaration has the attribute returns_nonnull.
+ bool isNonNullPtr(const CallEvent &Call) const {
+ QualType ExprRetType = Call.getResultType();
+ const Decl *CallDeclaration = Call.getDecl();
+ if (!ExprRetType->isAnyPointerType() || !CallDeclaration)
+ return false;
+
+ return CallDeclaration->hasAttr<ReturnsNonNullAttr>();
+ }
+};
+
+} // namespace
+
+void ento::registerTrustReturnsNonnullChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<TrustReturnsNonnullChecker>(Mgr.getASTContext());
+}
+
+bool ento::shouldRegisterTrustReturnsNonnullChecker(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 ebe5ad53cc30..aa478b69aade 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
@@ -18,6 +18,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include <optional>
#include <utility>
using namespace clang;
@@ -26,7 +27,7 @@ using namespace ento;
namespace {
class UndefBranchChecker : public Checker<check::BranchCondition> {
- mutable std::unique_ptr<BuiltinBug> BT;
+ const BugType BT{this, "Branch condition evaluates to a garbage value"};
struct FindUndefExpr {
ProgramStateRef St;
@@ -63,52 +64,47 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition,
// ObjCForCollection is a loop, but has no actual condition.
if (isa<ObjCForCollectionStmt>(Condition))
return;
- SVal X = Ctx.getSVal(Condition);
- if (X.isUndef()) {
- // Generate a sink node, which implicitly marks both outgoing branches as
- // infeasible.
- ExplodedNode *N = Ctx.generateErrorNode();
- if (N) {
- if (!BT)
- BT.reset(new BuiltinBug(
- this, "Branch condition evaluates to a garbage value"));
-
- // What's going on here: we want to highlight the subexpression of the
- // condition that is the most likely source of the "uninitialized
- // branch condition." We do a recursive walk of the condition's
- // subexpressions and roughly look for the most nested subexpression
- // that binds to Undefined. We then highlight that expression's range.
-
- // Get the predecessor node and check if is a PostStmt with the Stmt
- // being the terminator condition. We want to inspect the state
- // of that node instead because it will contain main information about
- // the subexpressions.
-
- // Note: any predecessor will do. They should have identical state,
- // since all the BlockEdge did was act as an error sink since the value
- // had to already be undefined.
- assert (!N->pred_empty());
- const Expr *Ex = cast<Expr>(Condition);
- ExplodedNode *PrevN = *N->pred_begin();
- ProgramPoint P = PrevN->getLocation();
- ProgramStateRef St = N->getState();
-
- if (Optional<PostStmt> PS = P.getAs<PostStmt>())
- if (PS->getStmt() == Ex)
- St = PrevN->getState();
-
- FindUndefExpr FindIt(St, Ctx.getLocationContext());
- Ex = FindIt.FindExpr(Ex);
-
- // Emit the bug report.
- auto R = std::make_unique<PathSensitiveBugReport>(
- *BT, BT->getDescription(), N);
- bugreporter::trackExpressionValue(N, Ex, *R);
- R->addRange(Ex->getSourceRange());
-
- Ctx.emitReport(std::move(R));
- }
- }
+ if (!Ctx.getSVal(Condition).isUndef())
+ return;
+
+ // Generate a sink node, which implicitly marks both outgoing branches as
+ // infeasible.
+ ExplodedNode *N = Ctx.generateErrorNode();
+ if (!N)
+ return;
+ // What's going on here: we want to highlight the subexpression of the
+ // condition that is the most likely source of the "uninitialized
+ // branch condition." We do a recursive walk of the condition's
+ // subexpressions and roughly look for the most nested subexpression
+ // that binds to Undefined. We then highlight that expression's range.
+
+ // Get the predecessor node and check if is a PostStmt with the Stmt
+ // being the terminator condition. We want to inspect the state
+ // of that node instead because it will contain main information about
+ // the subexpressions.
+
+ // Note: any predecessor will do. They should have identical state,
+ // since all the BlockEdge did was act as an error sink since the value
+ // had to already be undefined.
+ assert(!N->pred_empty());
+ const Expr *Ex = cast<Expr>(Condition);
+ ExplodedNode *PrevN = *N->pred_begin();
+ ProgramPoint P = PrevN->getLocation();
+ ProgramStateRef St = N->getState();
+
+ if (std::optional<PostStmt> PS = P.getAs<PostStmt>())
+ if (PS->getStmt() == Ex)
+ St = PrevN->getState();
+
+ FindUndefExpr FindIt(St, Ctx.getLocationContext());
+ Ex = FindIt.FindExpr(Ex);
+
+ // Emit the bug report.
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, BT.getDescription(), N);
+ bugreporter::trackExpressionValue(N, Ex, *R);
+ R->addRange(Ex->getSourceRange());
+
+ Ctx.emitReport(std::move(R));
}
void ento::registerUndefBranchChecker(CheckerManager &mgr) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
index 816a547cadc3..2839ef0b6d2e 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
@@ -19,6 +19,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -26,7 +27,7 @@ using namespace ento;
namespace {
class UndefCapturedBlockVarChecker
: public Checker< check::PostStmt<BlockExpr> > {
- mutable std::unique_ptr<BugType> BT;
+ const BugType BT{this, "uninitialized variable captured by block"};
public:
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
@@ -56,26 +57,19 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
ProgramStateRef state = C.getState();
auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
- BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
- E = R->referenced_vars_end();
-
- for (; I != E; ++I) {
+ for (auto Var : R->referenced_vars()) {
// This VarRegion is the region associated with the block; we need
// the one associated with the encompassing context.
- const VarRegion *VR = I.getCapturedRegion();
+ const VarRegion *VR = Var.getCapturedRegion();
const VarDecl *VD = VR->getDecl();
if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage())
continue;
// Get the VarRegion associated with VD in the local stack frame.
- if (Optional<UndefinedVal> V =
- state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) {
+ if (std::optional<UndefinedVal> V =
+ state->getSVal(Var.getOriginalRegion()).getAs<UndefinedVal>()) {
if (ExplodedNode *N = C.generateErrorNode()) {
- if (!BT)
- BT.reset(
- new BuiltinBug(this, "uninitialized variable captured by block"));
-
// Generate a bug report.
SmallString<128> buf;
llvm::raw_svector_ostream os(buf);
@@ -83,7 +77,7 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
os << "Variable '" << VD->getName()
<< "' is uninitialized when captured by block";
- auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD))
R->addRange(Ex->getSourceRange());
bugreporter::trackStoredValue(*V, VR, *R,
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
index 477d910bc653..4b845bb3ded2 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
@@ -28,7 +28,7 @@ namespace {
class UndefResultChecker
: public Checker< check::PostStmt<BinaryOperator> > {
- mutable std::unique_ptr<BugType> BT;
+ const BugType BT{this, "Result of operation is garbage or undefined"};
public:
void checkPostStmt(const BinaryOperator *B, CheckerContext &C) const;
@@ -53,33 +53,17 @@ static bool isArrayIndexOutOfBounds(CheckerContext &C, const Expr *Ex) {
DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType());
- ProgramStateRef StInBound = state->assumeInBound(Idx, ElementCount, true);
- ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false);
+ ProgramStateRef StInBound, StOutBound;
+ std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, ElementCount);
return StOutBound && !StInBound;
}
-static bool isShiftOverflow(const BinaryOperator *B, CheckerContext &C) {
- return C.isGreaterOrEqual(
- B->getRHS(), C.getASTContext().getIntWidth(B->getLHS()->getType()));
-}
-
-static bool isLeftShiftResultUnrepresentable(const BinaryOperator *B,
- CheckerContext &C) {
- SValBuilder &SB = C.getSValBuilder();
- ProgramStateRef State = C.getState();
- const llvm::APSInt *LHS = SB.getKnownValue(State, C.getSVal(B->getLHS()));
- const llvm::APSInt *RHS = SB.getKnownValue(State, C.getSVal(B->getRHS()));
- assert(LHS && RHS && "Values unknown, inconsistent state");
- return (unsigned)RHS->getZExtValue() > LHS->countLeadingZeros();
-}
-
void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
CheckerContext &C) const {
if (C.getSVal(B).isUndef()) {
// Do not report assignments of uninitialized values inside swap functions.
// This should allow to swap partially uninitialized structs
- // (radar://14129997)
if (const FunctionDecl *EnclosingFunctionDecl =
dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
@@ -90,10 +74,6 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
if (!N)
return;
- if (!BT)
- BT.reset(
- new BuiltinBug(this, "Result of operation is garbage or undefined"));
-
SmallString<256> sbuf;
llvm::raw_svector_ostream OS(sbuf);
const Expr *Ex = nullptr;
@@ -116,62 +96,11 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
OS << " due to array index out of bounds";
} else {
// Neither operand was undefined, but the result is undefined.
- if ((B->getOpcode() == BinaryOperatorKind::BO_Shl ||
- B->getOpcode() == BinaryOperatorKind::BO_Shr) &&
- C.isNegative(B->getRHS())) {
- OS << "The result of the "
- << ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left"
- : "right")
- << " shift is undefined because the right operand is negative";
- Ex = B->getRHS();
- } else if ((B->getOpcode() == BinaryOperatorKind::BO_Shl ||
- B->getOpcode() == BinaryOperatorKind::BO_Shr) &&
- isShiftOverflow(B, C)) {
-
- OS << "The result of the "
- << ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left"
- : "right")
- << " shift is undefined due to shifting by ";
- Ex = B->getRHS();
-
- SValBuilder &SB = C.getSValBuilder();
- const llvm::APSInt *I =
- SB.getKnownValue(C.getState(), C.getSVal(B->getRHS()));
- if (!I)
- OS << "a value that is";
- else if (I->isUnsigned())
- OS << '\'' << I->getZExtValue() << "\', which is";
- else
- OS << '\'' << I->getSExtValue() << "\', which is";
-
- OS << " greater or equal to the width of type '"
- << B->getLHS()->getType().getAsString() << "'.";
- } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
- C.isNegative(B->getLHS())) {
- OS << "The result of the left shift is undefined because the left "
- "operand is negative";
- Ex = B->getLHS();
- } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
- isLeftShiftResultUnrepresentable(B, C)) {
- ProgramStateRef State = C.getState();
- SValBuilder &SB = C.getSValBuilder();
- const llvm::APSInt *LHS =
- SB.getKnownValue(State, C.getSVal(B->getLHS()));
- const llvm::APSInt *RHS =
- SB.getKnownValue(State, C.getSVal(B->getRHS()));
- OS << "The result of the left shift is undefined due to shifting \'"
- << LHS->getSExtValue() << "\' by \'" << RHS->getZExtValue()
- << "\', which is unrepresentable in the unsigned version of "
- << "the return type \'" << B->getLHS()->getType().getAsString()
- << "\'";
- Ex = B->getLHS();
- } else {
- OS << "The result of the '"
- << BinaryOperator::getOpcodeStr(B->getOpcode())
- << "' expression is undefined";
- }
+ OS << "The result of the '"
+ << BinaryOperator::getOpcodeStr(B->getOpcode())
+ << "' expression is undefined";
}
- auto report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
+ auto report = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N);
if (Ex) {
report->addRange(Ex->getSourceRange());
bugreporter::trackExpressionValue(N, Ex, *report);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
index fdefe75e8201..baa07fa66764 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
@@ -24,7 +24,7 @@ using namespace ento;
namespace {
class UndefinedArraySubscriptChecker
: public Checker< check::PreStmt<ArraySubscriptExpr> > {
- mutable std::unique_ptr<BugType> BT;
+ const BugType BT{this, "Array subscript is undefined"};
public:
void checkPreStmt(const ArraySubscriptExpr *A, CheckerContext &C) const;
@@ -48,11 +48,8 @@ UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A,
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
- if (!BT)
- BT.reset(new BuiltinBug(this, "Array subscript is undefined"));
-
// Generate a report for this bug.
- auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, BT.getDescription(), N);
R->addRange(A->getIdx()->getSourceRange());
bugreporter::trackExpressionValue(N, A->getIdx(), *R);
C.emitReport(std::move(R));
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
index 05f8f6084c0b..ddc6cc9e8202 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
@@ -23,7 +23,7 @@ using namespace ento;
namespace {
class UndefinedAssignmentChecker
: public Checker<check::Bind> {
- mutable std::unique_ptr<BugType> BT;
+ const BugType BT{this, "Assigned value is garbage or undefined"};
public:
void checkBind(SVal location, SVal val, const Stmt *S,
@@ -39,7 +39,6 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
// Do not report assignments of uninitialized values inside swap functions.
// This should allow to swap partially uninitialized structs
- // (radar://14129997)
if (const FunctionDecl *EnclosingFunctionDecl =
dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
@@ -50,11 +49,6 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
if (!N)
return;
- static const char *const DefaultMsg =
- "Assigned value is garbage or undefined";
- if (!BT)
- BT.reset(new BuiltinBug(this, DefaultMsg));
-
// Generate a report for this bug.
llvm::SmallString<128> Str;
llvm::raw_svector_ostream OS(Str);
@@ -92,7 +86,7 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
if (const auto *CD =
dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) {
if (CD->isImplicit()) {
- for (auto I : CD->inits()) {
+ for (auto *I : CD->inits()) {
if (I->getInit()->IgnoreImpCasts() == StoreE) {
OS << "Value assigned to field '" << I->getMember()->getName()
<< "' in implicit constructor is garbage or undefined";
@@ -106,9 +100,9 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
}
if (OS.str().empty())
- OS << DefaultMsg;
+ OS << BT.getDescription();
- auto R = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N);
if (ex) {
R->addRange(ex->getSourceRange());
bugreporter::trackExpressionValue(N, ex, *R);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp
new file mode 100644
index 000000000000..f053ee887a1a
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp
@@ -0,0 +1,80 @@
+//===--- UndefinedNewArraySizeChecker.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 defines UndefinedNewArraySizeChecker, a builtin check in ExprEngine
+// that checks if the size of the array in a new[] expression is undefined.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class UndefinedNewArraySizeChecker : public Checker<check::PreCall> {
+
+private:
+ BugType BT{this, "Undefined array element count in new[]",
+ categories::LogicError};
+
+public:
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void HandleUndefinedArrayElementCount(CheckerContext &C, SVal ArgVal,
+ const Expr *Init,
+ SourceRange Range) const;
+};
+} // namespace
+
+void UndefinedNewArraySizeChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (const auto *AC = dyn_cast<CXXAllocatorCall>(&Call)) {
+ if (!AC->isArray())
+ return;
+
+ auto *SizeEx = *AC->getArraySizeExpr();
+ auto SizeVal = AC->getArraySizeVal();
+
+ if (SizeVal.isUndef())
+ HandleUndefinedArrayElementCount(C, SizeVal, SizeEx,
+ SizeEx->getSourceRange());
+ }
+}
+
+void UndefinedNewArraySizeChecker::HandleUndefinedArrayElementCount(
+ CheckerContext &C, SVal ArgVal, const Expr *Init, SourceRange Range) const {
+
+ if (ExplodedNode *N = C.generateErrorNode()) {
+
+ SmallString<100> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ os << "Element count in new[] is a garbage value";
+
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
+ R->markInteresting(ArgVal);
+ R->addRange(Range);
+ bugreporter::trackExpressionValue(N, Init, *R);
+
+ C.emitReport(std::move(R));
+ }
+}
+
+void ento::registerUndefinedNewArraySizeChecker(CheckerManager &mgr) {
+ mgr.registerChecker<UndefinedNewArraySizeChecker>();
+}
+
+bool ento::shouldRegisterUndefinedNewArraySizeChecker(
+ const CheckerManager &mgr) {
+ return mgr.getLangOpts().CPlusPlus;
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h
index 2fcdd6086309..e35778e6480c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h
@@ -299,7 +299,7 @@ private:
bool isDereferencableUninit(const FieldRegion *FR, FieldChainInfo LocalChain);
/// Returns true if the value of a primitive object is uninitialized.
- bool isPrimitiveUninit(const SVal &V);
+ bool isPrimitiveUninit(SVal V);
// Note that we don't have a method for arrays -- the elements of an array are
// often left uninitialized intentionally even when it is of a C++ record
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 4182b51c02b0..6e1222fedad3 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
@@ -38,15 +38,12 @@ namespace {
class UninitializedObjectChecker
: public Checker<check::EndFunction, check::DeadSymbols> {
- std::unique_ptr<BuiltinBug> BT_uninitField;
+ const BugType BT_uninitField{this, "Uninitialized fields"};
public:
// The fields of this struct will be initialized when registering the checker.
UninitObjCheckerOptions Opts;
- UninitializedObjectChecker()
- : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {}
-
void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
};
@@ -57,19 +54,17 @@ class RegularField final : public FieldNode {
public:
RegularField(const FieldRegion *FR) : FieldNode(FR) {}
- virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
+ void printNoteMsg(llvm::raw_ostream &Out) const override {
Out << "uninitialized field ";
}
- virtual void printPrefix(llvm::raw_ostream &Out) const override {}
+ void printPrefix(llvm::raw_ostream &Out) const override {}
- virtual void printNode(llvm::raw_ostream &Out) const override {
+ void printNode(llvm::raw_ostream &Out) const override {
Out << getVariableName(getDecl());
}
- virtual void printSeparator(llvm::raw_ostream &Out) const override {
- Out << '.';
- }
+ void printSeparator(llvm::raw_ostream &Out) const override { Out << '.'; }
};
/// Represents that the FieldNode that comes after this is declared in a base
@@ -85,20 +80,20 @@ public:
assert(T->getAsCXXRecordDecl());
}
- virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
+ void printNoteMsg(llvm::raw_ostream &Out) const override {
llvm_unreachable("This node can never be the final node in the "
"fieldchain!");
}
- virtual void printPrefix(llvm::raw_ostream &Out) const override {}
+ void printPrefix(llvm::raw_ostream &Out) const override {}
- virtual void printNode(llvm::raw_ostream &Out) const override {
+ void printNode(llvm::raw_ostream &Out) const override {
Out << BaseClassT->getAsCXXRecordDecl()->getName() << "::";
}
- virtual void printSeparator(llvm::raw_ostream &Out) const override {}
+ void printSeparator(llvm::raw_ostream &Out) const override {}
- virtual bool isBase() const override { return true; }
+ bool isBase() const override { return true; }
};
} // end of anonymous namespace
@@ -188,7 +183,7 @@ void UninitializedObjectChecker::checkEndFunction(
for (const auto &Pair : UninitFields) {
auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT_uninitField, Pair.second, Node, LocUsedForUniqueing,
+ BT_uninitField, Pair.second, Node, LocUsedForUniqueing,
Node->getLocationContext()->getDecl());
Context.emitReport(std::move(Report));
}
@@ -202,7 +197,7 @@ void UninitializedObjectChecker::checkEndFunction(
<< " at the end of the constructor call";
auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,
+ BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,
Node->getLocationContext()->getDecl());
for (const auto &Pair : UninitFields) {
@@ -330,7 +325,7 @@ bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R,
SVal V = State->getSVal(FieldVal);
- if (isDereferencableType(T) || V.getAs<nonloc::LocAsInteger>()) {
+ if (isDereferencableType(T) || isa<nonloc::LocAsInteger>(V)) {
if (isDereferencableUninit(FR, LocalChain))
ContainsUninitField = true;
continue;
@@ -381,7 +376,7 @@ bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) {
return false;
}
-bool FindUninitializedFields::isPrimitiveUninit(const SVal &V) {
+bool FindUninitializedFields::isPrimitiveUninit(SVal V) {
if (V.isUndef())
return true;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
index f0dd0bf813af..54e1e0e11909 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
@@ -19,6 +19,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
+#include <optional>
using namespace clang;
using namespace clang::ento;
@@ -34,20 +35,20 @@ public:
LocField(const FieldRegion *FR, const bool IsDereferenced = true)
: FieldNode(FR), IsDereferenced(IsDereferenced) {}
- virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
+ void printNoteMsg(llvm::raw_ostream &Out) const override {
if (IsDereferenced)
Out << "uninitialized pointee ";
else
Out << "uninitialized pointer ";
}
- virtual void printPrefix(llvm::raw_ostream &Out) const override {}
+ void printPrefix(llvm::raw_ostream &Out) const override {}
- virtual void printNode(llvm::raw_ostream &Out) const override {
+ void printNode(llvm::raw_ostream &Out) const override {
Out << getVariableName(getDecl());
}
- virtual void printSeparator(llvm::raw_ostream &Out) const override {
+ void printSeparator(llvm::raw_ostream &Out) const override {
if (getDecl()->getType()->isPointerType())
Out << "->";
else
@@ -64,11 +65,11 @@ public:
NeedsCastLocField(const FieldRegion *FR, const QualType &T)
: FieldNode(FR), CastBackType(T) {}
- virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
+ void printNoteMsg(llvm::raw_ostream &Out) const override {
Out << "uninitialized pointee ";
}
- virtual void printPrefix(llvm::raw_ostream &Out) const override {
+ void printPrefix(llvm::raw_ostream &Out) const override {
// If this object is a nonloc::LocAsInteger.
if (getDecl()->getType()->isIntegerType())
Out << "reinterpret_cast";
@@ -78,13 +79,11 @@ public:
Out << '<' << CastBackType.getAsString() << ">(";
}
- virtual void printNode(llvm::raw_ostream &Out) const override {
+ void printNode(llvm::raw_ostream &Out) const override {
Out << getVariableName(getDecl()) << ')';
}
- virtual void printSeparator(llvm::raw_ostream &Out) const override {
- Out << "->";
- }
+ void printSeparator(llvm::raw_ostream &Out) const override { Out << "->"; }
};
/// Represents a Loc field that points to itself.
@@ -93,17 +92,17 @@ class CyclicLocField final : public FieldNode {
public:
CyclicLocField(const FieldRegion *FR) : FieldNode(FR) {}
- virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
+ void printNoteMsg(llvm::raw_ostream &Out) const override {
Out << "object references itself ";
}
- virtual void printPrefix(llvm::raw_ostream &Out) const override {}
+ void printPrefix(llvm::raw_ostream &Out) const override {}
- virtual void printNode(llvm::raw_ostream &Out) const override {
+ void printNode(llvm::raw_ostream &Out) const override {
Out << getVariableName(getDecl());
}
- virtual void printSeparator(llvm::raw_ostream &Out) const override {
+ void printSeparator(llvm::raw_ostream &Out) const override {
llvm_unreachable("CyclicLocField objects must be the last node of the "
"fieldchain!");
}
@@ -123,9 +122,9 @@ struct DereferenceInfo {
/// Dereferences \p FR and returns with the pointee's region, and whether it
/// needs to be casted back to it's location type. If for whatever reason
-/// dereferencing fails, returns with None.
-static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State,
- const FieldRegion *FR);
+/// dereferencing fails, returns std::nullopt.
+static std::optional<DereferenceInfo> dereference(ProgramStateRef State,
+ const FieldRegion *FR);
/// Returns whether \p T can be (transitively) dereferenced to a void pointer
/// type (void*, void**, ...).
@@ -141,10 +140,10 @@ bool FindUninitializedFields::isDereferencableUninit(
SVal V = State->getSVal(FR);
assert((isDereferencableType(FR->getDecl()->getType()) ||
- V.getAs<nonloc::LocAsInteger>()) &&
+ isa<nonloc::LocAsInteger>(V)) &&
"This method only checks dereferenceable objects!");
- if (V.isUnknown() || V.getAs<loc::ConcreteInt>()) {
+ if (V.isUnknown() || isa<loc::ConcreteInt>(V)) {
IsAnyFieldInitialized = true;
return false;
}
@@ -161,7 +160,7 @@ bool FindUninitializedFields::isDereferencableUninit(
// At this point the pointer itself is initialized and points to a valid
// location, we'll now check the pointee.
- llvm::Optional<DereferenceInfo> DerefInfo = dereference(State, FR);
+ std::optional<DereferenceInfo> DerefInfo = dereference(State, FR);
if (!DerefInfo) {
IsAnyFieldInitialized = true;
return false;
@@ -219,8 +218,8 @@ bool FindUninitializedFields::isDereferencableUninit(
// Utility functions.
//===----------------------------------------------------------------------===//
-static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State,
- const FieldRegion *FR) {
+static std::optional<DereferenceInfo> dereference(ProgramStateRef State,
+ const FieldRegion *FR) {
llvm::SmallSet<const TypedValueRegion *, 5> VisitedRegions;
@@ -230,13 +229,13 @@ static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State,
// If the static type of the field is a void pointer, or it is a
// nonloc::LocAsInteger, we need to cast it back to the dynamic type before
// dereferencing.
- bool NeedsCastBack = isVoidPointer(FR->getDecl()->getType()) ||
- V.getAs<nonloc::LocAsInteger>();
+ bool NeedsCastBack =
+ isVoidPointer(FR->getDecl()->getType()) || isa<nonloc::LocAsInteger>(V);
// The region we'd like to acquire.
const auto *R = V.getAsRegion()->getAs<TypedValueRegion>();
if (!R)
- return None;
+ return std::nullopt;
VisitedRegions.insert(R);
@@ -247,7 +246,7 @@ static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State,
R = Tmp->getAs<TypedValueRegion>();
if (!R)
- return None;
+ return std::nullopt;
// We found a cyclic pointer, like int *ptr = (int *)&ptr.
if (!VisitedRegions.insert(R).second)
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
index 381334de068e..b05ce610067c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
@@ -11,17 +11,18 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/Basic/TargetInfo.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/CheckerContext.h"
-#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"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -39,12 +40,12 @@ enum class OpenVariant {
namespace {
class UnixAPIMisuseChecker : public Checker< check::PreStmt<CallExpr> > {
- mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce;
- mutable Optional<uint64_t> Val_O_CREAT;
+ const BugType BT_open{this, "Improper use of 'open'", categories::UnixAPI};
+ const BugType BT_pthreadOnce{this, "Improper use of 'pthread_once'",
+ categories::UnixAPI};
+ mutable std::optional<uint64_t> Val_O_CREAT;
public:
- DefaultBool CheckMisuse, CheckPortability;
-
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
void CheckOpen(CheckerContext &C, const CallExpr *CE) const;
@@ -66,7 +67,9 @@ public:
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
private:
- mutable std::unique_ptr<BugType> BT_mallocZero;
+ const BugType BT_mallocZero{
+ this, "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)",
+ categories::UnixAPI};
void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;
void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;
@@ -89,14 +92,6 @@ private:
} //end anonymous namespace
-static void LazyInitialize(const CheckerBase *Checker,
- std::unique_ptr<BugType> &BT,
- const char *name) {
- if (BT)
- return;
- BT.reset(new BugType(Checker, name, categories::UnixAPI));
-}
-
//===----------------------------------------------------------------------===//
// "open" (man 2 open)
//===----------------------------------------------------------------------===/
@@ -110,7 +105,7 @@ void UnixAPIMisuseChecker::checkPreStmt(const CallExpr *CE,
// Don't treat functions in namespaces with the same name a Unix function
// as a call to the Unix function.
const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
- if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx))
+ if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
return;
StringRef FName = C.getCalleeName(FD);
@@ -134,9 +129,7 @@ void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C,
if (!N)
return;
- LazyInitialize(this, BT_open, "Improper use of 'open'");
-
- auto Report = std::make_unique<PathSensitiveBugReport>(*BT_open, Msg, N);
+ auto Report = std::make_unique<PathSensitiveBugReport>(BT_open, Msg, N);
Report->addRange(SR);
C.emitReport(std::move(Report));
}
@@ -182,8 +175,7 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
ProgramStateRef state = C.getState();
if (CE->getNumArgs() < MinArgCount) {
- // The frontend should issue a warning for this case, so this is a sanity
- // check.
+ // The frontend should issue a warning for this case. Just return.
return;
} else if (CE->getNumArgs() == MaxArgCount) {
const Expr *Arg = CE->getArg(CreateModeArgIndex);
@@ -214,7 +206,7 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
// The definition of O_CREAT is platform specific. We need a better way
// of querying this information from the checking environment.
- if (!Val_O_CREAT.hasValue()) {
+ if (!Val_O_CREAT) {
if (C.getASTContext().getTargetInfo().getTriple().getVendor()
== llvm::Triple::Apple)
Val_O_CREAT = 0x0200;
@@ -230,14 +222,15 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
// Now check if oflags has O_CREAT set.
const Expr *oflagsEx = CE->getArg(FlagsArgIndex);
const SVal V = C.getSVal(oflagsEx);
- if (!V.getAs<NonLoc>()) {
+ if (!isa<NonLoc>(V)) {
// The case where 'V' can be a location can only be due to a bad header,
// so in this case bail out.
return;
}
NonLoc oflags = V.castAs<NonLoc>();
NonLoc ocreateFlag = C.getSValBuilder()
- .makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType()).castAs<NonLoc>();
+ .makeIntVal(*Val_O_CREAT, oflagsEx->getType())
+ .castAs<NonLoc>();
SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
oflags, ocreateFlag,
oflagsEx->getType());
@@ -303,10 +296,8 @@ void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,
if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
os << " Perhaps you intended to declare the variable as 'static'?";
- LazyInitialize(this, BT_pthreadOnce, "Improper use of 'pthread_once'");
-
auto report =
- std::make_unique<PathSensitiveBugReport>(*BT_pthreadOnce, os.str(), N);
+ std::make_unique<PathSensitiveBugReport>(BT_pthreadOnce, os.str(), N);
report->addRange(CE->getArg(0)->getSourceRange());
C.emitReport(std::move(report));
}
@@ -343,14 +334,11 @@ bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(
if (!N)
return false;
- LazyInitialize(this, BT_mallocZero,
- "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
-
SmallString<256> S;
llvm::raw_svector_ostream os(S);
os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
auto report =
- std::make_unique<PathSensitiveBugReport>(*BT_mallocZero, os.str(), N);
+ std::make_unique<PathSensitiveBugReport>(BT_mallocZero, os.str(), N);
report->addRange(arg->getSourceRange());
bugreporter::trackExpressionValue(N, arg, *report);
@@ -366,7 +354,7 @@ void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C,
const unsigned numArgs,
const unsigned sizeArg,
const char *fn) const {
- // Sanity check for the correct number of arguments
+ // Check for the correct number of arguments.
if (CE->getNumArgs() != numArgs)
return;
@@ -466,7 +454,7 @@ void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,
// Don't treat functions in namespaces with the same name a Unix function
// as a call to the Unix function.
const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
- if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx))
+ if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
return;
StringRef FName = C.getCalleeName(FD);
@@ -504,7 +492,7 @@ void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,
mgr.registerChecker<CHECKERNAME>(); \
} \
\
- bool ento::shouldRegister##CHECKERNAME(const CheckerManager &mgr) { \
+ 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 d231be64c2e1..d24a124f5ffe 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
@@ -24,6 +24,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "llvm/ADT/SmallSet.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -58,9 +59,8 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
const ParentMap *PM = nullptr;
const LocationContext *LC = nullptr;
// Iterate over ExplodedGraph
- for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end();
- I != E; ++I) {
- const ProgramPoint &P = I->getLocation();
+ for (const ExplodedNode &N : G.nodes()) {
+ const ProgramPoint &P = N.getLocation();
LC = P.getLocationContext();
if (!LC->inTopFrame())
continue;
@@ -74,7 +74,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
if (!PM)
PM = &LC->getParentMap();
- if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
+ if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
const CFGBlock *CB = BE->getBlock();
reachable.insert(CB->getBlockID());
}
@@ -92,8 +92,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
return;
// Find CFGBlocks that were not covered by any node
- for (CFG::const_iterator I = C->begin(), E = C->end(); I != E; ++I) {
- const CFGBlock *CB = *I;
+ for (const CFGBlock *CB : *C) {
// Check if the block is unreachable
if (reachable.count(CB->getBlockID()))
continue;
@@ -129,7 +128,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
bool foundUnreachable = false;
for (CFGBlock::const_iterator ci = CB->begin(), ce = CB->end();
ci != ce; ++ci) {
- if (Optional<CFGStmt> S = (*ci).getAs<CFGStmt>())
+ if (std::optional<CFGStmt> S = (*ci).getAs<CFGStmt>())
if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) {
if (CE->getBuiltinCallee() == Builtin::BI__builtin_unreachable ||
CE->isBuiltinAssumeFalse(Eng.getContext())) {
@@ -180,34 +179,30 @@ void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB,
CFGBlocksSet &visited) {
visited.insert(CB->getBlockID());
- for (CFGBlock::const_pred_iterator I = CB->pred_begin(), E = CB->pred_end();
- I != E; ++I) {
- if (!*I)
+ for (const CFGBlock *PredBlock : CB->preds()) {
+ if (!PredBlock)
continue;
- if (!reachable.count((*I)->getBlockID())) {
+ if (!reachable.count(PredBlock->getBlockID())) {
// If we find an unreachable predecessor, mark this block as reachable so
// we don't report this block
reachable.insert(CB->getBlockID());
- if (!visited.count((*I)->getBlockID()))
+ if (!visited.count(PredBlock->getBlockID()))
// If we haven't previously visited the unreachable predecessor, recurse
- FindUnreachableEntryPoints(*I, reachable, visited);
+ FindUnreachableEntryPoints(PredBlock, reachable, visited);
}
}
}
// Find the Stmt* in a CFGBlock for reporting a warning
const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) {
- for (CFGBlock::const_iterator I = CB->begin(), E = CB->end(); I != E; ++I) {
- if (Optional<CFGStmt> S = I->getAs<CFGStmt>()) {
+ for (const CFGElement &Elem : *CB) {
+ if (std::optional<CFGStmt> S = Elem.getAs<CFGStmt>()) {
if (!isa<DeclStmt>(S->getStmt()))
return S->getStmt();
}
}
- if (const Stmt *S = CB->getTerminatorStmt())
- return S;
- else
- return nullptr;
+ return CB->getTerminatorStmt();
}
// Determines if the path to this CFGBlock contained an element that infers this
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
index 96501215c689..d76fe4991869 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
@@ -13,9 +13,9 @@
//
//===----------------------------------------------------------------------===//
-#include "Taint.h"
#include "clang/AST/CharUnits.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Checkers/Taint.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -24,6 +24,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -33,14 +34,11 @@ namespace {
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,
- VLA_Overflow
- };
+ const BugType BT{this, "Dangerous variable-length array (VLA) declaration"};
+ const BugType TaintBT{this,
+ "Dangerous variable-length array (VLA) declaration",
+ categories::TaintedData};
+ enum VLASize_Kind { VLA_Garbage, VLA_Zero, VLA_Negative, VLA_Overflow };
/// Check a VLA for validity.
/// Every dimension of the array and the total size is checked for validity.
@@ -54,8 +52,10 @@ class VLASizeChecker
const Expr *SizeE) const;
void reportBug(VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State,
- CheckerContext &C,
- std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const;
+ CheckerContext &C) const;
+
+ void reportTaintBug(const Expr *SizeE, ProgramStateRef State,
+ CheckerContext &C, SVal TaintedSVal) const;
public:
void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const;
@@ -166,8 +166,7 @@ ProgramStateRef VLASizeChecker::checkVLAIndexSize(CheckerContext &C,
// Check if the size is tainted.
if (isTainted(State, SizeV)) {
- reportBug(VLA_Tainted, SizeE, nullptr, C,
- std::make_unique<TaintBugVisitor>(SizeV));
+ reportTaintBug(SizeE, State, C, SizeV);
return nullptr;
}
@@ -191,8 +190,9 @@ ProgramStateRef VLASizeChecker::checkVLAIndexSize(CheckerContext &C,
QualType SizeTy = SizeE->getType();
DefinedOrUnknownSVal Zero = SVB.makeZeroVal(SizeTy);
- SVal LessThanZeroVal = SVB.evalBinOp(State, BO_LT, SizeD, Zero, SizeTy);
- if (Optional<DefinedSVal> LessThanZeroDVal =
+ SVal LessThanZeroVal =
+ SVB.evalBinOp(State, BO_LT, SizeD, Zero, SVB.getConditionType());
+ if (std::optional<DefinedSVal> LessThanZeroDVal =
LessThanZeroVal.getAs<DefinedSVal>()) {
ConstraintManager &CM = C.getConstraintManager();
ProgramStateRef StatePos, StateNeg;
@@ -208,17 +208,34 @@ ProgramStateRef VLASizeChecker::checkVLAIndexSize(CheckerContext &C,
return State;
}
-void VLASizeChecker::reportBug(
- VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State,
- CheckerContext &C, std::unique_ptr<BugReporterVisitor> Visitor) const {
+void VLASizeChecker::reportTaintBug(const Expr *SizeE, ProgramStateRef State,
+ CheckerContext &C, SVal TaintedSVal) const {
// Generate an error node.
ExplodedNode *N = C.generateErrorNode(State);
if (!N)
return;
- if (!BT)
- BT.reset(new BuiltinBug(
- this, "Dangerous variable-length array (VLA) declaration"));
+ SmallString<256> buf;
+ llvm::raw_svector_ostream os(buf);
+ os << "Declared variable-length array (VLA) ";
+ os << "has tainted size";
+
+ auto report = std::make_unique<PathSensitiveBugReport>(TaintBT, os.str(), N);
+ report->addRange(SizeE->getSourceRange());
+ bugreporter::trackExpressionValue(N, SizeE, *report);
+ // The vla size may be a complex expression where multiple memory locations
+ // are tainted.
+ for (auto Sym : getTaintedSymbols(State, TaintedSVal))
+ report->markInteresting(Sym);
+ C.emitReport(std::move(report));
+}
+
+void VLASizeChecker::reportBug(VLASize_Kind Kind, const Expr *SizeE,
+ ProgramStateRef State, CheckerContext &C) const {
+ // Generate an error node.
+ ExplodedNode *N = C.generateErrorNode(State);
+ if (!N)
+ return;
SmallString<256> buf;
llvm::raw_svector_ostream os(buf);
@@ -230,9 +247,6 @@ void VLASizeChecker::reportBug(
case VLA_Zero:
os << "has zero size";
break;
- case VLA_Tainted:
- os << "has tainted size";
- break;
case VLA_Negative:
os << "has negative size";
break;
@@ -241,8 +255,7 @@ void VLASizeChecker::reportBug(
break;
}
- auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
- report->addVisitor(std::move(Visitor));
+ auto report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
report->addRange(SizeE->getSourceRange());
bugreporter::trackExpressionValue(N, SizeE, *report);
C.emitReport(std::move(report));
@@ -278,8 +291,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
if (!State)
return;
- auto ArraySizeNL = ArraySize.getAs<NonLoc>();
- if (!ArraySizeNL) {
+ if (!isa<NonLoc>(ArraySize)) {
// Array size could not be determined but state may contain new assumptions.
C.addTransition(State);
return;
@@ -289,7 +301,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
if (VD) {
State =
setDynamicExtent(State, State->getRegion(VD, C.getLocationContext()),
- ArraySize.castAs<DefinedOrUnknownSVal>(), SVB);
+ ArraySize.castAs<NonLoc>(), SVB);
}
// Remember our assumptions!
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
index dde5912b6d6e..2d1b873abf73 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
@@ -15,6 +15,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -45,7 +46,7 @@ public:
CK_NumCheckKinds
};
- DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ bool ChecksEnabled[CK_NumCheckKinds] = {false};
CheckerNameRef CheckNames[CK_NumCheckKinds];
void checkPreStmt(const VAArgExpr *VAA, CheckerContext &C) const;
@@ -99,42 +100,41 @@ private:
};
const SmallVector<ValistChecker::VAListAccepter, 15>
- ValistChecker::VAListAccepters = {
- {{"vfprintf", 3}, 2},
- {{"vfscanf", 3}, 2},
- {{"vprintf", 2}, 1},
- {{"vscanf", 2}, 1},
- {{"vsnprintf", 4}, 3},
- {{"vsprintf", 3}, 2},
- {{"vsscanf", 3}, 2},
- {{"vfwprintf", 3}, 2},
- {{"vfwscanf", 3}, 2},
- {{"vwprintf", 2}, 1},
- {{"vwscanf", 2}, 1},
- {{"vswprintf", 4}, 3},
- // vswprintf is the wide version of vsnprintf,
- // vsprintf has no wide version
- {{"vswscanf", 3}, 2}};
-
-const CallDescription
- ValistChecker::VaStart("__builtin_va_start", /*Args=*/2, /*Params=*/1),
- ValistChecker::VaCopy("__builtin_va_copy", 2),
- ValistChecker::VaEnd("__builtin_va_end", 1);
+ ValistChecker::VAListAccepters = {{{{"vfprintf"}, 3}, 2},
+ {{{"vfscanf"}, 3}, 2},
+ {{{"vprintf"}, 2}, 1},
+ {{{"vscanf"}, 2}, 1},
+ {{{"vsnprintf"}, 4}, 3},
+ {{{"vsprintf"}, 3}, 2},
+ {{{"vsscanf"}, 3}, 2},
+ {{{"vfwprintf"}, 3}, 2},
+ {{{"vfwscanf"}, 3}, 2},
+ {{{"vwprintf"}, 2}, 1},
+ {{{"vwscanf"}, 2}, 1},
+ {{{"vswprintf"}, 4}, 3},
+ // vswprintf is the wide version of
+ // vsnprintf, vsprintf has no wide version
+ {{{"vswscanf"}, 3}, 2}};
+
+const CallDescription ValistChecker::VaStart({"__builtin_va_start"}, /*Args=*/2,
+ /*Params=*/1),
+ ValistChecker::VaCopy({"__builtin_va_copy"}, 2),
+ ValistChecker::VaEnd({"__builtin_va_end"}, 1);
} // end anonymous namespace
void ValistChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
if (!Call.isGlobalCFunction())
return;
- if (Call.isCalled(VaStart))
+ if (VaStart.matches(Call))
checkVAListStartCall(Call, C, false);
- else if (Call.isCalled(VaCopy))
+ else if (VaCopy.matches(Call))
checkVAListStartCall(Call, C, true);
- else if (Call.isCalled(VaEnd))
+ else if (VaEnd.matches(Call))
checkVAListEndCall(Call, C);
else {
for (auto FuncInfo : VAListAccepters) {
- if (!Call.isCalled(FuncInfo.Func))
+ if (!FuncInfo.Func.matches(Call))
continue;
bool Symbolic;
const MemRegion *VAList =
@@ -177,7 +177,7 @@ const MemRegion *ValistChecker::getVAListAsRegion(SVal SV, const Expr *E,
if (isa<ParmVarDecl>(DeclReg->getDecl()))
Reg = C.getState()->getSVal(SV.castAs<Loc>()).getAsRegion();
}
- IsSymbolic = Reg && Reg->getAs<SymbolicRegion>();
+ IsSymbolic = Reg && Reg->getBaseRegion()->getAs<SymbolicRegion>();
// Some VarRegion based VA lists reach here as ElementRegions.
const auto *EReg = dyn_cast_or_null<ElementRegion>(Reg);
return (EReg && VaListModelledAsArray) ? EReg->getSuperRegion() : Reg;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
index 8f147026ae19..cb73ac68edd1 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
@@ -9,7 +9,7 @@
// This file defines vfork checker which checks for dangerous uses of vfork.
// Vforked process shares memory (including stack) with parent so it's
// range of actions is significantly limited: can't write variables,
-// can't call functions not in whitelist, etc. For more details, see
+// can't call functions not in the allowed list, etc. For more details, see
// http://man7.org/linux/man-pages/man2/vfork.2.html
//
// This checker checks for prohibited constructs in vforked process.
@@ -35,6 +35,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/AST/ParentMap.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -43,20 +44,21 @@ namespace {
class VforkChecker : public Checker<check::PreCall, check::PostCall,
check::Bind, check::PreStmt<ReturnStmt>> {
- mutable std::unique_ptr<BuiltinBug> BT;
- mutable llvm::SmallSet<const IdentifierInfo *, 10> VforkWhitelist;
- mutable const IdentifierInfo *II_vfork;
+ const BugType BT{this, "Dangerous construct in a vforked process"};
+ mutable llvm::SmallSet<const IdentifierInfo *, 10> VforkAllowlist;
+ mutable const IdentifierInfo *II_vfork = nullptr;
static bool isChildProcess(const ProgramStateRef State);
bool isVforkCall(const Decl *D, CheckerContext &C) const;
- bool isCallWhitelisted(const IdentifierInfo *II, CheckerContext &C) const;
+ bool isCallExplicitelyAllowed(const IdentifierInfo *II,
+ CheckerContext &C) const;
void reportBug(const char *What, CheckerContext &C,
const char *Details = nullptr) const;
public:
- VforkChecker() : II_vfork(nullptr) {}
+ VforkChecker() = default;
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
@@ -93,9 +95,9 @@ bool VforkChecker::isVforkCall(const Decl *D, CheckerContext &C) const {
}
// Returns true iff ok to call function after successful vfork.
-bool VforkChecker::isCallWhitelisted(const IdentifierInfo *II,
- CheckerContext &C) const {
- if (VforkWhitelist.empty()) {
+bool VforkChecker::isCallExplicitelyAllowed(const IdentifierInfo *II,
+ CheckerContext &C) const {
+ if (VforkAllowlist.empty()) {
// According to manpage.
const char *ids[] = {
"_Exit",
@@ -112,19 +114,15 @@ bool VforkChecker::isCallWhitelisted(const IdentifierInfo *II,
ASTContext &AC = C.getASTContext();
for (const char **id = ids; *id; ++id)
- VforkWhitelist.insert(&AC.Idents.get(*id));
+ VforkAllowlist.insert(&AC.Idents.get(*id));
}
- return VforkWhitelist.count(II);
+ return VforkAllowlist.count(II);
}
void VforkChecker::reportBug(const char *What, CheckerContext &C,
const char *Details) const {
if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
- if (!BT)
- BT.reset(new BuiltinBug(this,
- "Dangerous construct in a vforked process"));
-
SmallString<256> buf;
llvm::raw_svector_ostream os(buf);
@@ -133,7 +131,7 @@ void VforkChecker::reportBug(const char *What, CheckerContext &C,
if (Details)
os << "; " << Details;
- auto Report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
+ auto Report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
// TODO: mark vfork call in BugReportVisitor
C.emitReport(std::move(Report));
}
@@ -153,8 +151,8 @@ void VforkChecker::checkPostCall(const CallEvent &Call,
// Get return value of vfork.
SVal VforkRetVal = Call.getReturnValue();
- Optional<DefinedOrUnknownSVal> DVal =
- VforkRetVal.getAs<DefinedOrUnknownSVal>();
+ std::optional<DefinedOrUnknownSVal> DVal =
+ VforkRetVal.getAs<DefinedOrUnknownSVal>();
if (!DVal)
return;
@@ -179,12 +177,13 @@ void VforkChecker::checkPostCall(const CallEvent &Call,
C.addTransition(ChildState);
}
-// Prohibit calls to non-whitelist functions in child process.
+// Prohibit calls to functions in child process which are not explicitly
+// allowed.
void VforkChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
- if (isChildProcess(State)
- && !isCallWhitelisted(Call.getCalleeIdentifier(), C))
+ if (isChildProcess(State) &&
+ !isCallExplicitelyAllowed(Call.getCalleeIdentifier(), C))
reportBug("This function call", C);
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
index 1c589e3468c2..33a9a07f9d32 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
@@ -116,7 +116,7 @@ void VirtualCallChecker::checkPreCall(const CallEvent &Call,
if (!ObState)
return;
- bool IsPure = MD->isPure();
+ bool IsPure = MD->isPureVirtual();
// At this point we're sure that we're calling a virtual method
// during construction or destruction, so we'll emit a report.
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 9c7a59971763..64028b277021 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -12,8 +12,8 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
+#include <optional>
-using llvm::Optional;
namespace clang {
std::pair<const Expr *, bool>
@@ -34,8 +34,7 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
}
if (auto *call = dyn_cast<CallExpr>(E)) {
if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(call)) {
- Optional<bool> IsGetterOfRefCt =
- isGetterOfRefCounted(memberCall->getMethodDecl());
+ std::optional<bool> IsGetterOfRefCt = isGetterOfRefCounted(memberCall->getMethodDecl());
if (IsGetterOfRefCt && *IsGetterOfRefCt) {
E = memberCall->getImplicitObjectArgument();
if (StopAtFirstRefCountedObj) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
index ed4577755457..e35ea4ef05dd 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
@@ -17,10 +17,6 @@
#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
@@ -29,7 +25,7 @@ class Expr;
/// values).
///
/// For more context see Static Analyzer checkers documentation - specifically
-/// webkit.UncountedCallArgsChecker checker. Whitelist of transformations:
+/// webkit.UncountedCallArgsChecker checker. Allowed list of transformations:
/// - constructors of ref-counted types (including factory methods)
/// - getters of ref-counted types
/// - member overloaded operators
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
index 97f75135bf92..c753ed84a700 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
@@ -17,8 +17,8 @@
#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"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -69,7 +69,7 @@ public:
if (shouldSkipDecl(RD))
return;
- for (auto Member : RD->fields()) {
+ for (auto *Member : RD->fields()) {
const Type *MemberType = Member->getType().getTypePtrOrNull();
if (!MemberType)
continue;
@@ -77,9 +77,9 @@ public:
if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
// If we don't see the definition we just don't know.
if (MemberCXXRD->hasDefinition()) {
- llvm::Optional<bool> isRCAble = isRefCountable(MemberCXXRD);
- if (isRCAble && *isRCAble)
- reportBug(Member, MemberType, MemberCXXRD, RD);
+ std::optional<bool> isRCAble = isRefCountable(MemberCXXRD);
+ if (isRCAble && *isRCAble)
+ reportBug(Member, MemberType, MemberCXXRD, RD);
}
}
}
@@ -103,7 +103,7 @@ public:
const auto Kind = RD->getTagKind();
// FIMXE: Should we check union members too?
- if (Kind != TTK_Struct && Kind != TTK_Class)
+ if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
return true;
// Ignore CXXRecords that come from system headers.
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index a198943c9433..d2b663410580 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -12,31 +12,21 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
-#include "llvm/ADT/Optional.h"
+#include <optional>
-using llvm::Optional;
using namespace clang;
namespace {
-bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
+bool hasPublicMethodInBaseClass(const CXXRecordDecl *R,
+ const char *NameToMatch) {
assert(R);
assert(R->hasDefinition());
- 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;
- }
+ if (MethodName == NameToMatch && MD->getAccess() == AS_public)
+ return true;
}
return false;
}
@@ -45,54 +35,70 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
namespace clang {
-llvm::Optional<const clang::CXXRecordDecl *>
-isRefCountable(const CXXBaseSpecifier *Base) {
+std::optional<const clang::CXXRecordDecl *>
+hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch) {
assert(Base);
const Type *T = Base->getType().getTypePtrOrNull();
if (!T)
- return llvm::None;
+ return std::nullopt;
const CXXRecordDecl *R = T->getAsCXXRecordDecl();
if (!R)
- return llvm::None;
+ return std::nullopt;
if (!R->hasDefinition())
- return llvm::None;
+ return std::nullopt;
- return hasPublicRefAndDeref(R) ? R : nullptr;
+ return hasPublicMethodInBaseClass(R, NameToMatch) ? R : nullptr;
}
-llvm::Optional<bool> isRefCountable(const CXXRecordDecl *R) {
+std::optional<bool> isRefCountable(const CXXRecordDecl* R)
+{
assert(R);
R = R->getDefinition();
if (!R)
- return llvm::None;
+ return std::nullopt;
- if (hasPublicRefAndDeref(R))
+ bool hasRef = hasPublicMethodInBaseClass(R, "ref");
+ bool hasDeref = hasPublicMethodInBaseClass(R, "deref");
+ if (hasRef && hasDeref)
return true;
CXXBasePaths Paths;
Paths.setOrigin(const_cast<CXXRecordDecl *>(R));
bool AnyInconclusiveBase = false;
- const auto isRefCountableBase =
+ const auto hasPublicRefInBase =
[&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
- Optional<const clang::CXXRecordDecl *> IsRefCountable =
- clang::isRefCountable(Base);
- if (!IsRefCountable) {
+ auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
+ if (!hasRefInBase) {
AnyInconclusiveBase = true;
return false;
}
- return (*IsRefCountable) != nullptr;
+ return (*hasRefInBase) != nullptr;
};
- bool BasesResult = R->lookupInBases(isRefCountableBase, Paths,
+ hasRef = hasRef || R->lookupInBases(hasPublicRefInBase, Paths,
/*LookupInDependent =*/true);
if (AnyInconclusiveBase)
- return llvm::None;
+ return std::nullopt;
- return BasesResult;
+ const auto hasPublicDerefInBase =
+ [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
+ auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
+ if (!hasDerefInBase) {
+ AnyInconclusiveBase = true;
+ return false;
+ }
+ return (*hasDerefInBase) != nullptr;
+ };
+ hasDeref = hasDeref || R->lookupInBases(hasPublicDerefInBase, Paths,
+ /*LookupInDependent =*/true);
+ if (AnyInconclusiveBase)
+ return std::nullopt;
+
+ return hasRef && hasDeref;
}
bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
@@ -112,19 +118,21 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
|| FunctionName == "Identifier";
}
-llvm::Optional<bool> isUncounted(const CXXRecordDecl *Class) {
+std::optional<bool> isUncounted(const CXXRecordDecl* Class)
+{
// Keep isRefCounted first as it's cheaper.
if (isRefCounted(Class))
return false;
- llvm::Optional<bool> IsRefCountable = isRefCountable(Class);
+ std::optional<bool> IsRefCountable = isRefCountable(Class);
if (!IsRefCountable)
- return llvm::None;
+ return std::nullopt;
return (*IsRefCountable);
}
-llvm::Optional<bool> isUncountedPtr(const Type *T) {
+std::optional<bool> isUncountedPtr(const Type* T)
+{
assert(T);
if (T->isPointerType() || T->isReferenceType()) {
@@ -135,7 +143,8 @@ llvm::Optional<bool> isUncountedPtr(const Type *T) {
return false;
}
-Optional<bool> isGetterOfRefCounted(const CXXMethodDecl *M) {
+std::optional<bool> isGetterOfRefCounted(const CXXMethodDecl* M)
+{
assert(M);
if (isa<CXXMethodDecl>(M)) {
@@ -183,8 +192,7 @@ bool isPtrConversion(const FunctionDecl *F) {
// FIXME: check # of params == 1
const auto FunctionName = safeGetName(F);
if (FunctionName == "getPtr" || FunctionName == "WeakPtr" ||
- FunctionName == "makeWeakPtr"
-
+ FunctionName == "dynamicDowncast"
|| FunctionName == "downcast" || FunctionName == "bitwise_cast")
return true;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 730a59977175..45b21cc09184 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -10,12 +10,12 @@
#define LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H
#include "llvm/ADT/APInt.h"
+#include <optional>
namespace clang {
class CXXBaseSpecifier;
class CXXMethodDecl;
class CXXRecordDecl;
-class Expr;
class FunctionDecl;
class Type;
@@ -26,32 +26,32 @@ class Type;
// 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, None if inconclusive.
-llvm::Optional<const clang::CXXRecordDecl *>
-isRefCountable(const clang::CXXBaseSpecifier *Base);
+/// \returns CXXRecordDecl of the base if the type has ref as a public method,
+/// nullptr if not, std::nullopt if inconclusive.
+std::optional<const clang::CXXRecordDecl *>
+hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch);
-/// \returns true if \p Class is ref-countable, false if not, None if
+/// \returns true if \p Class is ref-countable, false if not, std::nullopt if
/// inconclusive.
-llvm::Optional<bool> isRefCountable(const clang::CXXRecordDecl *Class);
+std::optional<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, None if inconclusive.
-llvm::Optional<bool> isUncounted(const clang::CXXRecordDecl *Class);
+/// not, std::nullopt if inconclusive.
+std::optional<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, None if inconclusive.
-llvm::Optional<bool> isUncountedPtr(const clang::Type *T);
+/// class, false if not, std::nullopt if inconclusive.
+std::optional<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.
-llvm::Optional<bool> isGetterOfRefCounted(const clang::CXXMethodDecl *Method);
+std::optional<bool> isGetterOfRefCounted(const clang::CXXMethodDecl* Method);
/// \returns true if \p F is a conversion between ref-countable or ref-counted
/// pointer types.
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
index fa9ece217cc0..d879c110b75d 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
@@ -14,6 +14,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -76,15 +77,53 @@ public:
(AccSpec == AS_none && RD->isClass()))
return false;
- llvm::Optional<const CXXRecordDecl *> RefCntblBaseRD =
- isRefCountable(Base);
- if (!RefCntblBaseRD || !(*RefCntblBaseRD))
+ auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
+ auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
+
+ bool hasRef = hasRefInBase && *hasRefInBase != nullptr;
+ bool hasDeref = hasDerefInBase && *hasDerefInBase != nullptr;
+
+ QualType T = Base->getType();
+ if (T.isNull())
+ return false;
+
+ const CXXRecordDecl *C = T->getAsCXXRecordDecl();
+ if (!C)
+ return false;
+ bool AnyInconclusiveBase = false;
+ const auto hasPublicRefInBase =
+ [&AnyInconclusiveBase](const CXXBaseSpecifier *Base,
+ CXXBasePath &) {
+ auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
+ if (!hasRefInBase) {
+ AnyInconclusiveBase = true;
+ return false;
+ }
+ return (*hasRefInBase) != nullptr;
+ };
+ const auto hasPublicDerefInBase = [&AnyInconclusiveBase](
+ const CXXBaseSpecifier *Base,
+ CXXBasePath &) {
+ auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
+ if (!hasDerefInBase) {
+ AnyInconclusiveBase = true;
+ return false;
+ }
+ return (*hasDerefInBase) != nullptr;
+ };
+ CXXBasePaths Paths;
+ Paths.setOrigin(C);
+ hasRef = hasRef || C->lookupInBases(hasPublicRefInBase, Paths,
+ /*LookupInDependent =*/true);
+ hasDeref = hasDeref || C->lookupInBases(hasPublicDerefInBase, Paths,
+ /*LookupInDependent =*/true);
+ if (AnyInconclusiveBase || !hasRef || !hasDeref)
return false;
- const auto *Dtor = (*RefCntblBaseRD)->getDestructor();
+ const auto *Dtor = C->getDestructor();
if (!Dtor || !Dtor->isVirtual()) {
ProblematicBaseSpecifier = Base;
- ProblematicBaseClass = *RefCntblBaseRD;
+ ProblematicBaseClass = C;
return true;
}
@@ -114,7 +153,7 @@ public:
return true;
const auto Kind = RD->getTagKind();
- if (Kind != TTK_Struct && Kind != TTK_Class)
+ if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
return true;
// Ignore CXXRecords that come from system headers.
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index d70bd9489d2c..31ccae8b097b 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -18,7 +18,7 @@
#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 <optional>
using namespace clang;
using namespace ento;
@@ -68,8 +68,7 @@ public:
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);
+ unsigned ArgIdx = isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
for (auto P = F->param_begin();
// FIXME: Also check variadic function parameters.
@@ -86,7 +85,7 @@ public:
continue; // FIXME? Should we bail?
// FIXME: more complex types (arrays, references to raw pointers, etc)
- Optional<bool> IsUncounted = isUncountedPtr(ArgType);
+ std::optional<bool> IsUncounted = isUncountedPtr(ArgType);
if (!IsUncounted || !(*IsUncounted))
continue;
@@ -149,7 +148,7 @@ public:
auto name = safeGetName(Callee);
if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
- name == "makeWeakPtr" || name == "downcast" || name == "bitwise_cast" ||
+ name == "dynamicDowncast" || name == "downcast" || name == "bitwise_cast" ||
name == "is" || name == "equal" || name == "hash" ||
name == "isType"
// FIXME: Most/all of these should be implemented via attributes.
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
index deebbd603b2c..a226a01ec0a5 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
@@ -14,6 +14,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -24,7 +25,7 @@ class UncountedLambdaCapturesChecker
private:
BugType Bug{this, "Lambda capture of uncounted variable",
"WebKit coding guidelines"};
- mutable BugReporter *BR;
+ mutable BugReporter *BR = nullptr;
public:
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
@@ -57,18 +58,18 @@ public:
void visitLambdaExpr(LambdaExpr *L) const {
for (const LambdaCapture &C : L->captures()) {
if (C.capturesVariable()) {
- VarDecl *CapturedVar = C.getCapturedVar();
+ ValueDecl *CapturedVar = C.getCapturedVar();
if (auto *CapturedVarType = CapturedVar->getType().getTypePtrOrNull()) {
- Optional<bool> IsUncountedPtr = isUncountedPtr(CapturedVarType);
- if (IsUncountedPtr && *IsUncountedPtr) {
- reportBug(C, CapturedVar, CapturedVarType);
- }
+ std::optional<bool> IsUncountedPtr = isUncountedPtr(CapturedVarType);
+ if (IsUncountedPtr && *IsUncountedPtr) {
+ reportBug(C, CapturedVar, CapturedVarType);
+ }
}
}
}
}
- void reportBug(const LambdaCapture &Capture, VarDecl *CapturedVar,
+ void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar,
const Type *T) const {
assert(CapturedVar);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp
index 7e86f28cb70f..5a72f53b12ed 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp
@@ -19,7 +19,7 @@
#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 <optional>
using namespace clang;
using namespace ento;
@@ -169,7 +169,7 @@ public:
if (!ArgType)
return;
- Optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
+ std::optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
if (IsUncountedPtr && *IsUncountedPtr) {
const Expr *const InitExpr = V->getInit();
if (!InitExpr)
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h
index ec612dde3b8b..b2d17420686e 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/YAMLTraits.h"
+#include <optional>
namespace clang {
namespace ento {
@@ -25,20 +26,20 @@ namespace ento {
/// template parameter must have a yaml MappingTraits.
/// Emit diagnostic error in case of any failure.
template <class T, class Checker>
-llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk,
- StringRef Option, StringRef ConfigFile) {
+std::optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk,
+ StringRef Option, StringRef ConfigFile) {
if (ConfigFile.trim().empty())
- return None;
+ return std::nullopt;
llvm::vfs::FileSystem *FS = llvm::vfs::getRealFileSystem().get();
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
FS->getBufferForFile(ConfigFile.str());
- if (std::error_code ec = Buffer.getError()) {
+ if (Buffer.getError()) {
Mgr.reportInvalidCheckerOptionValue(Chk, Option,
"a valid filename instead of '" +
std::string(ConfigFile) + "'");
- return None;
+ return std::nullopt;
}
llvm::yaml::Input Input(Buffer.get()->getBuffer());
@@ -48,7 +49,7 @@ llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk,
if (std::error_code ec = Input.error()) {
Mgr.reportInvalidCheckerOptionValue(Chk, Option,
"a valid yaml file: " + ec.message());
- return None;
+ return std::nullopt;
}
return Config;
@@ -57,4 +58,4 @@ llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk,
} // namespace ento
} // namespace clang
-#endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MOVE_H
+#endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKER_YAML_H
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
new file mode 100644
index 000000000000..e5dd907c660d
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
@@ -0,0 +1,353 @@
+//== InvalidPtrChecker.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 InvalidPtrChecker which finds usages of possibly
+// invalidated pointer.
+// CERT SEI Rules ENV31-C and ENV34-C
+// For more information see:
+// https://wiki.sei.cmu.edu/confluence/x/8tYxBQ
+// https://wiki.sei.cmu.edu/confluence/x/5NUxBQ
+//===----------------------------------------------------------------------===//
+
+#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/CallDescription.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class InvalidPtrChecker
+ : public Checker<check::Location, check::BeginFunction, check::PostCall> {
+private:
+ // For accurate emission of NoteTags, the BugType of this checker should have
+ // a unique address.
+ BugType InvalidPtrBugType{this, "Use of invalidated pointer",
+ categories::MemoryError};
+
+ void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const;
+
+ using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call,
+ CheckerContext &C) const;
+
+ // SEI CERT ENV31-C
+
+ // If set to true, consider getenv calls as invalidating operations on the
+ // environment variable buffer. This is implied in the standard, but in
+ // practice does not cause problems (in the commonly used environments).
+ bool InvalidatingGetEnv = false;
+
+ // GetEnv can be treated invalidating and non-invalidating as well.
+ const CallDescription GetEnvCall{{"getenv"}, 1};
+
+ const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = {
+ {{{"setenv"}, 3}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{{"unsetenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{{"putenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{{"_putenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{{"_wputenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ };
+
+ void postPreviousReturnInvalidatingCall(const CallEvent &Call,
+ CheckerContext &C) const;
+
+ // SEI CERT ENV34-C
+ const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = {
+ {{{"setlocale"}, 2},
+ &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ {{{"strerror"}, 1},
+ &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ {{{"localeconv"}, 0},
+ &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ {{{"asctime"}, 1},
+ &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ };
+
+ // The private members of this checker corresponding to commandline options
+ // are set in this function.
+ friend void ento::registerInvalidPtrChecker(CheckerManager &);
+
+public:
+ // Obtain the environment pointer from 'main()' (if present).
+ void checkBeginFunction(CheckerContext &C) const;
+
+ // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
+ // pointer from 'main()'
+ // Handle functions in PreviousCallInvalidatingFunctions.
+ // Also, check if invalidated region is passed to a
+ // conservatively evaluated function call as an argument.
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+
+ // Check if invalidated region is being dereferenced.
+ void checkLocation(SVal l, bool isLoad, const Stmt *S,
+ CheckerContext &C) const;
+
+private:
+ const NoteTag *createEnvInvalidationNote(CheckerContext &C,
+ ProgramStateRef State,
+ StringRef FunctionName) const;
+};
+
+} // namespace
+
+// Set of memory regions that were invalidated
+REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *)
+
+// Stores the region of the environment pointer of 'main' (if present).
+REGISTER_TRAIT_WITH_PROGRAMSTATE(MainEnvPtrRegion, const MemRegion *)
+
+// Stores the regions of environments returned by getenv calls.
+REGISTER_SET_WITH_PROGRAMSTATE(GetenvEnvPtrRegions, const MemRegion *)
+
+// Stores key-value pairs, where key is function declaration and value is
+// pointer to memory region returned by previous call of this function
+REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *,
+ const MemRegion *)
+
+const NoteTag *InvalidPtrChecker::createEnvInvalidationNote(
+ CheckerContext &C, ProgramStateRef State, StringRef FunctionName) const {
+
+ const MemRegion *MainRegion = State->get<MainEnvPtrRegion>();
+ const auto GetenvRegions = State->get<GetenvEnvPtrRegions>();
+
+ return C.getNoteTag([this, MainRegion, GetenvRegions,
+ FunctionName = std::string{FunctionName}](
+ PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
+ // Only handle the BugType of this checker.
+ if (&BR.getBugType() != &InvalidPtrBugType)
+ return;
+
+ // Mark all regions that were interesting before as NOT interesting now
+ // to avoid extra notes coming from invalidation points higher up the
+ // bugpath. This ensures that only the last invalidation point is marked
+ // with a note tag.
+ llvm::SmallVector<std::string, 2> InvalidLocationNames;
+ if (BR.isInteresting(MainRegion)) {
+ BR.markNotInteresting(MainRegion);
+ InvalidLocationNames.push_back("the environment parameter of 'main'");
+ }
+ bool InterestingGetenvFound = false;
+ for (const MemRegion *MR : GetenvRegions) {
+ if (BR.isInteresting(MR)) {
+ BR.markNotInteresting(MR);
+ if (!InterestingGetenvFound) {
+ InterestingGetenvFound = true;
+ InvalidLocationNames.push_back(
+ "the environment returned by 'getenv'");
+ }
+ }
+ }
+
+ // Emit note tag message.
+ if (InvalidLocationNames.size() >= 1)
+ Out << '\'' << FunctionName << "' call may invalidate "
+ << InvalidLocationNames[0];
+ if (InvalidLocationNames.size() == 2)
+ Out << ", and " << InvalidLocationNames[1];
+ });
+}
+
+void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ // This callevent invalidates all previously generated pointers to the
+ // environment.
+ ProgramStateRef State = C.getState();
+ if (const MemRegion *MainEnvPtr = State->get<MainEnvPtrRegion>())
+ State = State->add<InvalidMemoryRegions>(MainEnvPtr);
+ for (const MemRegion *EnvPtr : State->get<GetenvEnvPtrRegions>())
+ State = State->add<InvalidMemoryRegions>(EnvPtr);
+
+ StringRef FunctionName = Call.getCalleeIdentifier()->getName();
+ const NoteTag *InvalidationNote =
+ createEnvInvalidationNote(C, State, FunctionName);
+
+ C.addTransition(State, InvalidationNote);
+}
+
+void InvalidPtrChecker::postPreviousReturnInvalidatingCall(
+ const CallEvent &Call, CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ const NoteTag *Note = nullptr;
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ // Invalidate the region of the previously returned pointer - if there was
+ // one.
+ if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) {
+ const MemRegion *PrevReg = *Reg;
+ State = State->add<InvalidMemoryRegions>(PrevReg);
+ Note = C.getNoteTag([this, PrevReg, FD](PathSensitiveBugReport &BR,
+ llvm::raw_ostream &Out) {
+ if (!BR.isInteresting(PrevReg) || &BR.getBugType() != &InvalidPtrBugType)
+ return;
+ Out << '\'';
+ FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
+ Out << "' call may invalidate the result of the previous " << '\'';
+ FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
+ Out << '\'';
+ });
+ }
+
+ const LocationContext *LCtx = C.getLocationContext();
+ const auto *CE = cast<CallExpr>(Call.getOriginExpr());
+
+ // Function call will return a pointer to the new symbolic region.
+ DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal(
+ CE, LCtx, CE->getType(), C.blockCount());
+ State = State->BindExpr(CE, LCtx, RetVal);
+
+ // Remember to this region.
+ const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion());
+ const MemRegion *MR = SymRegOfRetVal->getBaseRegion();
+ State = State->set<PreviousCallResultMap>(FD, MR);
+
+ ExplodedNode *Node = C.addTransition(State, Note);
+ const NoteTag *PreviousCallNote = C.getNoteTag(
+ [this, MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
+ if (!BR.isInteresting(MR) || &BR.getBugType() != &InvalidPtrBugType)
+ return;
+ Out << "previous function call was here";
+ });
+
+ C.addTransition(State, Node, PreviousCallNote);
+}
+
+// TODO: This seems really ugly. Simplify this.
+static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State,
+ const MemRegion *Reg) {
+ while (Reg) {
+ if (State->contains<InvalidMemoryRegions>(Reg))
+ return Reg;
+ const auto *SymBase = Reg->getSymbolicBase();
+ if (!SymBase)
+ break;
+ const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol());
+ if (!SRV)
+ break;
+ Reg = SRV->getRegion();
+ if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion()))
+ Reg = VarReg;
+ }
+ return nullptr;
+}
+
+// Handle functions in EnvpInvalidatingFunctions, that invalidate environment
+// pointer from 'main()' Also, check if invalidated region is passed to a
+// function call as an argument.
+void InvalidPtrChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+
+ ProgramStateRef State = C.getState();
+
+ // Model 'getenv' calls
+ if (GetEnvCall.matches(Call)) {
+ const MemRegion *Region = Call.getReturnValue().getAsRegion();
+ if (Region) {
+ State = State->add<GetenvEnvPtrRegions>(Region);
+ C.addTransition(State);
+ }
+ }
+
+ // Check if function invalidates 'envp' argument of 'main'
+ if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call))
+ (this->**Handler)(Call, C);
+
+ // Check if function invalidates the result of previous call
+ if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call))
+ (this->**Handler)(Call, C);
+
+ // If pedantic mode is on, regard 'getenv' calls invalidating as well
+ if (InvalidatingGetEnv && GetEnvCall.matches(Call))
+ postPreviousReturnInvalidatingCall(Call, C);
+
+ // Check if one of the arguments of the function call is invalidated
+
+ // If call was inlined, don't report invalidated argument
+ if (C.wasInlined)
+ return;
+
+ for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) {
+
+ if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(
+ Call.getArgSVal(I).getAsRegion())) {
+ if (const MemRegion *InvalidatedSymbolicBase =
+ findInvalidatedSymbolicBase(State, SR)) {
+ ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
+ if (!ErrorNode)
+ return;
+
+ SmallString<256> Msg;
+ llvm::raw_svector_ostream Out(Msg);
+ Out << "use of invalidated pointer '";
+ Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr,
+ C.getASTContext().getPrintingPolicy());
+ Out << "' in a function call";
+
+ auto Report = std::make_unique<PathSensitiveBugReport>(
+ InvalidPtrBugType, Out.str(), ErrorNode);
+ Report->markInteresting(InvalidatedSymbolicBase);
+ Report->addRange(Call.getArgSourceRange(I));
+ C.emitReport(std::move(Report));
+ }
+ }
+ }
+}
+
+// Obtain the environment pointer from 'main()', if present.
+void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const {
+ if (!C.inTopFrame())
+ return;
+
+ const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl());
+ if (!FD || FD->param_size() != 3 || !FD->isMain())
+ return;
+
+ ProgramStateRef State = C.getState();
+ const MemRegion *EnvpReg =
+ State->getRegion(FD->parameters()[2], C.getLocationContext());
+
+ // Save the memory region pointed by the environment pointer parameter of
+ // 'main'.
+ C.addTransition(State->set<MainEnvPtrRegion>(EnvpReg));
+}
+
+// Check if invalidated region is being dereferenced.
+void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ // Ignore memory operations involving 'non-invalidated' locations.
+ const MemRegion *InvalidatedSymbolicBase =
+ findInvalidatedSymbolicBase(State, Loc.getAsRegion());
+ if (!InvalidatedSymbolicBase)
+ return;
+
+ ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
+ if (!ErrorNode)
+ return;
+
+ auto Report = std::make_unique<PathSensitiveBugReport>(
+ InvalidPtrBugType, "dereferencing an invalid pointer", ErrorNode);
+ Report->markInteresting(InvalidatedSymbolicBase);
+ C.emitReport(std::move(Report));
+}
+
+void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
+ auto *Checker = Mgr.registerChecker<InvalidPtrChecker>();
+ Checker->InvalidatingGetEnv =
+ Mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker,
+ "InvalidatingGetEnv");
+}
+
+bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
+ return true;
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp
index 1c67bbd77ec8..eae162cda693 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
@@ -29,7 +30,7 @@ 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};
+ const CallDescription Putenv{{"putenv"}, 1};
public:
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
@@ -38,7 +39,7 @@ public:
void PutenvWithAutoChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
- if (!Call.isCalled(Putenv))
+ if (!Putenv.matches(Call))
return;
SVal ArgV = Call.getArgSVal(0);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/APSIntType.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/APSIntType.cpp
index a1de10c89ed9..1185cdaa044a 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/APSIntType.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/APSIntType.cpp
@@ -23,7 +23,7 @@ APSIntType::testInRange(const llvm::APSInt &Value,
unsigned MinBits;
if (AllowSignConversions) {
if (Value.isSigned() && !IsUnsigned)
- MinBits = Value.getMinSignedBits();
+ MinBits = Value.getSignificantBits();
else
MinBits = Value.getActiveBits();
@@ -33,7 +33,7 @@ APSIntType::testInRange(const llvm::APSInt &Value,
// Unsigned integers can be converted to unsigned integers of the same width
// or signed integers with one more bit.
if (Value.isSigned())
- MinBits = Value.getMinSignedBits() - IsUnsigned;
+ MinBits = Value.getSignificantBits() - IsUnsigned;
else
MinBits = Value.getActiveBits() + !IsUnsigned;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp
index ecfc7106560e..f9750db7b501 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp
@@ -50,17 +50,14 @@ AnalysisManager::AnalysisManager(ASTContext &ASTCtx, Preprocessor &PP,
AnalysisManager::~AnalysisManager() {
FlushDiagnostics();
- for (PathDiagnosticConsumers::iterator I = PathConsumers.begin(),
- E = PathConsumers.end(); I != E; ++I) {
- delete *I;
+ for (PathDiagnosticConsumer *Consumer : PathConsumers) {
+ delete Consumer;
}
}
void AnalysisManager::FlushDiagnostics() {
PathDiagnosticConsumer::FilesMade filesMade;
- for (PathDiagnosticConsumers::iterator I = PathConsumers.begin(),
- E = PathConsumers.end();
- I != E; ++I) {
- (*I)->FlushDiagnostics(&filesMade);
+ for (PathDiagnosticConsumer *Consumer : PathConsumers) {
+ Consumer->FlushDiagnostics(&filesMade);
}
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
index 8cd7f75e4e38..86ef4a568665 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -23,6 +23,7 @@
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cstddef>
+#include <optional>
#include <utility>
#include <vector>
@@ -64,34 +65,44 @@ void AnalyzerOptions::printFormattedEntry(
ExplorationStrategyKind
AnalyzerOptions::getExplorationStrategy() const {
auto K =
- llvm::StringSwitch<llvm::Optional<ExplorationStrategyKind>>(
- ExplorationStrategy)
+ llvm::StringSwitch<std::optional<ExplorationStrategyKind>>(
+ ExplorationStrategy)
.Case("dfs", ExplorationStrategyKind::DFS)
.Case("bfs", ExplorationStrategyKind::BFS)
- .Case("unexplored_first",
- ExplorationStrategyKind::UnexploredFirst)
+ .Case("unexplored_first", ExplorationStrategyKind::UnexploredFirst)
.Case("unexplored_first_queue",
ExplorationStrategyKind::UnexploredFirstQueue)
.Case("unexplored_first_location_queue",
ExplorationStrategyKind::UnexploredFirstLocationQueue)
.Case("bfs_block_dfs_contents",
ExplorationStrategyKind::BFSBlockDFSContents)
- .Default(None);
- assert(K.hasValue() && "User mode is invalid.");
- return K.getValue();
+ .Default(std::nullopt);
+ assert(K && "User mode is invalid.");
+ return *K;
+}
+
+CTUPhase1InliningKind AnalyzerOptions::getCTUPhase1Inlining() const {
+ auto K = llvm::StringSwitch<std::optional<CTUPhase1InliningKind>>(
+ CTUPhase1InliningMode)
+ .Case("none", CTUPhase1InliningKind::None)
+ .Case("small", CTUPhase1InliningKind::Small)
+ .Case("all", CTUPhase1InliningKind::All)
+ .Default(std::nullopt);
+ assert(K && "CTU inlining mode is invalid.");
+ return *K;
}
IPAKind AnalyzerOptions::getIPAMode() const {
- auto K = llvm::StringSwitch<llvm::Optional<IPAKind>>(IPAMode)
- .Case("none", IPAK_None)
- .Case("basic-inlining", IPAK_BasicInlining)
- .Case("inlining", IPAK_Inlining)
- .Case("dynamic", IPAK_DynamicDispatch)
- .Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate)
- .Default(None);
- assert(K.hasValue() && "IPA Mode is invalid.");
-
- return K.getValue();
+ auto K = llvm::StringSwitch<std::optional<IPAKind>>(IPAMode)
+ .Case("none", IPAK_None)
+ .Case("basic-inlining", IPAK_BasicInlining)
+ .Case("inlining", IPAK_Inlining)
+ .Case("dynamic", IPAK_DynamicDispatch)
+ .Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate)
+ .Default(std::nullopt);
+ assert(K && "IPA Mode is invalid.");
+
+ return *K;
}
bool
@@ -100,16 +111,15 @@ AnalyzerOptions::mayInlineCXXMemberFunction(
if (getIPAMode() < IPAK_Inlining)
return false;
- auto K =
- llvm::StringSwitch<llvm::Optional<CXXInlineableMemberKind>>(
- CXXMemberInliningMode)
- .Case("constructors", CIMK_Constructors)
- .Case("destructors", CIMK_Destructors)
- .Case("methods", CIMK_MemberFunctions)
- .Case("none", CIMK_None)
- .Default(None);
+ auto K = llvm::StringSwitch<std::optional<CXXInlineableMemberKind>>(
+ CXXMemberInliningMode)
+ .Case("constructors", CIMK_Constructors)
+ .Case("destructors", CIMK_Destructors)
+ .Case("methods", CIMK_MemberFunctions)
+ .Case("none", CIMK_None)
+ .Default(std::nullopt);
- assert(K.hasValue() && "Invalid c++ member function inlining mode.");
+ assert(K && "Invalid c++ member function inlining mode.");
return *K >= Param;
}
@@ -151,12 +161,12 @@ StringRef AnalyzerOptions::getCheckerStringOption(const ento::CheckerBase *C,
bool AnalyzerOptions::getCheckerBooleanOption(StringRef CheckerName,
StringRef OptionName,
bool SearchInParents) const {
- auto Ret = llvm::StringSwitch<llvm::Optional<bool>>(
- getCheckerStringOption(CheckerName, OptionName,
- SearchInParents))
- .Case("true", true)
- .Case("false", false)
- .Default(None);
+ auto Ret =
+ llvm::StringSwitch<std::optional<bool>>(
+ getCheckerStringOption(CheckerName, OptionName, SearchInParents))
+ .Case("true", true)
+ .Case("false", false)
+ .Default(std::nullopt);
assert(Ret &&
"This option should be either 'true' or 'false', and should've been "
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
index 40cdaef1bfa7..5c10e757244d 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -97,8 +97,7 @@ const llvm::APSInt& BasicValueFactory::getValue(const llvm::APSInt& X) {
FoldNodeTy* P = APSIntSet.FindNodeOrInsertPos(ID, InsertPos);
if (!P) {
- P = (FoldNodeTy*) BPAlloc.Allocate<FoldNodeTy>();
- new (P) FoldNodeTy(X);
+ P = new (BPAlloc) FoldNodeTy(X);
APSIntSet.InsertNode(P, InsertPos);
}
@@ -132,8 +131,7 @@ BasicValueFactory::getCompoundValData(QualType T,
CompoundValData* D = CompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos);
if (!D) {
- D = (CompoundValData*) BPAlloc.Allocate<CompoundValData>();
- new (D) CompoundValData(T, Vals);
+ D = new (BPAlloc) CompoundValData(T, Vals);
CompoundValDataSet.InsertNode(D, InsertPos);
}
@@ -151,8 +149,7 @@ BasicValueFactory::getLazyCompoundValData(const StoreRef &store,
LazyCompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos);
if (!D) {
- D = (LazyCompoundValData*) BPAlloc.Allocate<LazyCompoundValData>();
- new (D) LazyCompoundValData(store, region);
+ D = new (BPAlloc) LazyCompoundValData(store, region);
LazyCompoundValDataSet.InsertNode(D, InsertPos);
}
@@ -169,8 +166,7 @@ const PointerToMemberData *BasicValueFactory::getPointerToMemberData(
PointerToMemberDataSet.FindNodeOrInsertPos(ID, InsertPos);
if (!D) {
- D = (PointerToMemberData *)BPAlloc.Allocate<PointerToMemberData>();
- new (D) PointerToMemberData(ND, L);
+ D = new (BPAlloc) PointerToMemberData(ND, L);
PointerToMemberDataSet.InsertNode(D, InsertPos);
}
@@ -276,7 +272,7 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op,
// FIXME: This logic should probably go higher up, where we can
// test these conditions symbolically.
- if (V2.isSigned() && V2.isNegative())
+ if (V2.isNegative() || V2.getBitWidth() > 64)
return nullptr;
uint64_t Amt = V2.getZExtValue();
@@ -284,14 +280,6 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op,
if (Amt >= V1.getBitWidth())
return nullptr;
- if (!Ctx.getLangOpts().CPlusPlus20) {
- if (V1.isSigned() && V1.isNegative())
- return nullptr;
-
- if (V1.isSigned() && Amt > V1.countLeadingZeros())
- return nullptr;
- }
-
return &getValue( V1.operator<<( (unsigned) Amt ));
}
@@ -299,7 +287,7 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op,
// FIXME: This logic should probably go higher up, where we can
// test these conditions symbolically.
- if (V2.isSigned() && V2.isNegative())
+ if (V2.isNegative() || V2.getBitWidth() > 64)
return nullptr;
uint64_t Amt = V2.getZExtValue();
@@ -358,8 +346,7 @@ BasicValueFactory::getPersistentSValWithData(const SVal& V, uintptr_t Data) {
FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos);
if (!P) {
- P = (FoldNodeTy*) BPAlloc.Allocate<FoldNodeTy>();
- new (P) FoldNodeTy(std::make_pair(V, Data));
+ P = new (BPAlloc) FoldNodeTy(std::make_pair(V, Data));
Map.InsertNode(P, InsertPos);
}
@@ -383,8 +370,7 @@ BasicValueFactory::getPersistentSValPair(const SVal& V1, const SVal& V2) {
FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos);
if (!P) {
- P = (FoldNodeTy*) BPAlloc.Allocate<FoldNodeTy>();
- new (P) FoldNodeTy(std::make_pair(V1, V2));
+ P = new (BPAlloc) FoldNodeTy(std::make_pair(V1, V2));
Map.InsertNode(P, InsertPos);
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
index d6f69ae03afe..f3e0a5f9f314 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -12,12 +12,15 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ParentMap.h"
+#include "clang/AST/ParentMapContext.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtObjC.h"
@@ -46,8 +49,6 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/None.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
@@ -66,6 +67,7 @@
#include <cstddef>
#include <iterator>
#include <memory>
+#include <optional>
#include <queue>
#include <string>
#include <tuple>
@@ -188,6 +190,9 @@ public:
PathPieces &getMutablePieces() { return PD->getMutablePieces(); }
bool shouldAddPathEdges() const { return Consumer->shouldAddPathEdges(); }
+ bool shouldAddControlNotes() const {
+ return Consumer->shouldAddControlNotes();
+ }
bool shouldGenerateDiagnostics() const {
return Consumer->shouldGenerateDiagnostics();
}
@@ -218,8 +223,8 @@ class PathDiagnosticBuilder : public BugReporterContext {
public:
/// Find a non-invalidated report for a given equivalence class, and returns
/// a PathDiagnosticBuilder able to construct bug reports for different
- /// consumers. Returns None if no valid report is found.
- static Optional<PathDiagnosticBuilder>
+ /// consumers. Returns std::nullopt if no valid report is found.
+ static std::optional<PathDiagnosticBuilder>
findValidReport(ArrayRef<PathSensitiveBugReport *> &bugReports,
PathSensitiveBugReporter &Reporter);
@@ -294,26 +299,24 @@ std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){
return {};
// Check if one of the parameters are set to the interesting symbol.
- unsigned ArgIndex = 0;
- for (CallExpr::const_arg_iterator I = CE->arg_begin(),
- E = CE->arg_end(); I != E; ++I, ++ArgIndex){
- SVal SV = N->getSVal(*I);
+ for (auto [Idx, ArgExpr] : llvm::enumerate(CE->arguments())) {
+ SVal SV = N->getSVal(ArgExpr);
// Check if the variable corresponding to the symbol is passed by value.
SymbolRef AS = SV.getAsLocSymbol();
if (AS == Sym) {
- return getMessageForArg(*I, ArgIndex);
+ return getMessageForArg(ArgExpr, Idx);
}
// Check if the parameter is a pointer to the symbol.
- if (Optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) {
+ if (std::optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) {
// Do not attempt to dereference void*.
- if ((*I)->getType()->isVoidPointerType())
+ if (ArgExpr->getType()->isVoidPointerType())
continue;
SVal PSV = N->getState()->getSVal(Reg->getRegion());
SymbolRef AS = PSV.getAsLocSymbol();
if (AS == Sym) {
- return getMessageForArg(*I, ArgIndex);
+ return getMessageForArg(ArgExpr, Idx);
}
}
}
@@ -534,10 +537,10 @@ static void removeEdgesToDefaultInitializers(PathPieces &Pieces) {
if (auto *CF = dyn_cast<PathDiagnosticControlFlowPiece>(I->get())) {
const Stmt *Start = CF->getStartLocation().asStmt();
const Stmt *End = CF->getEndLocation().asStmt();
- if (Start && isa<CXXDefaultInitExpr>(Start)) {
+ if (isa_and_nonnull<CXXDefaultInitExpr>(Start)) {
I = Pieces.erase(I);
continue;
- } else if (End && isa<CXXDefaultInitExpr>(End)) {
+ } else if (isa_and_nonnull<CXXDefaultInitExpr>(End)) {
PathPieces::iterator Next = std::next(I);
if (Next != E) {
if (auto *NextCF =
@@ -764,7 +767,7 @@ PathDiagnosticPieceRef PathDiagnosticBuilder::generateDiagForSwitchOP(
case Stmt::CaseStmtClass: {
os << "Control jumps to 'case ";
const auto *Case = cast<CaseStmt>(S);
- const Expr *LHS = Case->getLHS()->IgnoreParenCasts();
+ const Expr *LHS = Case->getLHS()->IgnoreParenImpCasts();
// Determine if it is an enum.
bool GetRawInt = true;
@@ -1030,7 +1033,7 @@ static bool isContainedByStmt(const ParentMap &PM, const Stmt *S,
static const Stmt *getStmtBeforeCond(const ParentMap &PM, const Stmt *Term,
const ExplodedNode *N) {
while (N) {
- Optional<StmtPoint> SP = N->getLocation().getAs<StmtPoint>();
+ std::optional<StmtPoint> SP = N->getLocation().getAs<StmtPoint>();
if (SP) {
const Stmt *S = SP->getStmt();
if (!isContainedByStmt(PM, Term, S))
@@ -1191,7 +1194,7 @@ void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
"location context associated with the active path!");
// Have we encountered an exit from a function call?
- if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) {
+ if (std::optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) {
// We are descending into a call (backwards). Construct
// a new call piece to contain the path pieces for that call.
@@ -1232,8 +1235,11 @@ void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
} else if (auto BE = P.getAs<BlockEdge>()) {
- if (!C.shouldAddPathEdges()) {
+ if (C.shouldAddControlNotes()) {
generateMinimalDiagForBlockEdge(C, *BE);
+ }
+
+ if (!C.shouldAddPathEdges()) {
return;
}
@@ -1254,12 +1260,14 @@ void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
// do-while statements are explicitly excluded here
auto p = std::make_shared<PathDiagnosticEventPiece>(
- L, "Looping back to the head "
- "of the loop");
+ L, "Looping back to the head of the loop");
p->setPrunable(true);
addEdgeToPath(C.getActivePath(), PrevLoc, p->getLocation());
- C.getActivePath().push_front(std::move(p));
+ // We might've added a very similar control node already
+ if (!C.shouldAddControlNotes()) {
+ C.getActivePath().push_front(std::move(p));
+ }
if (const auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) {
addEdgeToPath(C.getActivePath(), PrevLoc,
@@ -1300,10 +1308,13 @@ void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str);
PE->setPrunable(true);
addEdgeToPath(C.getActivePath(), PrevLoc, PE->getLocation());
- C.getActivePath().push_front(std::move(PE));
+
+ // We might've added a very similar control node already
+ if (!C.shouldAddControlNotes()) {
+ C.getActivePath().push_front(std::move(PE));
+ }
}
- } else if (isa<BreakStmt>(Term) || isa<ContinueStmt>(Term) ||
- isa<GotoStmt>(Term)) {
+ } else if (isa<BreakStmt, ContinueStmt, GotoStmt>(Term)) {
PathDiagnosticLocation L(Term, SM, C.getCurrLocationContext());
addEdgeToPath(C.getActivePath(), PrevLoc, L);
}
@@ -1342,9 +1353,7 @@ static const Stmt *getStmtParent(const Stmt *S, const ParentMap &PM) {
if (!S)
break;
- if (isa<FullExpr>(S) ||
- isa<CXXBindTemporaryExpr>(S) ||
- isa<SubstNonTypeTemplateParmExpr>(S))
+ if (isa<FullExpr, CXXBindTemporaryExpr, SubstNonTypeTemplateParmExpr>(S))
continue;
break;
@@ -1446,7 +1455,7 @@ static void addContextEdges(PathPieces &pieces, const LocationContext *LC) {
break;
// If the source is in the same context, we're already good.
- if (llvm::find(SrcContexts, DstContext) != SrcContexts.end())
+ if (llvm::is_contained(SrcContexts, DstContext))
break;
// Update the subexpression node to point to the context edge.
@@ -1540,9 +1549,8 @@ static void simplifySimpleBranches(PathPieces &pieces) {
// We only perform this transformation for specific branch kinds.
// We don't want to do this for do..while, for example.
- if (!(isa<ForStmt>(s1Start) || isa<WhileStmt>(s1Start) ||
- isa<IfStmt>(s1Start) || isa<ObjCForCollectionStmt>(s1Start) ||
- isa<CXXForRangeStmt>(s1Start)))
+ if (!isa<ForStmt, WhileStmt, IfStmt, ObjCForCollectionStmt,
+ CXXForRangeStmt>(s1Start))
continue;
// Is s1End the branch condition?
@@ -1558,21 +1566,22 @@ static void simplifySimpleBranches(PathPieces &pieces) {
/// Returns the number of bytes in the given (character-based) SourceRange.
///
-/// If the locations in the range are not on the same line, returns None.
+/// If the locations in the range are not on the same line, returns
+/// std::nullopt.
///
/// Note that this does not do a precise user-visible character or column count.
-static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM,
- SourceRange Range) {
+static std::optional<size_t> getLengthOnSingleLine(const SourceManager &SM,
+ SourceRange Range) {
SourceRange ExpansionRange(SM.getExpansionLoc(Range.getBegin()),
SM.getExpansionRange(Range.getEnd()).getEnd());
FileID FID = SM.getFileID(ExpansionRange.getBegin());
if (FID != SM.getFileID(ExpansionRange.getEnd()))
- return None;
+ return std::nullopt;
- Optional<MemoryBufferRef> Buffer = SM.getBufferOrNone(FID);
+ std::optional<MemoryBufferRef> Buffer = SM.getBufferOrNone(FID);
if (!Buffer)
- return None;
+ return std::nullopt;
unsigned BeginOffset = SM.getFileOffset(ExpansionRange.getBegin());
unsigned EndOffset = SM.getFileOffset(ExpansionRange.getEnd());
@@ -1583,15 +1592,15 @@ static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM,
// SourceRange is covering a large or small amount of space in the user's
// editor.
if (Snippet.find_first_of("\r\n") != StringRef::npos)
- return None;
+ return std::nullopt;
// This isn't Unicode-aware, but it doesn't need to be.
return Snippet.size();
}
/// \sa getLengthOnSingleLine(SourceManager, SourceRange)
-static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM,
- const Stmt *S) {
+static std::optional<size_t> getLengthOnSingleLine(const SourceManager &SM,
+ const Stmt *S) {
return getLengthOnSingleLine(SM, S->getSourceRange());
}
@@ -1650,9 +1659,9 @@ static void removeContextCycles(PathPieces &Path, const SourceManager &SM) {
if (s1Start && s2Start && s1Start == s2End && s2Start == s1End) {
const size_t MAX_SHORT_LINE_LENGTH = 80;
- Optional<size_t> s1Length = getLengthOnSingleLine(SM, s1Start);
+ std::optional<size_t> s1Length = getLengthOnSingleLine(SM, s1Start);
if (s1Length && *s1Length <= MAX_SHORT_LINE_LENGTH) {
- Optional<size_t> s2Length = getLengthOnSingleLine(SM, s2Start);
+ std::optional<size_t> s2Length = getLengthOnSingleLine(SM, s2Start);
if (s2Length && *s2Length <= MAX_SHORT_LINE_LENGTH) {
Path.erase(I);
I = Path.erase(NextI);
@@ -1711,7 +1720,7 @@ static void removePunyEdges(PathPieces &path, const SourceManager &SM,
std::swap(SecondLoc, FirstLoc);
SourceRange EdgeRange(FirstLoc, SecondLoc);
- Optional<size_t> ByteWidth = getLengthOnSingleLine(SM, EdgeRange);
+ std::optional<size_t> ByteWidth = getLengthOnSingleLine(SM, EdgeRange);
// If the statements are on different lines, continue.
if (!ByteWidth)
@@ -1875,7 +1884,7 @@ static bool optimizeEdges(const PathDiagnosticConstruct &C, PathPieces &path,
lexicalContains(PM, s1Start, s1End)) {
SourceRange EdgeRange(PieceI->getEndLocation().asLocation(),
PieceI->getStartLocation().asLocation());
- if (!getLengthOnSingleLine(SM, EdgeRange).hasValue())
+ if (!getLengthOnSingleLine(SM, EdgeRange))
removeEdge = true;
}
}
@@ -2093,8 +2102,6 @@ PathDiagnosticBuilder::generate(const PathDiagnosticConsumer *PDC) const {
void BugType::anchor() {}
-void BuiltinBug::anchor() {}
-
//===----------------------------------------------------------------------===//
// Methods for BugReport and subclasses.
//===----------------------------------------------------------------------===//
@@ -2135,15 +2142,14 @@ PathSensitiveBugReport::PathSensitiveBugReport(
"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!");
+ assert((bt.getCheckerName().starts_with("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(
@@ -2302,7 +2308,7 @@ void PathSensitiveBugReport::markInteresting(const LocationContext *LC) {
InterestingLocationContexts.insert(LC);
}
-Optional<bugreporter::TrackingKind>
+std::optional<bugreporter::TrackingKind>
PathSensitiveBugReport::getInterestingnessKind(SVal V) const {
auto RKind = getInterestingnessKind(V.getAsRegion());
auto SKind = getInterestingnessKind(V.getAsSymbol());
@@ -2324,25 +2330,25 @@ PathSensitiveBugReport::getInterestingnessKind(SVal V) const {
"BugReport::getInterestingnessKind currently can only handle 2 different "
"tracking kinds! Please define what tracking kind should we return here "
"when the kind of getAsRegion() and getAsSymbol() is different!");
- return None;
+ return std::nullopt;
}
-Optional<bugreporter::TrackingKind>
+std::optional<bugreporter::TrackingKind>
PathSensitiveBugReport::getInterestingnessKind(SymbolRef sym) const {
if (!sym)
- return None;
+ return std::nullopt;
// We don't currently consider metadata symbols to be interesting
// even if we know their region is interesting. Is that correct behavior?
auto It = InterestingSymbols.find(sym);
if (It == InterestingSymbols.end())
- return None;
+ return std::nullopt;
return It->getSecond();
}
-Optional<bugreporter::TrackingKind>
+std::optional<bugreporter::TrackingKind>
PathSensitiveBugReport::getInterestingnessKind(const MemRegion *R) const {
if (!R)
- return None;
+ return std::nullopt;
R = R->getBaseRegion();
auto It = InterestingRegions.find(R);
@@ -2351,19 +2357,19 @@ PathSensitiveBugReport::getInterestingnessKind(const MemRegion *R) const {
if (const auto *SR = dyn_cast<SymbolicRegion>(R))
return getInterestingnessKind(SR->getSymbol());
- return None;
+ return std::nullopt;
}
bool PathSensitiveBugReport::isInteresting(SVal V) const {
- return getInterestingnessKind(V).hasValue();
+ return getInterestingnessKind(V).has_value();
}
bool PathSensitiveBugReport::isInteresting(SymbolRef sym) const {
- return getInterestingnessKind(sym).hasValue();
+ return getInterestingnessKind(sym).has_value();
}
bool PathSensitiveBugReport::isInteresting(const MemRegion *R) const {
- return getInterestingnessKind(R).hasValue();
+ return getInterestingnessKind(R).has_value();
}
bool PathSensitiveBugReport::isInteresting(const LocationContext *LC) const {
@@ -2379,7 +2385,7 @@ const Stmt *PathSensitiveBugReport::getStmt() const {
ProgramPoint ProgP = ErrorNode->getLocation();
const Stmt *S = nullptr;
- if (Optional<BlockEntrance> BE = ProgP.getAs<BlockEntrance>()) {
+ if (std::optional<BlockEntrance> BE = ProgP.getAs<BlockEntrance>()) {
CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit();
if (BE->getBlock() == &Exit)
S = ErrorNode->getPreviousStmtForDiagnostics();
@@ -2411,7 +2417,7 @@ PathSensitiveBugReport::getLocation() const {
if (!S) {
// If this is an implicit call, return the implicit call point location.
- if (Optional<PreImplicitCall> PIE = P.getAs<PreImplicitCall>())
+ if (std::optional<PreImplicitCall> PIE = P.getAs<PreImplicitCall>())
return PathDiagnosticLocation(PIE->getLocation(), SM);
if (auto FE = P.getAs<FunctionExitPoint>()) {
if (const ReturnStmt *RS = FE->getStmt())
@@ -2421,6 +2427,12 @@ PathSensitiveBugReport::getLocation() const {
}
if (S) {
+ // Attributed statements usually have corrupted begin locations,
+ // it's OK to ignore attributes for our purposes and deal with
+ // the actual annotated statement.
+ if (const auto *AS = dyn_cast<AttributedStmt>(S))
+ S = AS->getSubStmt();
+
// For member expressions, return the location of the '.' or '->'.
if (const auto *ME = dyn_cast<MemberExpr>(S))
return PathDiagnosticLocation::createMemberLoc(ME, SM);
@@ -2619,8 +2631,7 @@ BugPathInfo *BugPathGetter::getNextBugPath() {
const ExplodedNode *OrigN;
std::tie(CurrentBugPath.Report, OrigN) = ReportNodes.pop_back_val();
- assert(PriorityMap.find(OrigN) != PriorityMap.end() &&
- "error node not accessible from root");
+ assert(PriorityMap.contains(OrigN) && "error node not accessible from root");
// Create a new graph with a single path. This is the graph that will be
// returned to the caller.
@@ -2813,7 +2824,7 @@ generateVisitorsDiagnostics(PathSensitiveBugReport *R,
return Notes;
}
-Optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport(
+std::optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport(
ArrayRef<PathSensitiveBugReport *> &bugReports,
PathSensitiveBugReporter &Reporter) {
@@ -2872,7 +2883,7 @@ PathSensitiveBugReporter::generatePathDiagnostics(
auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
- Optional<PathDiagnosticBuilder> PDB =
+ std::optional<PathDiagnosticBuilder> PDB =
PathDiagnosticBuilder::findValidReport(bugReports, *this);
if (PDB) {
@@ -2894,6 +2905,10 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) {
if (!ValidSourceLoc)
return;
+ // If the user asked to suppress this report, we should skip it.
+ if (UserSuppressions.isSuppressed(*R))
+ return;
+
// Compute the bug report's hash to determine its equivalence class.
llvm::FoldingSetNodeID ID;
R->Profile(ID);
@@ -3061,8 +3076,7 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
// See whether we need to silence the checker/package.
for (const std::string &CheckerOrPackage :
getAnalyzerOptions().SilencedCheckersAndPackages) {
- if (report->getBugType().getCheckerName().startswith(
- CheckerOrPackage))
+ if (report->getBugType().getCheckerName().starts_with(CheckerOrPackage))
return;
}
@@ -3089,9 +3103,8 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
if (getAnalyzerOptions().ShouldDisplayNotesAsEvents) {
// For path diagnostic consumers that don't support extra notes,
// we may optionally convert those to path notes.
- for (auto I = report->getNotes().rbegin(),
- E = report->getNotes().rend(); I != E; ++I) {
- PathDiagnosticNotePiece *Piece = I->get();
+ for (const auto &I : llvm::reverse(report->getNotes())) {
+ PathDiagnosticNotePiece *Piece = I.get();
auto ConvertedPiece = std::make_shared<PathDiagnosticEventPiece>(
Piece->getLocation(), Piece->getString());
for (const auto &R: Piece->getRanges())
@@ -3100,9 +3113,8 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
Pieces.push_front(std::move(ConvertedPiece));
}
} else {
- for (auto I = report->getNotes().rbegin(),
- E = report->getNotes().rend(); I != E; ++I)
- Pieces.push_front(*I);
+ for (const auto &I : llvm::reverse(report->getNotes()))
+ Pieces.push_front(I);
}
for (const auto &I : report->getFixits())
@@ -3181,7 +3193,7 @@ findExecutedLines(const SourceManager &SM, const ExplodedNode *N) {
P = N->getParentMap().getParent(RS);
}
- if (P && (isa<SwitchCase>(P) || isa<LabelStmt>(P)))
+ if (isa_and_nonnull<SwitchCase, LabelStmt>(P))
populateExecutedLinesWithStmt(P, SM, *ExecutedLines);
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index d06a2d493303..2f9965036b9e 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -46,8 +46,6 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/None.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
@@ -60,6 +58,7 @@
#include <cassert>
#include <deque>
#include <memory>
+#include <optional>
#include <string>
#include <utility>
@@ -82,6 +81,10 @@ static const Expr *peelOffPointerArithmetic(const BinaryOperator *B) {
return nullptr;
}
+/// \return A subexpression of @c Ex which represents the
+/// expression-of-interest.
+static const Expr *peelOffOuterExpr(const Expr *Ex, const ExplodedNode *N);
+
/// Given that expression S represents a pointer that would be dereferenced,
/// try to find a sub-expression from which the pointer came from.
/// This is used for tracking down origins of a null or undefined value:
@@ -129,6 +132,16 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) {
}
// Pattern match for a few useful cases: a[0], p->f, *p etc.
else if (const auto *ME = dyn_cast<MemberExpr>(E)) {
+ // This handles the case when the dereferencing of a member reference
+ // happens. This is needed, because the AST for dereferencing a
+ // member reference looks like the following:
+ // |-MemberExpr
+ // `-DeclRefExpr
+ // Without this special case the notes would refer to the whole object
+ // (struct, class or union variable) instead of just the relevant member.
+
+ if (ME->getMemberDecl()->getType()->isReferenceType())
+ break;
E = ME->getBase();
} else if (const auto *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
E = IvarRef->getBase();
@@ -154,26 +167,42 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) {
return E;
}
+static const VarDecl *getVarDeclForExpression(const Expr *E) {
+ if (const auto *DR = dyn_cast<DeclRefExpr>(E))
+ return dyn_cast<VarDecl>(DR->getDecl());
+ return nullptr;
+}
+
static const MemRegion *
getLocationRegionIfReference(const Expr *E, const ExplodedNode *N,
bool LookingForReference = true) {
- if (const auto *DR = dyn_cast<DeclRefExpr>(E)) {
- if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) {
- if (LookingForReference && !VD->getType()->isReferenceType())
- return nullptr;
- return N->getState()
- ->getLValue(VD, N->getLocationContext())
- .getAsRegion();
+ if (const auto *ME = dyn_cast<MemberExpr>(E)) {
+ // This handles null references from FieldRegions, for example:
+ // struct Wrapper { int &ref; };
+ // Wrapper w = { *(int *)0 };
+ // w.ref = 1;
+ const Expr *Base = ME->getBase();
+ const VarDecl *VD = getVarDeclForExpression(Base);
+ if (!VD)
+ return nullptr;
+
+ const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
+ if (!FD)
+ return nullptr;
+
+ if (FD->getType()->isReferenceType()) {
+ SVal StructSVal = N->getState()->getLValue(VD, N->getLocationContext());
+ return N->getState()->getLValue(FD, StructSVal).getAsRegion();
}
+ return nullptr;
}
- // FIXME: This does not handle other kinds of null references,
- // for example, references from FieldRegions:
- // struct Wrapper { int &ref; };
- // Wrapper w = { *(int *)0 };
- // w.ref = 1;
-
- return nullptr;
+ const VarDecl *VD = getVarDeclForExpression(E);
+ if (!VD)
+ return nullptr;
+ if (LookingForReference && !VD->getType()->isReferenceType())
+ return nullptr;
+ return N->getState()->getLValue(VD, N->getLocationContext()).getAsRegion();
}
/// Comparing internal representations of symbolic values (via
@@ -202,8 +231,8 @@ static bool hasVisibleUpdate(const ExplodedNode *LeftNode, SVal LeftVal,
RLCV->getStore() == RightNode->getState()->getStore();
}
-static Optional<SVal> getSValForVar(const Expr *CondVarExpr,
- const ExplodedNode *N) {
+static std::optional<SVal> getSValForVar(const Expr *CondVarExpr,
+ const ExplodedNode *N) {
ProgramStateRef State = N->getState();
const LocationContext *LCtx = N->getLocationContext();
@@ -223,16 +252,16 @@ static Optional<SVal> getSValForVar(const Expr *CondVarExpr,
if (auto FieldL = State->getSVal(ME, LCtx).getAs<Loc>())
return State->getRawSVal(*FieldL, FD->getType());
- return None;
+ return std::nullopt;
}
-static Optional<const llvm::APSInt *>
+static std::optional<const llvm::APSInt *>
getConcreteIntegerValue(const Expr *CondVarExpr, const ExplodedNode *N) {
- if (Optional<SVal> V = getSValForVar(CondVarExpr, N))
+ if (std::optional<SVal> V = getSValForVar(CondVarExpr, N))
if (auto CI = V->getAs<nonloc::ConcreteInt>())
return &CI->getValue();
- return None;
+ return std::nullopt;
}
static bool isVarAnInterestingCondition(const Expr *CondVarExpr,
@@ -244,8 +273,9 @@ static bool isVarAnInterestingCondition(const Expr *CondVarExpr,
if (!B->getErrorNode()->getStackFrame()->isParentOf(N->getStackFrame()))
return false;
- if (Optional<SVal> V = getSValForVar(CondVarExpr, N))
- if (Optional<bugreporter::TrackingKind> K = B->getInterestingnessKind(*V))
+ if (std::optional<SVal> V = getSValForVar(CondVarExpr, N))
+ if (std::optional<bugreporter::TrackingKind> K =
+ B->getInterestingnessKind(*V))
return *K == bugreporter::TrackingKind::Condition;
return false;
@@ -253,8 +283,8 @@ static bool isVarAnInterestingCondition(const Expr *CondVarExpr,
static bool isInterestingExpr(const Expr *E, const ExplodedNode *N,
const PathSensitiveBugReport *B) {
- if (Optional<SVal> V = getSValForVar(E, N))
- return B->getInterestingnessKind(*V).hasValue();
+ if (std::optional<SVal> V = getSValForVar(E, N))
+ return B->getInterestingnessKind(*V).has_value();
return false;
}
@@ -344,45 +374,178 @@ BugReporterVisitor::getDefaultEndPath(const BugReporterContext &BRC,
}
//===----------------------------------------------------------------------===//
+// Implementation of NoStateChangeFuncVisitor.
+//===----------------------------------------------------------------------===//
+
+bool NoStateChangeFuncVisitor::isModifiedInFrame(const ExplodedNode *N) {
+ const LocationContext *Ctx = N->getLocationContext();
+ const StackFrameContext *SCtx = Ctx->getStackFrame();
+ if (!FramesModifyingCalculated.count(SCtx))
+ findModifyingFrames(N);
+ return FramesModifying.count(SCtx);
+}
+
+void NoStateChangeFuncVisitor::markFrameAsModifying(
+ const StackFrameContext *SCtx) {
+ while (!SCtx->inTopFrame()) {
+ auto p = FramesModifying.insert(SCtx);
+ if (!p.second)
+ break; // Frame and all its parents already inserted.
+
+ SCtx = SCtx->getParent()->getStackFrame();
+ }
+}
+
+static const ExplodedNode *getMatchingCallExitEnd(const ExplodedNode *N) {
+ assert(N->getLocationAs<CallEnter>());
+ // The stackframe of the callee is only found in the nodes succeeding
+ // the CallEnter node. CallEnter's stack frame refers to the caller.
+ const StackFrameContext *OrigSCtx = N->getFirstSucc()->getStackFrame();
+
+ // Similarly, the nodes preceding CallExitEnd refer to the callee's stack
+ // frame.
+ auto IsMatchingCallExitEnd = [OrigSCtx](const ExplodedNode *N) {
+ return N->getLocationAs<CallExitEnd>() &&
+ OrigSCtx == N->getFirstPred()->getStackFrame();
+ };
+ while (N && !IsMatchingCallExitEnd(N)) {
+ assert(N->succ_size() <= 1 &&
+ "This function is to be used on the trimmed ExplodedGraph!");
+ N = N->getFirstSucc();
+ }
+ return N;
+}
+
+void NoStateChangeFuncVisitor::findModifyingFrames(
+ const ExplodedNode *const CallExitBeginN) {
+
+ assert(CallExitBeginN->getLocationAs<CallExitBegin>());
+
+ const StackFrameContext *const OriginalSCtx =
+ CallExitBeginN->getLocationContext()->getStackFrame();
+
+ const ExplodedNode *CurrCallExitBeginN = CallExitBeginN;
+ const StackFrameContext *CurrentSCtx = OriginalSCtx;
+
+ for (const ExplodedNode *CurrN = CallExitBeginN; CurrN;
+ CurrN = CurrN->getFirstPred()) {
+ // Found a new inlined call.
+ if (CurrN->getLocationAs<CallExitBegin>()) {
+ CurrCallExitBeginN = CurrN;
+ CurrentSCtx = CurrN->getStackFrame();
+ FramesModifyingCalculated.insert(CurrentSCtx);
+ // We won't see a change in between two identical exploded nodes: skip.
+ continue;
+ }
+
+ if (auto CE = CurrN->getLocationAs<CallEnter>()) {
+ if (const ExplodedNode *CallExitEndN = getMatchingCallExitEnd(CurrN))
+ if (wasModifiedInFunction(CurrN, CallExitEndN))
+ markFrameAsModifying(CurrentSCtx);
+
+ // We exited this inlined call, lets actualize the stack frame.
+ CurrentSCtx = CurrN->getStackFrame();
+
+ // Stop calculating at the current function, but always regard it as
+ // modifying, so we can avoid notes like this:
+ // void f(Foo &F) {
+ // F.field = 0; // note: 0 assigned to 'F.field'
+ // // note: returning without writing to 'F.field'
+ // }
+ if (CE->getCalleeContext() == OriginalSCtx) {
+ markFrameAsModifying(CurrentSCtx);
+ break;
+ }
+ }
+
+ if (wasModifiedBeforeCallExit(CurrN, CurrCallExitBeginN))
+ markFrameAsModifying(CurrentSCtx);
+ }
+}
+
+PathDiagnosticPieceRef NoStateChangeFuncVisitor::VisitNode(
+ const ExplodedNode *N, BugReporterContext &BR, PathSensitiveBugReport &R) {
+
+ const LocationContext *Ctx = N->getLocationContext();
+ const StackFrameContext *SCtx = Ctx->getStackFrame();
+ ProgramStateRef State = N->getState();
+ auto CallExitLoc = N->getLocationAs<CallExitBegin>();
+
+ // No diagnostic if region was modified inside the frame.
+ if (!CallExitLoc || isModifiedInFrame(N))
+ return nullptr;
+
+ CallEventRef<> Call =
+ BR.getStateManager().getCallEventManager().getCaller(SCtx, State);
+
+ // Optimistically suppress uninitialized value bugs that result
+ // from system headers having a chance to initialize the value
+ // but failing to do so. It's too unlikely a system header's fault.
+ // It's much more likely a situation in which the function has a failure
+ // mode that the user decided not to check. If we want to hunt such
+ // omitted checks, we should provide an explicit function-specific note
+ // describing the precondition under which the function isn't supposed to
+ // initialize its out-parameter, and additionally check that such
+ // precondition can actually be fulfilled on the current path.
+ if (Call->isInSystemHeader()) {
+ // We make an exception for system header functions that have no branches.
+ // Such functions unconditionally fail to initialize the variable.
+ // If they call other functions that have more paths within them,
+ // this suppression would still apply when we visit these inner functions.
+ // One common example of a standard function that doesn't ever initialize
+ // its out parameter is operator placement new; it's up to the follow-up
+ // constructor (if any) to initialize the memory.
+ if (!N->getStackFrame()->getCFG()->isLinear()) {
+ static int i = 0;
+ R.markInvalid(&i, nullptr);
+ }
+ return nullptr;
+ }
+
+ if (const auto *MC = dyn_cast<ObjCMethodCall>(Call)) {
+ // If we failed to construct a piece for self, we still want to check
+ // whether the entity of interest is in a parameter.
+ if (PathDiagnosticPieceRef Piece = maybeEmitNoteForObjCSelf(R, *MC, N))
+ return Piece;
+ }
+
+ if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) {
+ // Do not generate diagnostics for not modified parameters in
+ // constructors.
+ return maybeEmitNoteForCXXThis(R, *CCall, N);
+ }
+
+ return maybeEmitNoteForParameters(R, *Call, N);
+}
+
+//===----------------------------------------------------------------------===//
// Implementation of NoStoreFuncVisitor.
//===----------------------------------------------------------------------===//
namespace {
-
/// Put a diagnostic on return statement of all inlined functions
/// for which the region of interest \p RegionOfInterest was passed into,
/// but not written inside, and it has caused an undefined read or a null
/// pointer dereference outside.
-class NoStoreFuncVisitor final : public BugReporterVisitor {
+class NoStoreFuncVisitor final : public NoStateChangeFuncVisitor {
const SubRegion *RegionOfInterest;
MemRegionManager &MmrMgr;
const SourceManager &SM;
const PrintingPolicy &PP;
- bugreporter::TrackingKind TKind;
/// Recursion limit for dereferencing fields when looking for the
/// region of interest.
/// The limit of two indicates that we will dereference fields only once.
static const unsigned DEREFERENCE_LIMIT = 2;
- /// Frames writing into \c RegionOfInterest.
- /// This visitor generates a note only if a function does not write into
- /// a region of interest. This information is not immediately available
- /// by looking at the node associated with the exit from the function
- /// (usually the return statement). To avoid recomputing the same information
- /// many times (going up the path for each node and checking whether the
- /// region was written into) we instead lazily compute the
- /// stack frames along the path which write into the region of interest.
- llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingRegion;
- llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated;
-
using RegionVector = SmallVector<const MemRegion *, 5>;
public:
NoStoreFuncVisitor(const SubRegion *R, bugreporter::TrackingKind TKind)
- : RegionOfInterest(R), MmrMgr(R->getMemRegionManager()),
+ : NoStateChangeFuncVisitor(TKind), RegionOfInterest(R),
+ MmrMgr(R->getMemRegionManager()),
SM(MmrMgr.getContext().getSourceManager()),
- PP(MmrMgr.getContext().getPrintingPolicy()), TKind(TKind) {}
+ PP(MmrMgr.getContext().getPrintingPolicy()) {}
void Profile(llvm::FoldingSetNodeID &ID) const override {
static int Tag = 0;
@@ -390,41 +553,36 @@ public:
ID.AddPointer(RegionOfInterest);
}
- void *getTag() const {
- static int Tag = 0;
- return static_cast<void *>(&Tag);
- }
-
- PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
- BugReporterContext &BR,
- PathSensitiveBugReport &R) override;
-
private:
+ /// \return Whether \c RegionOfInterest was modified at \p CurrN compared to
+ /// the value it holds in \p CallExitBeginN.
+ bool wasModifiedBeforeCallExit(const ExplodedNode *CurrN,
+ const ExplodedNode *CallExitBeginN) override;
+
/// Attempts to find the region of interest in a given record decl,
/// by either following the base classes or fields.
/// Dereferences fields up to a given recursion limit.
/// Note that \p Vec is passed by value, leading to quadratic copying cost,
/// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT.
- /// \return A chain fields leading to the region of interest or None.
- const Optional<RegionVector>
+ /// \return A chain fields leading to the region of interest or std::nullopt.
+ const std::optional<RegionVector>
findRegionOfInterestInRecord(const RecordDecl *RD, ProgramStateRef State,
const MemRegion *R, const RegionVector &Vec = {},
int depth = 0);
- /// Check and lazily calculate whether the region of interest is
- /// modified in the stack frame to which \p N belongs.
- /// The calculation is cached in FramesModifyingRegion.
- bool isRegionOfInterestModifiedInFrame(const ExplodedNode *N) {
- const LocationContext *Ctx = N->getLocationContext();
- const StackFrameContext *SCtx = Ctx->getStackFrame();
- if (!FramesModifyingCalculated.count(SCtx))
- findModifyingFrames(N);
- return FramesModifyingRegion.count(SCtx);
- }
+ // Region of interest corresponds to an IVar, exiting a method
+ // which could have written into that IVar, but did not.
+ PathDiagnosticPieceRef maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
+ const ObjCMethodCall &Call,
+ const ExplodedNode *N) final;
+
+ PathDiagnosticPieceRef maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
+ const CXXConstructorCall &Call,
+ const ExplodedNode *N) final;
- /// Write to \c FramesModifyingRegion all stack frames along
- /// the path in the current stack frame which modify \c RegionOfInterest.
- void findModifyingFrames(const ExplodedNode *N);
+ PathDiagnosticPieceRef
+ maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call,
+ const ExplodedNode *N) final;
/// Consume the information on the no-store stack frame in order to
/// either emit a note or suppress the report enirely.
@@ -436,22 +594,18 @@ private:
const MemRegion *MatchedRegion, StringRef FirstElement,
bool FirstIsReferenceType, unsigned IndirectionLevel);
- /// Pretty-print region \p MatchedRegion to \p os.
- /// \return Whether printing succeeded.
- bool prettyPrintRegionName(StringRef FirstElement, bool FirstIsReferenceType,
+ bool prettyPrintRegionName(const RegionVector &FieldChain,
const MemRegion *MatchedRegion,
- const RegionVector &FieldChain,
- int IndirectionLevel,
+ StringRef FirstElement, bool FirstIsReferenceType,
+ unsigned IndirectionLevel,
llvm::raw_svector_ostream &os);
- /// Print first item in the chain, return new separator.
- static StringRef prettyPrintFirstElement(StringRef FirstElement,
- bool MoreItemsExpected,
- int IndirectionLevel,
- llvm::raw_svector_ostream &os);
+ StringRef prettyPrintFirstElement(StringRef FirstElement,
+ bool MoreItemsExpected,
+ int IndirectionLevel,
+ llvm::raw_svector_ostream &os);
};
-
-} // end of anonymous namespace
+} // namespace
/// \return Whether the method declaration \p Parent
/// syntactically has a binary operation writing into the ivar \p Ivar.
@@ -478,7 +632,7 @@ static bool potentiallyWritesIntoIvar(const Decl *Parent,
if (const auto *DRE = dyn_cast<DeclRefExpr>(Base))
if (const auto *ID = dyn_cast<ImplicitParamDecl>(DRE->getDecl()))
- if (ID->getParameterKind() == ImplicitParamDecl::ObjCSelf)
+ if (ID->getParameterKind() == ImplicitParamKind::ObjCSelf)
return true;
return false;
@@ -486,50 +640,31 @@ static bool potentiallyWritesIntoIvar(const Decl *Parent,
return false;
}
-/// Get parameters associated with runtime definition in order
-/// to get the correct parameter name.
-static ArrayRef<ParmVarDecl *> getCallParameters(CallEventRef<> Call) {
- // Use runtime definition, if available.
- RuntimeDefinition RD = Call->getRuntimeDefinition();
- if (const auto *FD = dyn_cast_or_null<FunctionDecl>(RD.getDecl()))
- return FD->parameters();
- if (const auto *MD = dyn_cast_or_null<ObjCMethodDecl>(RD.getDecl()))
- return MD->parameters();
-
- return Call->parameters();
-}
-
-/// \return whether \p Ty points to a const type, or is a const reference.
-static bool isPointerToConst(QualType Ty) {
- return !Ty->getPointeeType().isNull() &&
- Ty->getPointeeType().getCanonicalType().isConstQualified();
-}
-
/// Attempts to find the region of interest in a given CXX decl,
/// by either following the base classes or fields.
/// Dereferences fields up to a given recursion limit.
/// Note that \p Vec is passed by value, leading to quadratic copying cost,
/// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT.
-/// \return A chain fields leading to the region of interest or None.
-const Optional<NoStoreFuncVisitor::RegionVector>
+/// \return A chain fields leading to the region of interest or std::nullopt.
+const std::optional<NoStoreFuncVisitor::RegionVector>
NoStoreFuncVisitor::findRegionOfInterestInRecord(
const RecordDecl *RD, ProgramStateRef State, const MemRegion *R,
const NoStoreFuncVisitor::RegionVector &Vec /* = {} */,
int depth /* = 0 */) {
if (depth == DEREFERENCE_LIMIT) // Limit the recursion depth.
- return None;
+ return std::nullopt;
if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD))
if (!RDX->hasDefinition())
- return None;
+ return std::nullopt;
// Recursively examine the base classes.
// Note that following base classes does not increase the recursion depth.
if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD))
for (const auto &II : RDX->bases())
if (const RecordDecl *RRD = II.getType()->getAsRecordDecl())
- if (Optional<RegionVector> Out =
+ if (std::optional<RegionVector> Out =
findRegionOfInterestInRecord(RRD, State, R, Vec, depth))
return Out;
@@ -555,77 +690,75 @@ NoStoreFuncVisitor::findRegionOfInterestInRecord(
continue;
if (const RecordDecl *RRD = PT->getAsRecordDecl())
- if (Optional<RegionVector> Out =
+ if (std::optional<RegionVector> Out =
findRegionOfInterestInRecord(RRD, State, VR, VecF, depth + 1))
return Out;
}
- return None;
+ return std::nullopt;
}
PathDiagnosticPieceRef
-NoStoreFuncVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BR,
- PathSensitiveBugReport &R) {
-
- const LocationContext *Ctx = N->getLocationContext();
- const StackFrameContext *SCtx = Ctx->getStackFrame();
- ProgramStateRef State = N->getState();
- auto CallExitLoc = N->getLocationAs<CallExitBegin>();
-
- // No diagnostic if region was modified inside the frame.
- if (!CallExitLoc || isRegionOfInterestModifiedInFrame(N))
- return nullptr;
-
- CallEventRef<> Call =
- BR.getStateManager().getCallEventManager().getCaller(SCtx, State);
-
- // Region of interest corresponds to an IVar, exiting a method
- // which could have written into that IVar, but did not.
- if (const auto *MC = dyn_cast<ObjCMethodCall>(Call)) {
- if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest)) {
- const MemRegion *SelfRegion = MC->getReceiverSVal().getAsRegion();
- if (RegionOfInterest->isSubRegionOf(SelfRegion) &&
- potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(),
- IvarR->getDecl()))
- return maybeEmitNote(R, *Call, N, {}, SelfRegion, "self",
- /*FirstIsReferenceType=*/false, 1);
- }
+NoStoreFuncVisitor::maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
+ const ObjCMethodCall &Call,
+ const ExplodedNode *N) {
+ if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest)) {
+ const MemRegion *SelfRegion = Call.getReceiverSVal().getAsRegion();
+ if (RegionOfInterest->isSubRegionOf(SelfRegion) &&
+ potentiallyWritesIntoIvar(Call.getRuntimeDefinition().getDecl(),
+ IvarR->getDecl()))
+ return maybeEmitNote(R, Call, N, {}, SelfRegion, "self",
+ /*FirstIsReferenceType=*/false, 1);
}
+ return nullptr;
+}
- if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) {
- const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion();
- if (RegionOfInterest->isSubRegionOf(ThisR) &&
- !CCall->getDecl()->isImplicit())
- return maybeEmitNote(R, *Call, N, {}, ThisR, "this",
- /*FirstIsReferenceType=*/false, 1);
+PathDiagnosticPieceRef
+NoStoreFuncVisitor::maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
+ const CXXConstructorCall &Call,
+ const ExplodedNode *N) {
+ const MemRegion *ThisR = Call.getCXXThisVal().getAsRegion();
+ if (RegionOfInterest->isSubRegionOf(ThisR) && !Call.getDecl()->isImplicit())
+ return maybeEmitNote(R, Call, N, {}, ThisR, "this",
+ /*FirstIsReferenceType=*/false, 1);
+
+ // Do not generate diagnostics for not modified parameters in
+ // constructors.
+ return nullptr;
+}
- // Do not generate diagnostics for not modified parameters in
- // constructors.
- return nullptr;
- }
+/// \return whether \p Ty points to a const type, or is a const reference.
+static bool isPointerToConst(QualType Ty) {
+ return !Ty->getPointeeType().isNull() &&
+ Ty->getPointeeType().getCanonicalType().isConstQualified();
+}
- ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call);
- for (unsigned I = 0; I < Call->getNumArgs() && I < parameters.size(); ++I) {
- const ParmVarDecl *PVD = parameters[I];
- SVal V = Call->getArgSVal(I);
+PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNoteForParameters(
+ PathSensitiveBugReport &R, const CallEvent &Call, const ExplodedNode *N) {
+ ArrayRef<ParmVarDecl *> Parameters = Call.parameters();
+ for (unsigned I = 0; I < Call.getNumArgs() && I < Parameters.size(); ++I) {
+ const ParmVarDecl *PVD = Parameters[I];
+ SVal V = Call.getArgSVal(I);
bool ParamIsReferenceType = PVD->getType()->isReferenceType();
std::string ParamName = PVD->getNameAsString();
- int IndirectionLevel = 1;
+ unsigned IndirectionLevel = 1;
QualType T = PVD->getType();
while (const MemRegion *MR = V.getAsRegion()) {
if (RegionOfInterest->isSubRegionOf(MR) && !isPointerToConst(T))
- return maybeEmitNote(R, *Call, N, {}, MR, ParamName,
+ return maybeEmitNote(R, Call, N, {}, MR, ParamName,
ParamIsReferenceType, IndirectionLevel);
QualType PT = T->getPointeeType();
if (PT.isNull() || PT->isVoidType())
break;
+ ProgramStateRef State = N->getState();
+
if (const RecordDecl *RD = PT->getAsRecordDecl())
- if (Optional<RegionVector> P =
+ if (std::optional<RegionVector> P =
findRegionOfInterestInRecord(RD, State, MR))
- return maybeEmitNote(R, *Call, N, *P, RegionOfInterest, ParamName,
+ return maybeEmitNote(R, Call, N, *P, RegionOfInterest, ParamName,
ParamIsReferenceType, IndirectionLevel);
V = State->getSVal(MR, PT);
@@ -637,40 +770,11 @@ NoStoreFuncVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BR,
return nullptr;
}
-void NoStoreFuncVisitor::findModifyingFrames(const ExplodedNode *N) {
- assert(N->getLocationAs<CallExitBegin>());
- ProgramStateRef LastReturnState = N->getState();
- SVal ValueAtReturn = LastReturnState->getSVal(RegionOfInterest);
- const LocationContext *Ctx = N->getLocationContext();
- const StackFrameContext *OriginalSCtx = Ctx->getStackFrame();
-
- do {
- ProgramStateRef State = N->getState();
- auto CallExitLoc = N->getLocationAs<CallExitBegin>();
- if (CallExitLoc) {
- LastReturnState = State;
- ValueAtReturn = LastReturnState->getSVal(RegionOfInterest);
- }
-
- FramesModifyingCalculated.insert(N->getLocationContext()->getStackFrame());
-
- if (wasRegionOfInterestModifiedAt(RegionOfInterest, N, ValueAtReturn)) {
- const StackFrameContext *SCtx = N->getStackFrame();
- while (!SCtx->inTopFrame()) {
- auto p = FramesModifyingRegion.insert(SCtx);
- if (!p.second)
- break; // Frame and all its parents already inserted.
- SCtx = SCtx->getParent()->getStackFrame();
- }
- }
-
- // Stop calculation at the call to the current function.
- if (auto CE = N->getLocationAs<CallEnter>())
- if (CE->getCalleeContext() == OriginalSCtx)
- break;
-
- N = N->getFirstPred();
- } while (N);
+bool NoStoreFuncVisitor::wasModifiedBeforeCallExit(
+ const ExplodedNode *CurrN, const ExplodedNode *CallExitBeginN) {
+ return ::wasRegionOfInterestModifiedAt(
+ RegionOfInterest, CurrN,
+ CallExitBeginN->getState()->getSVal(RegionOfInterest));
}
static llvm::StringLiteral WillBeUsedForACondition =
@@ -681,27 +785,6 @@ PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNote(
const RegionVector &FieldChain, const MemRegion *MatchedRegion,
StringRef FirstElement, bool FirstIsReferenceType,
unsigned IndirectionLevel) {
- // Optimistically suppress uninitialized value bugs that result
- // from system headers having a chance to initialize the value
- // but failing to do so. It's too unlikely a system header's fault.
- // It's much more likely a situation in which the function has a failure
- // mode that the user decided not to check. If we want to hunt such
- // omitted checks, we should provide an explicit function-specific note
- // describing the precondition under which the function isn't supposed to
- // initialize its out-parameter, and additionally check that such
- // precondition can actually be fulfilled on the current path.
- if (Call.isInSystemHeader()) {
- // We make an exception for system header functions that have no branches.
- // Such functions unconditionally fail to initialize the variable.
- // If they call other functions that have more paths within them,
- // this suppression would still apply when we visit these inner functions.
- // One common example of a standard function that doesn't ever initialize
- // its out parameter is operator placement new; it's up to the follow-up
- // constructor (if any) to initialize the memory.
- if (!N->getStackFrame()->getCFG()->isLinear())
- R.markInvalid(getTag(), nullptr);
- return nullptr;
- }
PathDiagnosticLocation L =
PathDiagnosticLocation::create(N->getLocation(), SM);
@@ -717,8 +800,8 @@ PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNote(
os << "Returning without writing to '";
// Do not generate the note if failed to pretty-print.
- if (!prettyPrintRegionName(FirstElement, FirstIsReferenceType, MatchedRegion,
- FieldChain, IndirectionLevel, os))
+ if (!prettyPrintRegionName(FieldChain, MatchedRegion, FirstElement,
+ FirstIsReferenceType, IndirectionLevel, os))
return nullptr;
os << "'";
@@ -727,11 +810,11 @@ PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNote(
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
}
-bool NoStoreFuncVisitor::prettyPrintRegionName(StringRef FirstElement,
- bool FirstIsReferenceType,
+bool NoStoreFuncVisitor::prettyPrintRegionName(const RegionVector &FieldChain,
const MemRegion *MatchedRegion,
- const RegionVector &FieldChain,
- int IndirectionLevel,
+ StringRef FirstElement,
+ bool FirstIsReferenceType,
+ unsigned IndirectionLevel,
llvm::raw_svector_ostream &os) {
if (FirstIsReferenceType)
@@ -754,7 +837,7 @@ bool NoStoreFuncVisitor::prettyPrintRegionName(StringRef FirstElement,
// Just keep going up to the base region.
// Element regions may appear due to casts.
- if (isa<CXXBaseObjectRegion>(R) || isa<CXXTempObjectRegion>(R))
+ if (isa<CXXBaseObjectRegion, CXXTempObjectRegion>(R))
continue;
if (Sep.empty())
@@ -854,7 +937,7 @@ public:
const SVal V) {
AnalyzerOptions &Options = N->getState()->getAnalysisManager().options;
if (EnableNullFPSuppression && Options.ShouldSuppressNullReturnPaths &&
- V.getAs<Loc>())
+ isa<Loc>(V))
BR.addVisitor<MacroNullReturnSuppressionVisitor>(R->getAs<SubRegion>(),
V);
}
@@ -871,12 +954,12 @@ public:
private:
/// \return Source location of right hand side of an assignment
/// into \c RegionOfInterest, empty optional if none found.
- Optional<SourceLocation> matchAssignment(const ExplodedNode *N) {
+ std::optional<SourceLocation> matchAssignment(const ExplodedNode *N) {
const Stmt *S = N->getStmtForDiagnostics();
ProgramStateRef State = N->getState();
auto *LCtx = N->getLocationContext();
if (!S)
- return None;
+ return std::nullopt;
if (const auto *DS = dyn_cast<DeclStmt>(S)) {
if (const auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl()))
@@ -891,7 +974,7 @@ private:
return RHS->getBeginLoc();
}
}
- return None;
+ return std::nullopt;
}
};
@@ -944,7 +1027,7 @@ public:
if (N->getLocationContext() != CalleeSFC)
return nullptr;
- Optional<StmtPoint> SP = N->getLocationAs<StmtPoint>();
+ std::optional<StmtPoint> SP = N->getLocationAs<StmtPoint>();
if (!SP)
return nullptr;
@@ -966,18 +1049,17 @@ public:
assert(RetE && "Tracking a return value for a void function");
// Handle cases where a reference is returned and then immediately used.
- Optional<Loc> LValue;
+ std::optional<Loc> LValue;
if (RetE->isGLValue()) {
if ((LValue = V.getAs<Loc>())) {
SVal RValue = State->getRawSVal(*LValue, RetE->getType());
- if (RValue.getAs<DefinedSVal>())
+ if (isa<DefinedSVal>(RValue))
V = RValue;
}
}
// Ignore aggregate rvalues.
- if (V.getAs<nonloc::LazyCompoundVal>() ||
- V.getAs<nonloc::CompoundVal>())
+ if (isa<nonloc::LazyCompoundVal, nonloc::CompoundVal>(V))
return nullptr;
RetE = RetE->IgnoreParenCasts();
@@ -992,7 +1074,7 @@ public:
bool WouldEventBeMeaningless = false;
if (State->isNull(V).isConstrainedTrue()) {
- if (V.getAs<Loc>()) {
+ if (isa<Loc>(V)) {
// If we have counter-suppression enabled, make sure we keep visiting
// future nodes. We want to emit a path note as well, in case
@@ -1022,10 +1104,7 @@ public:
if (N->getCFG().size() == 3)
WouldEventBeMeaningless = true;
- if (V.getAs<Loc>())
- Out << "Returning pointer";
- else
- Out << "Returning value";
+ Out << (isa<Loc>(V) ? "Returning pointer" : "Returning value");
}
}
@@ -1069,7 +1148,7 @@ public:
assert(Options.ShouldAvoidSuppressingNullArgumentPaths);
// Are we at the entry node for this call?
- Optional<CallEnter> CE = N->getLocationAs<CallEnter>();
+ std::optional<CallEnter> CE = N->getLocationAs<CallEnter>();
if (!CE)
return nullptr;
@@ -1087,7 +1166,7 @@ public:
ProgramStateRef State = N->getState();
CallEventRef<> Call = CallMgr.getCaller(CalleeSFC, State);
for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) {
- Optional<Loc> ArgV = Call->getArgSVal(I).getAs<Loc>();
+ std::optional<Loc> ArgV = Call->getArgSVal(I).getAs<Loc>();
if (!ArgV)
continue;
@@ -1134,8 +1213,6 @@ public:
}
};
-} // end of anonymous namespace
-
//===----------------------------------------------------------------------===//
// StoreSiteFinder
//===----------------------------------------------------------------------===//
@@ -1153,7 +1230,7 @@ class StoreSiteFinder final : public TrackingBugReporterVisitor {
public:
/// \param V We're searching for the store where \c R received this value.
/// \param R The region we're tracking.
- /// \param TKind May limit the amount of notes added to the bug report.
+ /// \param Options Tracking behavior options.
/// \param OriginSFC Only adds notes when the last store happened in a
/// different stackframe to this one. Disregarded if the tracking kind
/// is thorough.
@@ -1175,6 +1252,7 @@ public:
BugReporterContext &BRC,
PathSensitiveBugReport &BR) override;
};
+} // namespace
void StoreSiteFinder::Profile(llvm::FoldingSetNodeID &ID) const {
static int tag = 0;
@@ -1188,7 +1266,7 @@ void StoreSiteFinder::Profile(llvm::FoldingSetNodeID &ID) const {
/// Returns true if \p N represents the DeclStmt declaring and initializing
/// \p VR.
static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) {
- Optional<PostStmt> P = N->getLocationAs<PostStmt>();
+ std::optional<PostStmt> P = N->getLocationAs<PostStmt>();
if (!P)
return false;
@@ -1248,7 +1326,7 @@ static void showBRDiagnostics(llvm::raw_svector_ostream &OS, StoreInfo SI) {
llvm_unreachable("Unexpected store kind");
}
- if (SI.Value.getAs<loc::ConcreteInt>()) {
+ if (isa<loc::ConcreteInt>(SI.Value)) {
OS << Action << (isObjCPointer(SI.Dest) ? "nil" : "a null pointer value");
} else if (auto CVal = SI.Value.getAs<nonloc::ConcreteInt>()) {
@@ -1287,13 +1365,12 @@ static void showBRDiagnostics(llvm::raw_svector_ostream &OS, StoreInfo SI) {
static void showBRParamDiagnostics(llvm::raw_svector_ostream &OS,
StoreInfo SI) {
const auto *VR = cast<VarRegion>(SI.Dest);
- const auto *Param = cast<ParmVarDecl>(VR->getDecl());
+ const auto *D = VR->getDecl();
OS << "Passing ";
- if (SI.Value.getAs<loc::ConcreteInt>()) {
- OS << (isObjCPointer(Param) ? "nil object reference"
- : "null pointer value");
+ if (isa<loc::ConcreteInt>(SI.Value)) {
+ OS << (isObjCPointer(D) ? "nil object reference" : "null pointer value");
} else if (SI.Value.isUndef()) {
OS << "uninitialized value";
@@ -1308,12 +1385,18 @@ static void showBRParamDiagnostics(llvm::raw_svector_ostream &OS,
OS << "value";
}
- // Printed parameter indexes are 1-based, not 0-based.
- unsigned Idx = Param->getFunctionScopeIndex() + 1;
- OS << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter";
- if (VR->canPrintPretty()) {
- OS << " ";
- VR->printPretty(OS);
+ if (const auto *Param = dyn_cast<ParmVarDecl>(VR->getDecl())) {
+ // Printed parameter indexes are 1-based, not 0-based.
+ unsigned Idx = Param->getFunctionScopeIndex() + 1;
+ OS << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter";
+ if (VR->canPrintPretty()) {
+ OS << " ";
+ VR->printPretty(OS);
+ }
+ } else if (const auto *ImplParam = dyn_cast<ImplicitParamDecl>(D)) {
+ if (ImplParam->getParameterKind() == ImplicitParamKind::ObjCSelf) {
+ OS << " via implicit parameter 'self'";
+ }
}
}
@@ -1322,7 +1405,7 @@ static void showBRDefaultDiagnostics(llvm::raw_svector_ostream &OS,
StoreInfo SI) {
const bool HasSuffix = SI.Dest->canPrintPretty();
- if (SI.Value.getAs<loc::ConcreteInt>()) {
+ if (isa<loc::ConcreteInt>(SI.Value)) {
OS << (isObjCPointer(SI.Dest) ? "nil object reference stored"
: (HasSuffix ? "Null pointer value stored"
: "Storing null pointer value"));
@@ -1357,6 +1440,83 @@ static void showBRDefaultDiagnostics(llvm::raw_svector_ostream &OS,
}
}
+static bool isTrivialCopyOrMoveCtor(const CXXConstructExpr *CE) {
+ if (!CE)
+ return false;
+
+ const auto *CtorDecl = CE->getConstructor();
+
+ return CtorDecl->isCopyOrMoveConstructor() && CtorDecl->isTrivial();
+}
+
+static const Expr *tryExtractInitializerFromList(const InitListExpr *ILE,
+ const MemRegion *R) {
+
+ const auto *TVR = dyn_cast_or_null<TypedValueRegion>(R);
+
+ if (!TVR)
+ return nullptr;
+
+ const auto ITy = ILE->getType().getCanonicalType();
+
+ // Push each sub-region onto the stack.
+ std::stack<const TypedValueRegion *> TVRStack;
+ while (isa<FieldRegion>(TVR) || isa<ElementRegion>(TVR)) {
+ // We found a region that matches the type of the init list,
+ // so we assume this is the outer-most region. This can happen
+ // if the initializer list is inside a class. If our assumption
+ // is wrong, we return a nullptr in the end.
+ if (ITy == TVR->getValueType().getCanonicalType())
+ break;
+
+ TVRStack.push(TVR);
+ TVR = cast<TypedValueRegion>(TVR->getSuperRegion());
+ }
+
+ // If the type of the outer most region doesn't match the type
+ // of the ILE, we can't match the ILE and the region.
+ if (ITy != TVR->getValueType().getCanonicalType())
+ return nullptr;
+
+ const Expr *Init = ILE;
+ while (!TVRStack.empty()) {
+ TVR = TVRStack.top();
+ TVRStack.pop();
+
+ // We hit something that's not an init list before
+ // running out of regions, so we most likely failed.
+ if (!isa<InitListExpr>(Init))
+ return nullptr;
+
+ ILE = cast<InitListExpr>(Init);
+ auto NumInits = ILE->getNumInits();
+
+ if (const auto *FR = dyn_cast<FieldRegion>(TVR)) {
+ const auto *FD = FR->getDecl();
+
+ if (FD->getFieldIndex() >= NumInits)
+ return nullptr;
+
+ Init = ILE->getInit(FD->getFieldIndex());
+ } else if (const auto *ER = dyn_cast<ElementRegion>(TVR)) {
+ const auto Ind = ER->getIndex();
+
+ // If index is symbolic, we can't figure out which expression
+ // belongs to the region.
+ if (!Ind.isConstant())
+ return nullptr;
+
+ const auto IndVal = Ind.getAsInteger()->getLimitedValue();
+ if (IndVal >= NumInits)
+ return nullptr;
+
+ Init = ILE->getInit(IndVal);
+ }
+ }
+
+ return Init;
+}
+
PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) {
@@ -1378,7 +1538,8 @@ PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ,
// If this is a post initializer expression, initializing the region, we
// should track the initializer expression.
- if (Optional<PostInitializer> PIP = Pred->getLocationAs<PostInitializer>()) {
+ if (std::optional<PostInitializer> PIP =
+ Pred->getLocationAs<PostInitializer>()) {
const MemRegion *FieldReg = (const MemRegion *)PIP->getLocationValue();
if (FieldReg == R) {
StoreSite = Pred;
@@ -1396,25 +1557,101 @@ PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ,
return nullptr;
if (hasVisibleUpdate(Pred, Pred->getState()->getSVal(R), Succ, V)) {
- Optional<PostStore> PS = Succ->getLocationAs<PostStore>();
+ std::optional<PostStore> PS = Succ->getLocationAs<PostStore>();
if (!PS || PS->getLocationValue() != R)
return nullptr;
}
StoreSite = Succ;
- // If this is an assignment expression, we can track the value
- // being assigned.
- if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>())
- if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>())
+ if (std::optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) {
+ // If this is an assignment expression, we can track the value
+ // being assigned.
+ if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) {
if (BO->isAssignmentOp())
InitE = BO->getRHS();
+ }
+ // If we have a declaration like 'S s{1,2}' that needs special
+ // handling, we handle it here.
+ else if (const auto *DS = P->getStmtAs<DeclStmt>()) {
+ const auto *Decl = DS->getSingleDecl();
+ if (isa<VarDecl>(Decl)) {
+ const auto *VD = cast<VarDecl>(Decl);
+
+ // FIXME: Here we only track the inner most region, so we lose
+ // information, but it's still better than a crash or no information
+ // at all.
+ //
+ // E.g.: The region we have is 's.s2.s3.s4.y' and we only track 'y',
+ // and throw away the rest.
+ if (const auto *ILE = dyn_cast<InitListExpr>(VD->getInit()))
+ InitE = tryExtractInitializerFromList(ILE, R);
+ }
+ } else if (const auto *CE = P->getStmtAs<CXXConstructExpr>()) {
+
+ const auto State = Succ->getState();
+
+ if (isTrivialCopyOrMoveCtor(CE) && isa<SubRegion>(R)) {
+ // Migrate the field regions from the current object to
+ // the parent object. If we track 'a.y.e' and encounter
+ // 'S a = b' then we need to track 'b.y.e'.
+
+ // Push the regions to a stack, from last to first, so
+ // considering the example above the stack will look like
+ // (bottom) 'e' -> 'y' (top).
+
+ std::stack<const SubRegion *> SRStack;
+ const SubRegion *SR = cast<SubRegion>(R);
+ while (isa<FieldRegion>(SR) || isa<ElementRegion>(SR)) {
+ SRStack.push(SR);
+ SR = cast<SubRegion>(SR->getSuperRegion());
+ }
+
+ // Get the region for the object we copied/moved from.
+ const auto *OriginEx = CE->getArg(0);
+ const auto OriginVal =
+ State->getSVal(OriginEx, Succ->getLocationContext());
+
+ // Pop the stored field regions and apply them to the origin
+ // object in the same order we had them on the copy.
+ // OriginField will evolve like 'b' -> 'b.y' -> 'b.y.e'.
+ SVal OriginField = OriginVal;
+ while (!SRStack.empty()) {
+ const auto *TopR = SRStack.top();
+ SRStack.pop();
+
+ if (const auto *FR = dyn_cast<FieldRegion>(TopR)) {
+ OriginField = State->getLValue(FR->getDecl(), OriginField);
+ } else if (const auto *ER = dyn_cast<ElementRegion>(TopR)) {
+ OriginField = State->getLValue(ER->getElementType(),
+ ER->getIndex(), OriginField);
+ } else {
+ // FIXME: handle other region type
+ }
+ }
+
+ // Track 'b.y.e'.
+ getParentTracker().track(V, OriginField.getAsRegion(), Options);
+ InitE = OriginEx;
+ }
+ }
+ // This branch can occur in cases like `Ctor() : field{ x, y } {}'.
+ else if (const auto *ILE = P->getStmtAs<InitListExpr>()) {
+ // FIXME: Here we only track the top level region, so we lose
+ // information, but it's still better than a crash or no information
+ // at all.
+ //
+ // E.g.: The region we have is 's.s2.s3.s4.y' and we only track 'y', and
+ // throw away the rest.
+ InitE = tryExtractInitializerFromList(ILE, R);
+ }
+ }
// If this is a call entry, the variable should be a parameter.
// FIXME: Handle CXXThisRegion as well. (This is not a priority because
// 'this' should never be NULL, but this visitor isn't just for NULL and
// UndefinedVal.)
- if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) {
+ if (std::optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) {
if (const auto *VR = dyn_cast<VarRegion>(R)) {
if (const auto *Param = dyn_cast<ParmVarDecl>(VR->getDecl())) {
@@ -1537,7 +1774,7 @@ PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ,
R,
OldRegion};
- if (Optional<PostStmt> PS = StoreSite->getLocationAs<PostStmt>()) {
+ if (std::optional<PostStmt> PS = StoreSite->getLocationAs<PostStmt>()) {
const Stmt *S = PS->getStmt();
const auto *DS = dyn_cast<DeclStmt>(S);
const auto *VR = dyn_cast<VarRegion>(R);
@@ -1574,6 +1811,7 @@ PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ,
void TrackConstraintBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
static int tag = 0;
ID.AddPointer(&tag);
+ ID.AddString(Message);
ID.AddBoolean(Assumption);
ID.Add(Constraint);
}
@@ -1584,8 +1822,12 @@ const char *TrackConstraintBRVisitor::getTag() {
return "TrackConstraintBRVisitor";
}
+bool TrackConstraintBRVisitor::isZeroCheck() const {
+ return !Assumption && Constraint.getAs<Loc>();
+}
+
bool TrackConstraintBRVisitor::isUnderconstrained(const ExplodedNode *N) const {
- if (IsZeroCheck)
+ if (isZeroCheck())
return N->getState()->isNull(Constraint).isUnderconstrained();
return (bool)N->getState()->assume(Constraint, !Assumption);
}
@@ -1609,32 +1851,27 @@ PathDiagnosticPieceRef TrackConstraintBRVisitor::VisitNode(
if (isUnderconstrained(PrevN)) {
IsSatisfied = true;
- // As a sanity check, make sure that the negation of the constraint
- // was infeasible in the current state. If it is feasible, we somehow
- // missed the transition point.
+ // At this point, the negation of the constraint should be infeasible. If it
+ // is feasible, make sure that the negation of the constrainti was
+ // infeasible in the current state. If it is feasible, we somehow missed
+ // the transition point.
assert(!isUnderconstrained(N));
- // We found the transition point for the constraint. We now need to
- // pretty-print the constraint. (work-in-progress)
- SmallString<64> sbuf;
- llvm::raw_svector_ostream os(sbuf);
-
- if (Constraint.getAs<Loc>()) {
- os << "Assuming pointer value is ";
- os << (Assumption ? "non-null" : "null");
- }
+ // Construct a new PathDiagnosticPiece.
+ ProgramPoint P = N->getLocation();
- if (os.str().empty())
+ // If this node already have a specialized note, it's probably better
+ // than our generic note.
+ // FIXME: This only looks for note tags, not for other ways to add a note.
+ if (isa_and_nonnull<NoteTag>(P.getTag()))
return nullptr;
- // Construct a new PathDiagnosticPiece.
- ProgramPoint P = N->getLocation();
PathDiagnosticLocation L =
PathDiagnosticLocation::create(P, BRC.getSourceManager());
if (!L.isValid())
return nullptr;
- auto X = std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+ auto X = std::make_shared<PathDiagnosticEventPiece>(L, Message);
X->setTag(getTag());
return std::move(X);
}
@@ -1857,11 +2094,33 @@ TrackControlDependencyCondBRVisitor::VisitNode(const ExplodedNode *N,
return nullptr;
if (const Expr *Condition = NB->getLastCondition()) {
+
+ // If we can't retrieve a sensible condition, just bail out.
+ const Expr *InnerExpr = peelOffOuterExpr(Condition, N);
+ if (!InnerExpr)
+ return nullptr;
+
+ // If the condition was a function call, we likely won't gain much from
+ // tracking it either. Evidence suggests that it will mostly trigger in
+ // scenarios like this:
+ //
+ // void f(int *x) {
+ // x = nullptr;
+ // if (alwaysTrue()) // We don't need a whole lot of explanation
+ // // here, the function name is good enough.
+ // *x = 5;
+ // }
+ //
+ // Its easy to create a counterexample where this heuristic would make us
+ // lose valuable information, but we've never really seen one in practice.
+ if (isa<CallExpr>(InnerExpr))
+ return nullptr;
+
// Keeping track of the already tracked conditions on a visitor level
// isn't sufficient, because a new visitor is created for each tracked
// expression, hence the BugReport level set.
if (BR.addTrackedCondition(N)) {
- getParentTracker().track(Condition, N,
+ getParentTracker().track(InnerExpr, N,
{bugreporter::TrackingKind::Condition,
/*EnableNullFPSuppression=*/false});
return constructDebugPieceForTrackedCondition(Condition, N, BRC);
@@ -1876,10 +2135,8 @@ TrackControlDependencyCondBRVisitor::VisitNode(const ExplodedNode *N,
// Implementation of trackExpressionValue.
//===----------------------------------------------------------------------===//
-/// \return A subexpression of @c Ex which represents the
-/// expression-of-interest.
-static const Expr *peelOffOuterExpr(const Expr *Ex,
- const ExplodedNode *N) {
+static const Expr *peelOffOuterExpr(const Expr *Ex, const ExplodedNode *N) {
+
Ex = Ex->IgnoreParenCasts();
if (const auto *FE = dyn_cast<FullExpr>(Ex))
return peelOffOuterExpr(FE->getSubExpr(), N);
@@ -1902,7 +2159,7 @@ static const Expr *peelOffOuterExpr(const Expr *Ex,
const ExplodedNode *NI = N;
do {
ProgramPoint ProgPoint = NI->getLocation();
- if (Optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) {
+ if (std::optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) {
const CFGBlock *srcBlk = BE->getSrc();
if (const Stmt *term = srcBlk->getTerminatorStmt()) {
if (term == CO) {
@@ -1977,6 +2234,7 @@ PathDiagnosticPieceRef StoreHandler::constructNote(StoreInfo SI,
return std::make_shared<PathDiagnosticEventPiece>(L, NodeText);
}
+namespace {
class DefaultStoreHandler final : public StoreHandler {
public:
using StoreHandler::StoreHandler;
@@ -2125,8 +2383,9 @@ public:
// null.
if (V.getAsLocSymbol(/*IncludeBaseRegions=*/true))
if (LVState->isNull(V).isConstrainedTrue())
- Report.addVisitor<TrackConstraintBRVisitor>(V.castAs<DefinedSVal>(),
- false);
+ Report.addVisitor<TrackConstraintBRVisitor>(
+ V.castAs<DefinedSVal>(),
+ /*Assumption=*/false, "Assuming pointer value is null");
// Add visitor, which will suppress inline defensive checks.
if (auto DV = V.getAs<DefinedSVal>())
@@ -2174,7 +2433,8 @@ class InlinedFunctionCallHandler final : public ExpressionHandler {
do {
// If that is satisfied we found our statement as an inlined call.
- if (Optional<CallExitEnd> CEE = ExprNode->getLocationAs<CallExitEnd>())
+ if (std::optional<CallExitEnd> CEE =
+ ExprNode->getLocationAs<CallExitEnd>())
if (CEE->getCalleeContext()->getCallSite() == E)
break;
@@ -2189,7 +2449,7 @@ class InlinedFunctionCallHandler final : public ExpressionHandler {
// FIXME: This code currently bypasses the call site for the
// conservatively evaluated allocator.
if (!BypassCXXNewExprEval)
- if (Optional<StmtPoint> SP = ExprNode->getLocationAs<StmtPoint>())
+ if (std::optional<StmtPoint> SP = ExprNode->getLocationAs<StmtPoint>())
// See if we do not enter into another context.
if (SP->getStmt() == E && CurrentSFC == PredSFC)
break;
@@ -2204,7 +2464,7 @@ class InlinedFunctionCallHandler final : public ExpressionHandler {
return {};
// Finally, see if we inlined the call.
- Optional<CallExitEnd> CEE = ExprNode->getLocationAs<CallExitEnd>();
+ std::optional<CallExitEnd> CEE = ExprNode->getLocationAs<CallExitEnd>();
if (!CEE)
return {};
@@ -2218,7 +2478,7 @@ class InlinedFunctionCallHandler final : public ExpressionHandler {
// Handle cases where a reference is returned and then immediately used.
if (cast<Expr>(E)->isGLValue())
- if (Optional<Loc> LValue = RetVal.getAs<Loc>())
+ if (std::optional<Loc> LValue = RetVal.getAs<Loc>())
RetVal = State->getSVal(*LValue);
// See if the return value is NULL. If so, suppress the report.
@@ -2226,7 +2486,7 @@ class InlinedFunctionCallHandler final : public ExpressionHandler {
bool EnableNullFPSuppression = false;
if (Opts.EnableNullFPSuppression && Options.ShouldSuppressNullReturnPaths)
- if (Optional<Loc> RetLoc = RetVal.getAs<Loc>())
+ if (std::optional<Loc> RetLoc = RetVal.getAs<Loc>())
EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue();
PathSensitiveBugReport &Report = getParentTracker().getReport();
@@ -2261,7 +2521,7 @@ public:
// what is written inside the pointer.
bool CanDereference = true;
if (const auto *SR = L->getRegionAs<SymbolicRegion>()) {
- if (SR->getSymbol()->getType()->getPointeeType()->isVoidType())
+ if (SR->getPointeeStaticType()->isVoidType())
CanDereference = false;
} else if (L->getRegionAs<AllocaRegion>())
CanDereference = false;
@@ -2271,7 +2531,7 @@ public:
// well. Try to use the correct type when looking up the value.
SVal RVal;
if (ExplodedGraph::isInterestingLValueExpr(Inner))
- RVal = LVState->getRawSVal(L.getValue(), Inner->getType());
+ RVal = LVState->getRawSVal(*L, Inner->getType());
else if (CanDereference)
RVal = LVState->getSVal(L->getRegion());
@@ -2289,7 +2549,7 @@ public:
Report.markInteresting(RegionRVal, Opts.Kind);
Report.addVisitor<TrackConstraintBRVisitor>(
loc::MemRegionVal(RegionRVal),
- /*assumption=*/false);
+ /*Assumption=*/false, "Assuming pointer value is null");
Result.FoundSomethingToTrack = true;
}
}
@@ -2314,6 +2574,29 @@ public:
if (!RVNode)
return {};
+ Tracker::Result CombinedResult;
+ Tracker &Parent = getParentTracker();
+
+ const auto track = [&CombinedResult, &Parent, ExprNode,
+ Opts](const Expr *Inner) {
+ CombinedResult.combineWith(Parent.track(Inner, ExprNode, Opts));
+ };
+
+ // FIXME: Initializer lists can appear in many different contexts
+ // and most of them needs a special handling. For now let's handle
+ // what we can. If the initializer list only has 1 element, we track
+ // that.
+ // This snippet even handles nesting, e.g.: int *x{{{{{y}}}}};
+ if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
+ if (ILE->getNumInits() == 1) {
+ track(ILE->getInit(0));
+
+ return CombinedResult;
+ }
+
+ return {};
+ }
+
ProgramStateRef RVState = RVNode->getState();
SVal V = RVState->getSValAsScalarOrLoc(E, RVNode->getLocationContext());
const auto *BO = dyn_cast<BinaryOperator>(E);
@@ -2325,13 +2608,6 @@ public:
SVal LHSV = RVState->getSVal(BO->getLHS(), RVNode->getLocationContext());
// Track both LHS and RHS of a multiplication.
- Tracker::Result CombinedResult;
- Tracker &Parent = getParentTracker();
-
- const auto track = [&CombinedResult, &Parent, ExprNode, Opts](Expr *Inner) {
- CombinedResult.combineWith(Parent.track(Inner, ExprNode, Opts));
- };
-
if (BO->getOpcode() == BO_Mul) {
if (LHSV.isZeroConstant())
track(BO->getLHS());
@@ -2345,6 +2621,7 @@ public:
return CombinedResult;
}
};
+} // namespace
Tracker::Tracker(PathSensitiveBugReport &Report) : Report(Report) {
// Default expression handlers.
@@ -2443,7 +2720,7 @@ const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S,
PathDiagnosticPieceRef
NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
PathSensitiveBugReport &BR) {
- Optional<PreStmt> P = N->getLocationAs<PreStmt>();
+ std::optional<PreStmt> P = N->getLocationAs<PreStmt>();
if (!P)
return nullptr;
@@ -2507,7 +2784,7 @@ ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N,
// If an assumption was made on a branch, it should be caught
// here by looking at the state transition.
- if (Optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) {
+ if (std::optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) {
const CFGBlock *SrcBlock = BE->getSrc();
if (const Stmt *Term = SrcBlock->getTerminatorStmt()) {
// If the tag of the previous node is 'Eagerly Assume...' the current
@@ -2524,7 +2801,7 @@ ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N,
return nullptr;
}
- if (Optional<PostStmt> PS = ProgPoint.getAs<PostStmt>()) {
+ if (std::optional<PostStmt> PS = ProgPoint.getAs<PostStmt>()) {
const ProgramPointTag *CurrentNodeTag = PS->getTag();
if (CurrentNodeTag != Tags.first && CurrentNodeTag != Tags.second)
return nullptr;
@@ -2663,20 +2940,17 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, BugReporterContext &BRC,
Loc, TookTrue ? GenericTrueMessage : GenericFalseMessage);
}
-bool ConditionBRVisitor::patternMatch(const Expr *Ex,
- const Expr *ParentEx,
- raw_ostream &Out,
- BugReporterContext &BRC,
+bool ConditionBRVisitor::patternMatch(const Expr *Ex, const Expr *ParentEx,
+ raw_ostream &Out, BugReporterContext &BRC,
PathSensitiveBugReport &report,
const ExplodedNode *N,
- Optional<bool> &prunable,
+ std::optional<bool> &prunable,
bool IsSameFieldName) {
const Expr *OriginalExpr = Ex;
Ex = Ex->IgnoreParenCasts();
- if (isa<GNUNullExpr>(Ex) || isa<ObjCBoolLiteralExpr>(Ex) ||
- isa<CXXBoolLiteralExpr>(Ex) || isa<IntegerLiteral>(Ex) ||
- isa<FloatingLiteral>(Ex)) {
+ if (isa<GNUNullExpr, ObjCBoolLiteralExpr, CXXBoolLiteralExpr, IntegerLiteral,
+ FloatingLiteral>(Ex)) {
// Use heuristics to determine if the expression is a macro
// expanding to a literal and if so, use the macro's name.
SourceLocation BeginLoc = OriginalExpr->getBeginLoc();
@@ -2743,7 +3017,8 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex,
Out << '\''
<< Lexer::getSourceText(
CharSourceRange::getTokenRange(Ex->getSourceRange()),
- BRC.getSourceManager(), BRC.getASTContext().getLangOpts(), 0)
+ BRC.getSourceManager(), BRC.getASTContext().getLangOpts(),
+ nullptr)
<< '\'';
}
@@ -2755,7 +3030,7 @@ PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(
PathSensitiveBugReport &R, const ExplodedNode *N, bool TookTrue,
bool IsAssuming) {
bool shouldInvert = false;
- Optional<bool> shouldPrune;
+ std::optional<bool> shouldPrune;
// Check if the field name of the MemberExprs is ambiguous. Example:
// " 'a.d' is equal to 'h.d' " in 'test/Analysis/null-deref-path-notes.cpp'.
@@ -2865,8 +3140,8 @@ PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(
PathDiagnosticLocation Loc(Cond, SM, LCtx);
auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Message);
- if (shouldPrune.hasValue())
- event->setPrunable(shouldPrune.getValue());
+ if (shouldPrune)
+ event->setPrunable(*shouldPrune);
return event;
}
@@ -2989,20 +3264,20 @@ bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out,
if (!Ty->isIntegralOrEnumerationType())
return false;
- Optional<const llvm::APSInt *> IntValue;
+ std::optional<const llvm::APSInt *> IntValue;
if (!IsAssuming)
IntValue = getConcreteIntegerValue(CondVarExpr, N);
- if (IsAssuming || !IntValue.hasValue()) {
+ if (IsAssuming || !IntValue) {
if (Ty->isBooleanType())
Out << (TookTrue ? "true" : "false");
else
Out << (TookTrue ? "not equal to 0" : "0");
} else {
if (Ty->isBooleanType())
- Out << (IntValue.getValue()->getBoolValue() ? "true" : "false");
+ Out << ((*IntValue)->getBoolValue() ? "true" : "false");
else
- Out << *IntValue.getValue();
+ Out << **IntValue;
}
return true;
@@ -3097,7 +3372,7 @@ void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor(
FullSourceLoc Loc = BR.getLocation().asLocation();
while (Loc.isMacroID()) {
Loc = Loc.getSpellingLoc();
- if (SM.getFilename(Loc).endswith("sys/queue.h")) {
+ if (SM.getFilename(Loc).ends_with("sys/queue.h")) {
BR.markInvalid(getTag(), nullptr);
return;
}
@@ -3115,7 +3390,7 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
ProgramPoint ProgLoc = N->getLocation();
// We are only interested in visiting CallEnter nodes.
- Optional<CallEnter> CEnter = ProgLoc.getAs<CallEnter>();
+ std::optional<CallEnter> CEnter = ProgLoc.getAs<CallEnter>();
if (!CEnter)
return nullptr;
@@ -3194,11 +3469,11 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor(
}
// And check for satisfiability
- Optional<bool> IsSAT = RefutationSolver->check();
- if (!IsSAT.hasValue())
+ std::optional<bool> IsSAT = RefutationSolver->check();
+ if (!IsSAT)
return;
- if (!IsSAT.getValue())
+ if (!*IsSAT)
BR.markInvalid("Infeasible constraints", EndPathNode->getLocationContext());
}
@@ -3253,7 +3528,7 @@ PathDiagnosticPieceRef TagVisitor::VisitNode(const ExplodedNode *N,
if (!T)
return nullptr;
- if (Optional<std::string> Msg = T->generateMessage(BRC, R)) {
+ if (std::optional<std::string> Msg = T->generateMessage(BRC, R)) {
PathDiagnosticLocation Loc =
PathDiagnosticLocation::create(PP, BRC.getSourceManager());
auto Piece = std::make_shared<PathDiagnosticEventPiece>(Loc, *Msg);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugSuppression.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugSuppression.cpp
new file mode 100644
index 000000000000..b5991e47a538
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugSuppression.cpp
@@ -0,0 +1,169 @@
+//===- BugSuppression.cpp - Suppression interface -------------------------===//
+//
+// 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/BugReporter/BugSuppression.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+using Ranges = llvm::SmallVectorImpl<SourceRange>;
+
+inline bool hasSuppression(const Decl *D) {
+ // FIXME: Implement diagnostic identifier arguments
+ // (checker names, "hashtags").
+ if (const auto *Suppression = D->getAttr<SuppressAttr>())
+ return !Suppression->isGSL() &&
+ (Suppression->diagnosticIdentifiers().empty());
+ return false;
+}
+inline bool hasSuppression(const AttributedStmt *S) {
+ // FIXME: Implement diagnostic identifier arguments
+ // (checker names, "hashtags").
+ return llvm::any_of(S->getAttrs(), [](const Attr *A) {
+ const auto *Suppression = dyn_cast<SuppressAttr>(A);
+ return Suppression && !Suppression->isGSL() &&
+ (Suppression->diagnosticIdentifiers().empty());
+ });
+}
+
+template <class NodeType> inline SourceRange getRange(const NodeType *Node) {
+ return Node->getSourceRange();
+}
+template <> inline SourceRange getRange(const AttributedStmt *S) {
+ // Begin location for attributed statement node seems to be ALWAYS invalid.
+ //
+ // It is unlikely that we ever report any warnings on suppression
+ // attribute itself, but even if we do, we wouldn't want that warning
+ // to be suppressed by that same attribute.
+ //
+ // Long story short, we can use inner statement and it's not going to break
+ // anything.
+ return getRange(S->getSubStmt());
+}
+
+inline bool isLessOrEqual(SourceLocation LHS, SourceLocation RHS,
+ const SourceManager &SM) {
+ // SourceManager::isBeforeInTranslationUnit tests for strict
+ // inequality, when we need a non-strict comparison (bug
+ // can be reported directly on the annotated note).
+ // For this reason, we use the following equivalence:
+ //
+ // A <= B <==> !(B < A)
+ //
+ return !SM.isBeforeInTranslationUnit(RHS, LHS);
+}
+
+inline bool fullyContains(SourceRange Larger, SourceRange Smaller,
+ const SourceManager &SM) {
+ // Essentially this means:
+ //
+ // Larger.fullyContains(Smaller)
+ //
+ // However, that method has a very trivial implementation and couldn't
+ // compare regular locations and locations from macro expansions.
+ // We could've converted everything into regular locations as a solution,
+ // but the following solution seems to be the most bulletproof.
+ return isLessOrEqual(Larger.getBegin(), Smaller.getBegin(), SM) &&
+ isLessOrEqual(Smaller.getEnd(), Larger.getEnd(), SM);
+}
+
+class CacheInitializer : public RecursiveASTVisitor<CacheInitializer> {
+public:
+ static void initialize(const Decl *D, Ranges &ToInit) {
+ CacheInitializer(ToInit).TraverseDecl(const_cast<Decl *>(D));
+ }
+
+ bool VisitVarDecl(VarDecl *VD) {
+ // Bug location could be somewhere in the init value of
+ // a freshly declared variable. Even though it looks like the
+ // user applied attribute to a statement, it will apply to a
+ // variable declaration, and this is where we check for it.
+ return VisitAttributedNode(VD);
+ }
+
+ bool VisitAttributedStmt(AttributedStmt *AS) {
+ // When we apply attributes to statements, it actually creates
+ // a wrapper statement that only contains attributes and the wrapped
+ // statement.
+ return VisitAttributedNode(AS);
+ }
+
+private:
+ template <class NodeType> bool VisitAttributedNode(NodeType *Node) {
+ if (hasSuppression(Node)) {
+ // TODO: In the future, when we come up with good stable IDs for checkers
+ // we can return a list of kinds to ignore, or all if no arguments
+ // were provided.
+ addRange(getRange(Node));
+ }
+ // We should keep traversing AST.
+ return true;
+ }
+
+ void addRange(SourceRange R) {
+ if (R.isValid()) {
+ Result.push_back(R);
+ }
+ }
+
+ CacheInitializer(Ranges &R) : Result(R) {}
+ Ranges &Result;
+};
+
+} // end anonymous namespace
+
+// TODO: Introduce stable IDs for checkers and check for those here
+// to be more specific. Attribute without arguments should still
+// be considered as "suppress all".
+// It is already much finer granularity than what we have now
+// (i.e. removing the whole function from the analysis).
+bool BugSuppression::isSuppressed(const BugReport &R) {
+ PathDiagnosticLocation Location = R.getLocation();
+ PathDiagnosticLocation UniqueingLocation = R.getUniqueingLocation();
+ const Decl *DeclWithIssue = R.getDeclWithIssue();
+
+ return isSuppressed(Location, DeclWithIssue, {}) ||
+ isSuppressed(UniqueingLocation, DeclWithIssue, {});
+}
+
+bool BugSuppression::isSuppressed(const PathDiagnosticLocation &Location,
+ const Decl *DeclWithIssue,
+ DiagnosticIdentifierList Hashtags) {
+ if (!Location.isValid() || DeclWithIssue == nullptr)
+ return false;
+
+ // While some warnings are attached to AST nodes (mostly path-sensitive
+ // checks), others are simply associated with a plain source location
+ // or range. Figuring out the node based on locations can be tricky,
+ // so instead, we traverse the whole body of the declaration and gather
+ // information on ALL suppressions. After that we can simply check if
+ // any of those suppressions affect the warning in question.
+ //
+ // Traversing AST of a function is not a heavy operation, but for
+ // large functions with a lot of bugs it can make a dent in performance.
+ // In order to avoid this scenario, we cache traversal results.
+ auto InsertionResult = CachedSuppressionLocations.insert(
+ std::make_pair(DeclWithIssue, CachedRanges{}));
+ Ranges &SuppressionRanges = InsertionResult.first->second;
+ if (InsertionResult.second) {
+ // We haven't checked this declaration for suppressions yet!
+ CacheInitializer::initialize(DeclWithIssue, SuppressionRanges);
+ }
+
+ SourceRange BugRange = Location.asRange();
+ const SourceManager &SM = Location.getManager();
+
+ return llvm::any_of(SuppressionRanges,
+ [BugRange, &SM](SourceRange Suppression) {
+ return fullyContains(Suppression, BugRange, SM);
+ });
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallDescription.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallDescription.cpp
new file mode 100644
index 000000000000..94b2fde0a6f3
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallDescription.cpp
@@ -0,0 +1,169 @@
+//===- CallDescription.cpp - function/method call matching --*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file This file defines a generic mechanism for matching for function and
+/// method calls of C, C++, and Objective-C languages. Instances of these
+/// classes are frequently used together with the CallEvent classes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include "clang/AST/Decl.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/ArrayRef.h"
+#include <iterator>
+#include <optional>
+
+using namespace llvm;
+using namespace clang;
+
+using MaybeCount = std::optional<unsigned>;
+
+// A constructor helper.
+static MaybeCount readRequiredParams(MaybeCount RequiredArgs,
+ MaybeCount RequiredParams) {
+ if (RequiredParams)
+ return RequiredParams;
+ if (RequiredArgs)
+ return RequiredArgs;
+ return std::nullopt;
+}
+
+ento::CallDescription::CallDescription(CallDescriptionFlags Flags,
+ ArrayRef<StringRef> QualifiedName,
+ MaybeCount RequiredArgs /*= None*/,
+ MaybeCount RequiredParams /*= None*/)
+ : RequiredArgs(RequiredArgs),
+ RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)),
+ Flags(Flags) {
+ assert(!QualifiedName.empty());
+ this->QualifiedName.reserve(QualifiedName.size());
+ llvm::transform(QualifiedName, std::back_inserter(this->QualifiedName),
+ [](StringRef From) { return From.str(); });
+}
+
+/// Construct a CallDescription with default flags.
+ento::CallDescription::CallDescription(ArrayRef<StringRef> QualifiedName,
+ MaybeCount RequiredArgs /*= None*/,
+ MaybeCount RequiredParams /*= None*/)
+ : CallDescription(CDF_None, QualifiedName, RequiredArgs, RequiredParams) {}
+
+bool ento::CallDescription::matches(const CallEvent &Call) const {
+ // FIXME: Add ObjC Message support.
+ if (Call.getKind() == CE_ObjCMessage)
+ return false;
+
+ const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!FD)
+ return false;
+
+ return matchesImpl(FD, Call.getNumArgs(), Call.parameters().size());
+}
+
+bool ento::CallDescription::matchesAsWritten(const CallExpr &CE) const {
+ const auto *FD = dyn_cast_or_null<FunctionDecl>(CE.getCalleeDecl());
+ if (!FD)
+ return false;
+
+ return matchesImpl(FD, CE.getNumArgs(), FD->param_size());
+}
+
+bool ento::CallDescription::matchesImpl(const FunctionDecl *Callee,
+ size_t ArgCount,
+ size_t ParamCount) const {
+ const auto *FD = Callee;
+ if (!FD)
+ return false;
+
+ if (Flags & CDF_MaybeBuiltin) {
+ return CheckerContext::isCLibraryFunction(FD, getFunctionName()) &&
+ (!RequiredArgs || *RequiredArgs <= ArgCount) &&
+ (!RequiredParams || *RequiredParams <= ParamCount);
+ }
+
+ if (!II) {
+ II = &FD->getASTContext().Idents.get(getFunctionName());
+ }
+
+ const auto MatchNameOnly = [](const CallDescription &CD,
+ const NamedDecl *ND) -> bool {
+ DeclarationName Name = ND->getDeclName();
+ if (const auto *II = Name.getAsIdentifierInfo())
+ return II == *CD.II; // Fast case.
+
+ // Fallback to the slow stringification and comparison for:
+ // C++ overloaded operators, constructors, destructors, etc.
+ // FIXME This comparison is way SLOWER than comparing pointers.
+ // At some point in the future, we should compare FunctionDecl pointers.
+ return Name.getAsString() == CD.getFunctionName();
+ };
+
+ const auto ExactMatchArgAndParamCounts =
+ [](size_t ArgCount, size_t ParamCount,
+ const CallDescription &CD) -> bool {
+ const bool ArgsMatch = !CD.RequiredArgs || *CD.RequiredArgs == ArgCount;
+ const bool ParamsMatch =
+ !CD.RequiredParams || *CD.RequiredParams == ParamCount;
+ return ArgsMatch && ParamsMatch;
+ };
+
+ const auto MatchQualifiedNameParts = [](const CallDescription &CD,
+ const Decl *D) -> bool {
+ const auto FindNextNamespaceOrRecord =
+ [](const DeclContext *Ctx) -> const DeclContext * {
+ while (Ctx && !isa<NamespaceDecl, RecordDecl>(Ctx))
+ Ctx = Ctx->getParent();
+ return Ctx;
+ };
+
+ auto QualifierPartsIt = CD.begin_qualified_name_parts();
+ const auto QualifierPartsEndIt = CD.end_qualified_name_parts();
+
+ // Match namespace and record names. Skip unrelated names if they don't
+ // match.
+ const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext());
+ for (; Ctx && QualifierPartsIt != QualifierPartsEndIt;
+ Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) {
+ // If not matched just continue and try matching for the next one.
+ if (cast<NamedDecl>(Ctx)->getName() != *QualifierPartsIt)
+ continue;
+ ++QualifierPartsIt;
+ }
+
+ // We matched if we consumed all expected qualifier segments.
+ return QualifierPartsIt == QualifierPartsEndIt;
+ };
+
+ // Let's start matching...
+ if (!ExactMatchArgAndParamCounts(ArgCount, ParamCount, *this))
+ return false;
+
+ if (!MatchNameOnly(*this, FD))
+ return false;
+
+ if (!hasQualifiedNameParts())
+ return true;
+
+ return MatchQualifiedNameParts(*this, FD);
+}
+
+ento::CallDescriptionSet::CallDescriptionSet(
+ std::initializer_list<CallDescription> &&List) {
+ Impl.LinearMap.reserve(List.size());
+ for (const CallDescription &CD : List)
+ Impl.LinearMap.push_back({CD, /*unused*/ true});
+}
+
+bool ento::CallDescriptionSet::contains(const CallEvent &Call) const {
+ return static_cast<bool>(Impl.lookup(Call));
+}
+
+bool ento::CallDescriptionSet::containsAsWritten(const CallExpr &CE) const {
+ return static_cast<bool>(Impl.lookupAsWritten(CE));
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 3785f498414f..bc14aea27f67 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -36,6 +36,7 @@
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Specifiers.h"
#include "clang/CrossTU/CrossTranslationUnit.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h"
@@ -48,8 +49,6 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/ImmutableList.h"
-#include "llvm/ADT/None.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
@@ -61,6 +60,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
+#include <optional>
#include <utility>
#define DEBUG_TYPE "static-analyzer-call-event"
@@ -73,26 +73,7 @@ QualType CallEvent::getResultType() const {
const Expr *E = getOriginExpr();
if (!E)
return Ctx.VoidTy;
- assert(E);
-
- QualType ResultTy = E->getType();
-
- // A function that returns a reference to 'int' will have a result type
- // of simply 'int'. Check the origin expr's value kind to recover the
- // proper type.
- switch (E->getValueKind()) {
- case VK_LValue:
- ResultTy = Ctx.getLValueReferenceType(ResultTy);
- break;
- case VK_XValue:
- ResultTy = Ctx.getRValueReferenceType(ResultTy);
- break;
- case VK_PRValue:
- // No adjustment is necessary.
- break;
- }
-
- return ResultTy;
+ return Ctx.getReferenceQualifiedType(E);
}
static bool isCallback(QualType T) {
@@ -306,6 +287,7 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit,
const ProgramPointTag *Tag) const {
+
if (const Expr *E = getOriginExpr()) {
if (IsPreVisit)
return PreStmt(E, getLocationContext(), Tag);
@@ -314,69 +296,13 @@ ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit,
const Decl *D = getDecl();
assert(D && "Cannot get a program point without a statement or decl");
+ assert(ElemRef.getParent() &&
+ "Cannot get a program point without a CFGElementRef");
SourceLocation Loc = getSourceRange().getBegin();
if (IsPreVisit)
- return PreImplicitCall(D, Loc, getLocationContext(), Tag);
- return PostImplicitCall(D, Loc, getLocationContext(), Tag);
-}
-
-bool CallEvent::isCalled(const CallDescription &CD) const {
- // FIXME: Add ObjC Message support.
- if (getKind() == CE_ObjCMessage)
- return false;
-
- const IdentifierInfo *II = getCalleeIdentifier();
- if (!II)
- return false;
- const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(getDecl());
- if (!FD)
- return false;
-
- if (CD.Flags & CDF_MaybeBuiltin) {
- return CheckerContext::isCLibraryFunction(FD, CD.getFunctionName()) &&
- (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs()) &&
- (!CD.RequiredParams || CD.RequiredParams <= parameters().size());
- }
-
- if (!CD.IsLookupDone) {
- CD.IsLookupDone = true;
- CD.II = &getState()->getStateManager().getContext().Idents.get(
- CD.getFunctionName());
- }
-
- if (II != CD.II)
- return false;
-
- // If CallDescription provides prefix names, use them to improve matching
- // accuracy.
- if (CD.QualifiedName.size() > 1 && FD) {
- const DeclContext *Ctx = FD->getDeclContext();
- // See if we'll be able to match them all.
- size_t NumUnmatched = CD.QualifiedName.size() - 1;
- for (; Ctx && isa<NamedDecl>(Ctx); Ctx = Ctx->getParent()) {
- if (NumUnmatched == 0)
- break;
-
- if (const auto *ND = dyn_cast<NamespaceDecl>(Ctx)) {
- if (ND->getName() == CD.QualifiedName[NumUnmatched - 1])
- --NumUnmatched;
- continue;
- }
-
- if (const auto *RD = dyn_cast<RecordDecl>(Ctx)) {
- if (RD->getName() == CD.QualifiedName[NumUnmatched - 1])
- --NumUnmatched;
- continue;
- }
- }
-
- if (NumUnmatched > 0)
- return false;
- }
-
- return (!CD.RequiredArgs || CD.RequiredArgs == getNumArgs()) &&
- (!CD.RequiredParams || CD.RequiredParams == parameters().size());
+ return PreImplicitCall(D, Loc, getLocationContext(), ElemRef, Tag);
+ return PostImplicitCall(D, Loc, getLocationContext(), ElemRef, Tag);
}
SVal CallEvent::getArgSVal(unsigned Index) const {
@@ -406,7 +332,6 @@ void CallEvent::dump(raw_ostream &Out) const {
ASTContext &Ctx = getState()->getStateManager().getContext();
if (const Expr *E = getOriginExpr()) {
E->printPretty(Out, nullptr, Ctx.getPrintingPolicy());
- Out << "\n";
return;
}
@@ -420,9 +345,7 @@ void CallEvent::dump(raw_ostream &Out) const {
}
bool CallEvent::isCallStmt(const Stmt *S) {
- return isa<CallExpr>(S) || isa<ObjCMessageExpr>(S)
- || isa<CXXConstructExpr>(S)
- || isa<CXXNewExpr>(S);
+ return isa<CallExpr, ObjCMessageExpr, CXXConstructExpr, CXXNewExpr>(S);
}
QualType CallEvent::getDeclaredResultType(const Decl *D) {
@@ -503,6 +426,38 @@ static SVal processArgument(SVal Value, const Expr *ArgumentExpr,
return Value;
}
+/// Cast the argument value to the type of the parameter at the function
+/// declaration.
+/// Returns the argument value if it didn't need a cast.
+/// Or returns the cast argument if it needed a cast.
+/// Or returns 'Unknown' if it would need a cast but the callsite and the
+/// runtime definition don't match in terms of argument and parameter count.
+static SVal castArgToParamTypeIfNeeded(const CallEvent &Call, unsigned ArgIdx,
+ SVal ArgVal, SValBuilder &SVB) {
+ const FunctionDecl *RTDecl =
+ Call.getRuntimeDefinition().getDecl()->getAsFunction();
+ const auto *CallExprDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+
+ if (!RTDecl || !CallExprDecl)
+ return ArgVal;
+
+ // The function decl of the Call (in the AST) will not have any parameter
+ // declarations, if it was 'only' declared without a prototype. However, the
+ // engine will find the appropriate runtime definition - basically a
+ // redeclaration, which has a function body (and a function prototype).
+ if (CallExprDecl->hasPrototype() || !RTDecl->hasPrototype())
+ return ArgVal;
+
+ // Only do this cast if the number arguments at the callsite matches with
+ // the parameters at the runtime definition.
+ if (Call.getNumArgs() != RTDecl->getNumParams())
+ return UnknownVal();
+
+ const Expr *ArgExpr = Call.getArgExpr(ArgIdx);
+ const ParmVarDecl *Param = RTDecl->getParamDecl(ArgIdx);
+ return SVB.evalCast(ArgVal, Param->getType(), ArgExpr->getType());
+}
+
static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
CallEvent::BindingsTy &Bindings,
SValBuilder &SVB,
@@ -528,12 +483,18 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
// which makes getArgSVal() fail and return UnknownVal.
SVal ArgVal = Call.getArgSVal(Idx);
const Expr *ArgExpr = Call.getArgExpr(Idx);
- if (!ArgVal.isUnknown()) {
- Loc ParamLoc = SVB.makeLoc(
- MRMgr.getParamVarRegion(Call.getOriginExpr(), Idx, CalleeCtx));
- Bindings.push_back(
- std::make_pair(ParamLoc, processArgument(ArgVal, ArgExpr, *I, SVB)));
- }
+
+ if (ArgVal.isUnknown())
+ continue;
+
+ // Cast the argument value to match the type of the parameter in some
+ // edge-cases.
+ ArgVal = castArgToParamTypeIfNeeded(Call, Idx, ArgVal, SVB);
+
+ Loc ParamLoc = SVB.makeLoc(
+ MRMgr.getParamVarRegion(Call.getOriginExpr(), Idx, CalleeCtx));
+ Bindings.push_back(
+ std::make_pair(ParamLoc, processArgument(ArgVal, ArgExpr, *I, SVB)));
}
// FIXME: Variadic arguments are not handled at all right now.
@@ -556,24 +517,43 @@ const ConstructionContext *CallEvent::getConstructionContext() const {
return nullptr;
}
-Optional<SVal>
-CallEvent::getReturnValueUnderConstruction() const {
+const CallEventRef<> CallEvent::getCaller() const {
+ const auto *CallLocationContext = this->getLocationContext();
+ if (!CallLocationContext || CallLocationContext->inTopFrame())
+ return nullptr;
+
+ const auto *CallStackFrameContext = CallLocationContext->getStackFrame();
+ if (!CallStackFrameContext)
+ return nullptr;
+
+ CallEventManager &CEMgr = State->getStateManager().getCallEventManager();
+ return CEMgr.getCaller(CallStackFrameContext, State);
+}
+
+bool CallEvent::isCalledFromSystemHeader() const {
+ if (const CallEventRef<> Caller = getCaller())
+ return Caller->isInSystemHeader();
+
+ return false;
+}
+
+std::optional<SVal> CallEvent::getReturnValueUnderConstruction() const {
const auto *CC = getConstructionContext();
if (!CC)
- return None;
+ return std::nullopt;
EvalCallOptions CallOpts;
ExprEngine &Engine = getState()->getStateManager().getOwningEngine();
- SVal RetVal =
- Engine.computeObjectUnderConstruction(getOriginExpr(), getState(),
- getLocationContext(), CC, CallOpts);
+ SVal RetVal = Engine.computeObjectUnderConstruction(
+ getOriginExpr(), getState(), &Engine.getBuilderContext(),
+ getLocationContext(), CC, CallOpts);
return RetVal;
}
ArrayRef<ParmVarDecl*> AnyFunctionCall::parameters() const {
const FunctionDecl *D = getDecl();
if (!D)
- return None;
+ return std::nullopt;
return D->parameters();
}
@@ -594,20 +574,28 @@ RuntimeDefinition AnyFunctionCall::getRuntimeDefinition() const {
llvm::dbgs() << "Using autosynthesized body for " << FD->getName()
<< "\n";
});
- if (Body) {
- const Decl* Decl = AD->getDecl();
- return RuntimeDefinition(Decl);
- }
ExprEngine &Engine = getState()->getStateManager().getOwningEngine();
+ cross_tu::CrossTranslationUnitContext &CTUCtx =
+ *Engine.getCrossTranslationUnitContext();
+
AnalyzerOptions &Opts = Engine.getAnalysisManager().options;
+ if (Body) {
+ const Decl* Decl = AD->getDecl();
+ if (Opts.IsNaiveCTUEnabled && CTUCtx.isImportedAsNew(Decl)) {
+ // A newly created definition, but we had error(s) during the import.
+ if (CTUCtx.hasError(Decl))
+ return {};
+ return RuntimeDefinition(Decl, /*Foreign=*/true);
+ }
+ return RuntimeDefinition(Decl, /*Foreign=*/false);
+ }
+
// Try to get CTU definition only if CTUDir is provided.
if (!Opts.IsNaiveCTUEnabled)
return {};
- cross_tu::CrossTranslationUnitContext &CTUCtx =
- *Engine.getCrossTranslationUnitContext();
llvm::Expected<const FunctionDecl *> CTUDeclOrError =
CTUCtx.getCrossTUDefinition(FD, Opts.CTUDir, Opts.CTUIndexName,
Opts.DisplayCTUProgress);
@@ -620,7 +608,7 @@ RuntimeDefinition AnyFunctionCall::getRuntimeDefinition() const {
return {};
}
- return RuntimeDefinition(*CTUDeclOrError);
+ return RuntimeDefinition(*CTUDeclOrError, /*Foreign=*/true);
}
void AnyFunctionCall::getInitialStackFrameContents(
@@ -671,17 +659,17 @@ bool AnyFunctionCall::argumentsMayEscape() const {
// - CoreFoundation functions that end with "NoCopy" can free a passed-in
// buffer even if it is const.
- if (FName.endswith("NoCopy"))
+ if (FName.ends_with("NoCopy"))
return true;
// - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
// be deallocated by NSMapRemove.
- if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos))
+ if (FName.starts_with("NS") && FName.contains("Insert"))
return true;
// - Many CF containers allow objects to escape through custom
// allocators/deallocators upon container construction. (PR12101)
- if (FName.startswith("CF") || FName.startswith("CG")) {
+ if (FName.starts_with("CF") || FName.starts_with("CG")) {
return StrInStrNoCase(FName, "InsertValue") != StringRef::npos ||
StrInStrNoCase(FName, "AddValue") != StringRef::npos ||
StrInStrNoCase(FName, "SetValue") != StringRef::npos ||
@@ -747,11 +735,15 @@ void CXXInstanceCall::getExtraInvalidatedValues(
SVal CXXInstanceCall::getCXXThisVal() const {
const Expr *Base = getCXXThisExpr();
// FIXME: This doesn't handle an overloaded ->* operator.
- if (!Base)
- return UnknownVal();
+ SVal ThisVal = Base ? getSVal(Base) : UnknownVal();
- SVal ThisVal = getSVal(Base);
- assert(ThisVal.isUnknownOrUndef() || ThisVal.getAs<Loc>());
+ if (isa<NonLoc>(ThisVal)) {
+ SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
+ QualType OriginalTy = ThisVal.getType(SVB.getContext());
+ return SVB.evalCast(ThisVal, Base->getType(), OriginalTy);
+ }
+
+ assert(ThisVal.isUnknownOrUndef() || isa<Loc>(ThisVal));
return ThisVal;
}
@@ -797,8 +789,9 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const {
// the static type. However, because we currently don't update
// DynamicTypeInfo when an object is cast, we can't actually be sure the
// DynamicTypeInfo is up to date. This assert should be re-enabled once
- // this is fixed. <rdar://problem/12287087>
- //assert(!MD->getParent()->isDerivedFrom(RD) && "Bad DynamicTypeInfo");
+ // this is fixed.
+ //
+ // assert(!MD->getParent()->isDerivedFrom(RD) && "Bad DynamicTypeInfo");
return {};
}
@@ -841,9 +834,9 @@ void CXXInstanceCall::getInitialStackFrameContents(
QualType Ty = Ctx.getPointerType(Ctx.getRecordType(Class));
// FIXME: CallEvent maybe shouldn't be directly accessing StoreManager.
- bool Failed;
- ThisVal = StateMgr.getStoreManager().attemptDownCast(ThisVal, Ty, Failed);
- if (Failed) {
+ std::optional<SVal> V =
+ StateMgr.getStoreManager().evalBaseToDerived(ThisVal, Ty);
+ if (!V) {
// We might have suffered some sort of placement new earlier, so
// we're constructing in a completely unexpected storage.
// Fall back to a generic pointer cast for this-value.
@@ -851,7 +844,8 @@ void CXXInstanceCall::getInitialStackFrameContents(
const CXXRecordDecl *StaticClass = StaticMD->getParent();
QualType StaticTy = Ctx.getPointerType(Ctx.getRecordType(StaticClass));
ThisVal = SVB.evalCast(ThisVal, Ty, StaticTy);
- }
+ } else
+ ThisVal = *V;
}
if (!ThisVal.isUnknown())
@@ -889,7 +883,7 @@ const BlockDataRegion *BlockCall::getBlockRegion() const {
ArrayRef<ParmVarDecl*> BlockCall::parameters() const {
const BlockDecl *D = getDecl();
if (!D)
- return None;
+ return std::nullopt;
return D->parameters();
}
@@ -978,7 +972,7 @@ RuntimeDefinition CXXDestructorCall::getRuntimeDefinition() const {
ArrayRef<ParmVarDecl*> ObjCMethodCall::parameters() const {
const ObjCMethodDecl *D = getDecl();
if (!D)
- return None;
+ return std::nullopt;
return D->parameters();
}
@@ -1058,12 +1052,12 @@ const PseudoObjectExpr *ObjCMethodCall::getContainingPseudoObjectExpr() const {
static const Expr *
getSyntacticFromForPseudoObjectExpr(const PseudoObjectExpr *POE) {
- const Expr *Syntactic = POE->getSyntacticForm();
+ const Expr *Syntactic = POE->getSyntacticForm()->IgnoreParens();
// This handles the funny case of assigning to the result of a getter.
// This can happen if the getter returns a non-const reference.
if (const auto *BO = dyn_cast<BinaryOperator>(Syntactic))
- Syntactic = BO->getLHS();
+ Syntactic = BO->getLHS()->IgnoreParens();
return Syntactic;
}
@@ -1194,7 +1188,7 @@ static const ObjCMethodDecl *findDefiningRedecl(const ObjCMethodDecl *MD) {
// Find the redeclaration that defines the method.
if (!MD->hasBody()) {
- for (auto I : MD->redecls())
+ for (auto *I : MD->redecls())
if (I->hasBody())
MD = cast<ObjCMethodDecl>(I);
}
@@ -1255,14 +1249,14 @@ lookupRuntimeDefinition(const ObjCInterfaceDecl *Interface,
// stays around until clang quits, which also may be bad if we
// need to release memory.
using PrivateMethodCache =
- llvm::DenseMap<PrivateMethodKey, Optional<const ObjCMethodDecl *>>;
+ llvm::DenseMap<PrivateMethodKey, std::optional<const ObjCMethodDecl *>>;
static PrivateMethodCache PMC;
- Optional<const ObjCMethodDecl *> &Val =
+ std::optional<const ObjCMethodDecl *> &Val =
PMC[{Interface, LookupSelector, InstanceMethod}];
// Query lookupPrivateMethod() if the cache does not hit.
- if (!Val.hasValue()) {
+ if (!Val) {
Val = Interface->lookupPrivateMethod(LookupSelector, InstanceMethod);
if (!*Val) {
@@ -1271,7 +1265,7 @@ lookupRuntimeDefinition(const ObjCInterfaceDecl *Interface,
}
}
- return Val.getValue();
+ return *Val;
}
RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
@@ -1407,23 +1401,24 @@ void ObjCMethodCall::getInitialStackFrameContents(
CallEventRef<>
CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State,
- const LocationContext *LCtx) {
+ const LocationContext *LCtx,
+ CFGBlock::ConstCFGElementRef ElemRef) {
if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(CE))
- return create<CXXMemberCall>(MCE, State, LCtx);
+ return create<CXXMemberCall>(MCE, State, LCtx, ElemRef);
if (const auto *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) {
const FunctionDecl *DirectCallee = OpCE->getDirectCallee();
if (const auto *MD = dyn_cast<CXXMethodDecl>(DirectCallee))
- if (MD->isInstance())
- return create<CXXMemberOperatorCall>(OpCE, State, LCtx);
+ if (MD->isImplicitObjectMemberFunction())
+ return create<CXXMemberOperatorCall>(OpCE, State, LCtx, ElemRef);
} else if (CE->getCallee()->getType()->isBlockPointerType()) {
- return create<BlockCall>(CE, State, LCtx);
+ return create<BlockCall>(CE, State, LCtx, ElemRef);
}
// Otherwise, it's a normal function call, static member function call, or
// something we can't reason about.
- return create<SimpleFunctionCall>(CE, State, LCtx);
+ return create<SimpleFunctionCall>(CE, State, LCtx, ElemRef);
}
CallEventRef<>
@@ -1431,12 +1426,14 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
ProgramStateRef State) {
const LocationContext *ParentCtx = CalleeCtx->getParent();
const LocationContext *CallerCtx = ParentCtx->getStackFrame();
+ CFGBlock::ConstCFGElementRef ElemRef = {CalleeCtx->getCallSiteBlock(),
+ CalleeCtx->getIndex()};
assert(CallerCtx && "This should not be used for top-level stack frames");
const Stmt *CallSite = CalleeCtx->getCallSite();
if (CallSite) {
- if (CallEventRef<> Out = getCall(CallSite, State, CallerCtx))
+ if (CallEventRef<> Out = getCall(CallSite, State, CallerCtx, ElemRef))
return Out;
SValBuilder &SVB = State->getStateManager().getSValBuilder();
@@ -1445,10 +1442,11 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
SVal ThisVal = State->getSVal(ThisPtr);
if (const auto *CE = dyn_cast<CXXConstructExpr>(CallSite))
- return getCXXConstructorCall(CE, ThisVal.getAsRegion(), State, CallerCtx);
+ return getCXXConstructorCall(CE, ThisVal.getAsRegion(), State, CallerCtx,
+ ElemRef);
else if (const auto *CIE = dyn_cast<CXXInheritedCtorInitExpr>(CallSite))
return getCXXInheritedConstructorCall(CIE, ThisVal.getAsRegion(), State,
- CallerCtx);
+ CallerCtx, ElemRef);
else {
// All other cases are handled by getCall.
llvm_unreachable("This is not an inlineable statement");
@@ -1468,26 +1466,30 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
SVal ThisVal = State->getSVal(ThisPtr);
const Stmt *Trigger;
- if (Optional<CFGAutomaticObjDtor> AutoDtor = E.getAs<CFGAutomaticObjDtor>())
+ if (std::optional<CFGAutomaticObjDtor> AutoDtor =
+ E.getAs<CFGAutomaticObjDtor>())
Trigger = AutoDtor->getTriggerStmt();
- else if (Optional<CFGDeleteDtor> DeleteDtor = E.getAs<CFGDeleteDtor>())
+ else if (std::optional<CFGDeleteDtor> DeleteDtor = E.getAs<CFGDeleteDtor>())
Trigger = DeleteDtor->getDeleteExpr();
else
Trigger = Dtor->getBody();
return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(),
- E.getAs<CFGBaseDtor>().hasValue(), State,
- CallerCtx);
+ E.getAs<CFGBaseDtor>().has_value(), State,
+ CallerCtx, ElemRef);
}
CallEventRef<> CallEventManager::getCall(const Stmt *S, ProgramStateRef State,
- const LocationContext *LC) {
+ const LocationContext *LC,
+ CFGBlock::ConstCFGElementRef ElemRef) {
if (const auto *CE = dyn_cast<CallExpr>(S)) {
- return getSimpleCall(CE, State, LC);
+ return getSimpleCall(CE, State, LC, ElemRef);
} else if (const auto *NE = dyn_cast<CXXNewExpr>(S)) {
- return getCXXAllocatorCall(NE, State, LC);
+ return getCXXAllocatorCall(NE, State, LC, ElemRef);
+ } else if (const auto *DE = dyn_cast<CXXDeleteExpr>(S)) {
+ return getCXXDeallocatorCall(DE, State, LC, ElemRef);
} else if (const auto *ME = dyn_cast<ObjCMessageExpr>(S)) {
- return getObjCMethodCall(ME, State, LC);
+ return getObjCMethodCall(ME, State, LC, ElemRef);
} else {
return nullptr;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp
index 3d64ce453479..d6d4cec9dd3d 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp
@@ -14,6 +14,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/Basic/Builtins.h"
#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringExtras.h"
using namespace clang;
using namespace ento;
@@ -38,7 +39,7 @@ StringRef CheckerContext::getCalleeName(const FunctionDecl *FunDecl) const {
}
StringRef CheckerContext::getDeclDescription(const Decl *D) {
- if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
+ if (isa<ObjCMethodDecl, CXXMethodDecl>(D))
return "method";
if (isa<BlockDecl>(D))
return "anonymous block";
@@ -55,8 +56,29 @@ bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD,
if (Name.empty())
return true;
StringRef BName = FD->getASTContext().BuiltinInfo.getName(BId);
- if (BName.find(Name) != StringRef::npos)
- return true;
+ size_t start = BName.find(Name);
+ if (start != StringRef::npos) {
+ // Accept exact match.
+ if (BName.size() == Name.size())
+ return true;
+
+ // v-- match starts here
+ // ...xxxxx...
+ // _xxxxx_
+ // ^ ^ lookbehind and lookahead characters
+
+ const auto MatchPredecessor = [=]() -> bool {
+ return start <= 0 || !llvm::isAlpha(BName[start - 1]);
+ };
+ const auto MatchSuccessor = [=]() -> bool {
+ std::size_t LookbehindPlace = start + Name.size();
+ return LookbehindPlace >= BName.size() ||
+ !llvm::isAlpha(BName[LookbehindPlace]);
+ };
+
+ if (MatchPredecessor() && MatchSuccessor())
+ return true;
+ }
}
const IdentifierInfo *II = FD->getIdentifier();
@@ -83,11 +105,11 @@ bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD,
if (FName.equals(Name))
return true;
- if (FName.startswith("__inline") && (FName.find(Name) != StringRef::npos))
+ if (FName.starts_with("__inline") && FName.contains(Name))
return true;
- if (FName.startswith("__") && FName.endswith("_chk") &&
- FName.find(Name) != StringRef::npos)
+ if (FName.starts_with("__") && FName.ends_with("_chk") &&
+ FName.contains(Name))
return true;
return false;
@@ -107,10 +129,10 @@ static bool evalComparison(SVal LHSVal, BinaryOperatorKind ComparisonOp,
if (LHSVal.isUnknownOrUndef())
return false;
ProgramStateManager &Mgr = State->getStateManager();
- if (!LHSVal.getAs<NonLoc>()) {
+ if (!isa<NonLoc>(LHSVal)) {
LHSVal = Mgr.getStoreManager().getBinding(State->getStore(),
LHSVal.castAs<Loc>());
- if (LHSVal.isUnknownOrUndef() || !LHSVal.getAs<NonLoc>())
+ if (LHSVal.isUnknownOrUndef() || !isa<NonLoc>(LHSVal))
return false;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
index 626ae1ae8066..84ad20a54807 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
@@ -14,6 +14,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/Lex/Preprocessor.h"
+#include <optional>
namespace clang {
@@ -110,14 +111,13 @@ Nullability getNullabilityAnnotation(QualType Type) {
return Nullability::Unspecified;
}
-llvm::Optional<int> tryExpandAsInteger(StringRef Macro,
- const Preprocessor &PP) {
+std::optional<int> tryExpandAsInteger(StringRef Macro, const Preprocessor &PP) {
const auto *MacroII = PP.getIdentifierInfo(Macro);
if (!MacroII)
- return llvm::None;
+ return std::nullopt;
const MacroInfo *MI = PP.getMacroInfo(MacroII);
if (!MI)
- return llvm::None;
+ return std::nullopt;
// Filter out parens.
std::vector<Token> FilteredTokens;
@@ -131,12 +131,12 @@ llvm::Optional<int> tryExpandAsInteger(StringRef Macro,
// 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;
+ return std::nullopt;
StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength());
llvm::APInt IntValue;
constexpr unsigned AutoSenseRadix = 0;
if (ValueStr.getAsInteger(AutoSenseRadix, IntValue))
- return llvm::None;
+ return std::nullopt;
// Parse an optional minus sign.
size_t Size = FilteredTokens.size();
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
index e09399a83589..6fc16223ea82 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -26,7 +26,9 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormatVariadic.h"
#include <cassert>
+#include <optional>
#include <vector>
using namespace clang;
@@ -34,10 +36,7 @@ using namespace ento;
bool CheckerManager::hasPathSensitiveCheckers() const {
const auto IfAnyAreNonEmpty = [](const auto &... Callbacks) -> bool {
- bool Result = false;
- // FIXME: Use fold expressions in C++17.
- LLVM_ATTRIBUTE_UNUSED int Unused[]{0, (Result |= !Callbacks.empty())...};
- return Result;
+ return (!Callbacks.empty() || ...);
};
return IfAnyAreNonEmpty(
StmtCheckers, PreObjCMessageCheckers, ObjCMessageNilCheckers,
@@ -655,7 +654,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
ExprEngine &Eng,
const EvalCallOptions &CallOpts) {
for (auto *const Pred : Src) {
- bool anyEvaluated = false;
+ std::optional<CheckerNameRef> evaluatorChecker;
ExplodedNodeSet checkDst;
NodeBuilder B(Pred, checkDst, Eng.getBuilderContext());
@@ -674,10 +673,26 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
CheckerContext C(B, Eng, Pred, L);
evaluated = EvalCallChecker(Call, C);
}
- assert(!(evaluated && anyEvaluated)
- && "There are more than one checkers evaluating the call");
+#ifndef NDEBUG
+ if (evaluated && evaluatorChecker) {
+ const auto toString = [](const CallEvent &Call) -> std::string {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ Call.dump(OS);
+ OS.flush();
+ return Buf;
+ };
+ std::string AssertionMessage = llvm::formatv(
+ "The '{0}' call has been already evaluated by the {1} checker, "
+ "while the {2} checker also tried to evaluate the same call. At "
+ "most one checker supposed to evaluate a call.",
+ toString(Call), evaluatorChecker->getName(),
+ EvalCallChecker.Checker->getCheckerName());
+ llvm_unreachable(AssertionMessage.c_str());
+ }
+#endif
if (evaluated) {
- anyEvaluated = true;
+ evaluatorChecker = EvalCallChecker.Checker->getCheckerName();
Dst.insert(checkDst);
#ifdef NDEBUG
break; // on release don't check that no other checker also evals.
@@ -686,7 +701,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
}
// If none of the checkers evaluated the call, ask ExprEngine to handle it.
- if (!anyEvaluated) {
+ if (!evaluatorChecker) {
NodeBuilder B(Pred, Dst, Eng.getBuilderContext());
Eng.defaultEvalCall(B, Pred, Call, CallOpts);
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp
index 1b3e8b11549d..b9c6278991f4 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp
@@ -82,7 +82,7 @@ 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))
+ if (!Checker.FullName.starts_with(PackageName))
return false;
// Is the package actually just the name of a specific checker?
@@ -158,7 +158,7 @@ void CheckerRegistryData::printCheckerWithDescList(
continue;
}
- if (Checker.FullName.startswith("alpha")) {
+ if (Checker.FullName.starts_with("alpha")) {
if (AnOpts.ShowCheckerHelpAlpha)
Print(Out, Checker,
("(Enable only for development!) " + Checker.Desc).str());
@@ -228,7 +228,7 @@ void CheckerRegistryData::printCheckerOptionList(const AnalyzerOptions &AnOpts,
}
if (Option.DevelopmentStatus == "alpha" ||
- Entry.first.startswith("alpha")) {
+ Entry.first.starts_with("alpha")) {
if (AnOpts.ShowCheckerOptionAlphaList)
Print(Out, FullOption,
llvm::Twine("(Enable only for development!) " + Desc).str());
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
index d12c35ef156a..66fab523c864 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
@@ -13,6 +13,7 @@ namespace clang {
namespace ento {
namespace categories {
+const char *const AppleAPIMisuse = "API Misuse (Apple)";
const char *const CoreFoundationObjectiveC = "Core Foundation/Objective-C";
const char *const LogicError = "Logic error";
const char *const MemoryRefCount =
@@ -23,6 +24,7 @@ const char *const CXXObjectLifecycle = "C++ object lifecycle";
const char *const CXXMoveSemantics = "C++ move semantics";
const char *const SecurityError = "Security error";
const char *const UnusedCode = "Unused code";
+const char *const TaintedData = "Tainted data used";
} // namespace categories
} // namespace ento
} // namespace clang
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp
index d642c3530268..c0b3f346b654 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp
@@ -16,6 +16,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "llvm/ADT/ScopeExit.h"
using namespace clang;
using namespace ento;
@@ -41,3 +42,82 @@ ConditionTruthVal ConstraintManager::checkNull(ProgramStateRef State,
return ConditionTruthVal(true);
return {};
}
+
+template <typename AssumeFunction>
+ConstraintManager::ProgramStatePair
+ConstraintManager::assumeDualImpl(ProgramStateRef &State,
+ AssumeFunction &Assume) {
+ if (LLVM_UNLIKELY(State->isPosteriorlyOverconstrained()))
+ return {State, State};
+
+ // Assume functions might recurse (see `reAssume` or `tryRearrange`). During
+ // the recursion the State might not change anymore, that means we reached a
+ // fixpoint.
+ // We avoid infinite recursion of assume calls by checking already visited
+ // States on the stack of assume function calls.
+ const ProgramState *RawSt = State.get();
+ if (LLVM_UNLIKELY(AssumeStack.contains(RawSt)))
+ return {State, State};
+ AssumeStack.push(RawSt);
+ auto AssumeStackBuilder =
+ llvm::make_scope_exit([this]() { AssumeStack.pop(); });
+
+ ProgramStateRef StTrue = Assume(true);
+
+ if (!StTrue) {
+ ProgramStateRef StFalse = Assume(false);
+ if (LLVM_UNLIKELY(!StFalse)) { // both infeasible
+ ProgramStateRef StInfeasible = State->cloneAsPosteriorlyOverconstrained();
+ assert(StInfeasible->isPosteriorlyOverconstrained());
+ // Checkers might rely on the API contract that both returned states
+ // cannot be null. Thus, we return StInfeasible for both branches because
+ // it might happen that a Checker uncoditionally uses one of them if the
+ // other is a nullptr. This may also happen with the non-dual and
+ // adjacent `assume(true)` and `assume(false)` calls. By implementing
+ // assume in therms of assumeDual, we can keep our API contract there as
+ // well.
+ return ProgramStatePair(StInfeasible, StInfeasible);
+ }
+ return ProgramStatePair(nullptr, StFalse);
+ }
+
+ ProgramStateRef StFalse = Assume(false);
+ if (!StFalse) {
+ return ProgramStatePair(StTrue, nullptr);
+ }
+
+ return ProgramStatePair(StTrue, StFalse);
+}
+
+ConstraintManager::ProgramStatePair
+ConstraintManager::assumeDual(ProgramStateRef State, DefinedSVal Cond) {
+ auto AssumeFun = [&, Cond](bool Assumption) {
+ return assumeInternal(State, Cond, Assumption);
+ };
+ return assumeDualImpl(State, AssumeFun);
+}
+
+ConstraintManager::ProgramStatePair
+ConstraintManager::assumeInclusiveRangeDual(ProgramStateRef State, NonLoc Value,
+ const llvm::APSInt &From,
+ const llvm::APSInt &To) {
+ auto AssumeFun = [&](bool Assumption) {
+ return assumeInclusiveRangeInternal(State, Value, From, To, Assumption);
+ };
+ return assumeDualImpl(State, AssumeFun);
+}
+
+ProgramStateRef ConstraintManager::assume(ProgramStateRef State,
+ DefinedSVal Cond, bool Assumption) {
+ ConstraintManager::ProgramStatePair R = assumeDual(State, Cond);
+ return Assumption ? R.first : R.second;
+}
+
+ProgramStateRef
+ConstraintManager::assumeInclusiveRange(ProgramStateRef State, NonLoc Value,
+ const llvm::APSInt &From,
+ const llvm::APSInt &To, bool InBound) {
+ ConstraintManager::ProgramStatePair R =
+ assumeInclusiveRangeDual(State, Value, From, To);
+ return InBound ? R.first : R.second;
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
index bc939d252800..d3499e7a917d 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -26,7 +26,6 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Casting.h"
@@ -34,6 +33,7 @@
#include <algorithm>
#include <cassert>
#include <memory>
+#include <optional>
#include <utility>
using namespace clang;
@@ -43,6 +43,8 @@ using namespace ento;
STATISTIC(NumSteps,
"The # of steps executed.");
+STATISTIC(NumSTUSteps, "The # of STU steps executed.");
+STATISTIC(NumCTUSteps, "The # of CTU steps executed.");
STATISTIC(NumReachedMaxSteps,
"The # of times we reached the max number of steps.");
STATISTIC(NumPathsExplored,
@@ -73,11 +75,18 @@ static std::unique_ptr<WorkList> generateWorkList(AnalyzerOptions &Opts) {
CoreEngine::CoreEngine(ExprEngine &exprengine, FunctionSummariesTy *FS,
AnalyzerOptions &Opts)
: ExprEng(exprengine), WList(generateWorkList(Opts)),
+ CTUWList(Opts.IsNaiveCTUEnabled ? generateWorkList(Opts) : nullptr),
BCounterFactory(G.getAllocator()), FunctionSummaries(FS) {}
+void CoreEngine::setBlockCounter(BlockCounter C) {
+ WList->setBlockCounter(C);
+ if (CTUWList)
+ CTUWList->setBlockCounter(C);
+}
+
/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps.
-bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
- ProgramStateRef InitState) {
+bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned MaxSteps,
+ ProgramStateRef InitState) {
if (G.num_roots() == 0) { // Initialize the analysis by constructing
// the root if none exists.
@@ -100,7 +109,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
BlockEdge StartLoc(Entry, Succ, L);
// Set the current block counter to being empty.
- WList->setBlockCounter(BCounterFactory.GetEmptyCounter());
+ setBlockCounter(BCounterFactory.GetEmptyCounter());
if (!InitState)
InitState = ExprEng.getInitialState(L);
@@ -118,34 +127,54 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
}
// Check if we have a steps limit
- bool UnlimitedSteps = Steps == 0;
+ bool UnlimitedSteps = MaxSteps == 0;
+
// Cap our pre-reservation in the event that the user specifies
// a very large number of maximum steps.
const unsigned PreReservationCap = 4000000;
if(!UnlimitedSteps)
- G.reserve(std::min(Steps,PreReservationCap));
-
- while (WList->hasWork()) {
- if (!UnlimitedSteps) {
- if (Steps == 0) {
- NumReachedMaxSteps++;
- break;
+ G.reserve(std::min(MaxSteps, PreReservationCap));
+
+ auto ProcessWList = [this, UnlimitedSteps](unsigned MaxSteps) {
+ unsigned Steps = MaxSteps;
+ while (WList->hasWork()) {
+ if (!UnlimitedSteps) {
+ if (Steps == 0) {
+ NumReachedMaxSteps++;
+ break;
+ }
+ --Steps;
}
- --Steps;
- }
- NumSteps++;
+ NumSteps++;
- const WorkListUnit& WU = WList->dequeue();
+ const WorkListUnit &WU = WList->dequeue();
- // Set the current block counter.
- WList->setBlockCounter(WU.getBlockCounter());
+ // Set the current block counter.
+ setBlockCounter(WU.getBlockCounter());
- // Retrieve the node.
- ExplodedNode *Node = WU.getNode();
+ // Retrieve the node.
+ ExplodedNode *Node = WU.getNode();
- dispatchWorkItem(Node, Node->getLocation(), WU);
+ dispatchWorkItem(Node, Node->getLocation(), WU);
+ }
+ return MaxSteps - Steps;
+ };
+ const unsigned STUSteps = ProcessWList(MaxSteps);
+
+ if (CTUWList) {
+ NumSTUSteps += STUSteps;
+ const unsigned MinCTUSteps =
+ this->ExprEng.getAnalysisManager().options.CTUMaxNodesMin;
+ const unsigned Pct =
+ this->ExprEng.getAnalysisManager().options.CTUMaxNodesPercentage;
+ unsigned MaxCTUSteps = std::max(STUSteps * Pct / 100, MinCTUSteps);
+
+ WList = std::move(CTUWList);
+ const unsigned CTUSteps = ProcessWList(MaxCTUSteps);
+ NumCTUSteps += CTUSteps;
}
+
ExprEng.processEndWorklist();
return WList->hasWork();
}
@@ -244,10 +273,10 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
const ReturnStmt *RS = nullptr;
if (!L.getSrc()->empty()) {
CFGElement LastElement = L.getSrc()->back();
- if (Optional<CFGStmt> LastStmt = LastElement.getAs<CFGStmt>()) {
+ if (std::optional<CFGStmt> LastStmt = LastElement.getAs<CFGStmt>()) {
RS = dyn_cast<ReturnStmt>(LastStmt->getStmt());
- } else if (Optional<CFGAutomaticObjDtor> AutoDtor =
- LastElement.getAs<CFGAutomaticObjDtor>()) {
+ } else if (std::optional<CFGAutomaticObjDtor> AutoDtor =
+ LastElement.getAs<CFGAutomaticObjDtor>()) {
RS = dyn_cast<ReturnStmt>(AutoDtor->getTriggerStmt());
}
}
@@ -282,14 +311,13 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance &L,
BlockCounter Counter = WList->getBlockCounter();
Counter = BCounterFactory.IncrementCount(Counter, LC->getStackFrame(),
BlockId);
- WList->setBlockCounter(Counter);
+ setBlockCounter(Counter);
// Process the entrance of the block.
- if (Optional<CFGElement> E = L.getFirstElement()) {
+ if (std::optional<CFGElement> E = L.getFirstElement()) {
NodeBuilderContext Ctx(*this, L.getBlock(), Pred);
ExprEng.processCFGElement(*E, Pred, 0, &Ctx);
- }
- else
+ } else
HandleBlockExit(L.getBlock(), Pred);
}
@@ -475,8 +503,8 @@ void CoreEngine::HandleVirtualBaseBranch(const CFGBlock *B,
if (const auto *CallerCtor = dyn_cast_or_null<CXXConstructExpr>(
LCtx->getStackFrame()->getCallSite())) {
switch (CallerCtor->getConstructionKind()) {
- case CXXConstructExpr::CK_NonVirtualBase:
- case CXXConstructExpr::CK_VirtualBase: {
+ case CXXConstructionKind::NonVirtualBase:
+ case CXXConstructionKind::VirtualBase: {
BlockEdge Loc(B, *B->succ_begin(), LCtx);
HandleBlockEdge(Loc, Pred);
return;
@@ -587,7 +615,7 @@ void CoreEngine::enqueue(ExplodedNodeSet &Set,
}
void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS) {
- for (auto I : Set) {
+ for (auto *I : Set) {
// If we are in an inlined call, generate CallExitBegin node.
if (I->getLocationContext()->getParent()) {
I = generateCallExitBeginNode(I, RS);
@@ -686,8 +714,8 @@ SwitchNodeBuilder::generateDefaultCaseNode(ProgramStateRef St,
assert(Src->succ_rbegin() != Src->succ_rend());
CFGBlock *DefaultBlock = *Src->succ_rbegin();
- // Sanity check for default blocks that are unreachable and not caught
- // by earlier stages.
+ // Basic correctness check for default blocks that are unreachable and not
+ // caught by earlier stages.
if (!DefaultBlock)
return nullptr;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp
index db9698b4086e..6cf06413b537 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp
@@ -30,7 +30,9 @@ DefinedOrUnknownSVal getDynamicExtent(ProgramStateRef State,
MR = MR->StripCasts();
if (const DefinedOrUnknownSVal *Size = State->get<DynamicExtentMap>(MR))
- return *Size;
+ if (auto SSize =
+ SVB.convertToArrayIndex(*Size).getAs<DefinedOrUnknownSVal>())
+ return *SSize;
return MR->getMemRegionManager().getStaticSize(MR, SVB);
}
@@ -40,23 +42,49 @@ DefinedOrUnknownSVal getElementExtent(QualType Ty, SValBuilder &SVB) {
SVB.getArrayIndexType());
}
+static DefinedOrUnknownSVal getConstantArrayElementCount(SValBuilder &SVB,
+ const MemRegion *MR) {
+ MR = MR->StripCasts();
+
+ const auto *TVR = MR->getAs<TypedValueRegion>();
+ if (!TVR)
+ return UnknownVal();
+
+ if (const ConstantArrayType *CAT =
+ SVB.getContext().getAsConstantArrayType(TVR->getValueType()))
+ return SVB.makeIntVal(CAT->getSize(), /* isUnsigned = */ false);
+
+ return UnknownVal();
+}
+
+static DefinedOrUnknownSVal
+getDynamicElementCount(ProgramStateRef State, SVal Size,
+ DefinedOrUnknownSVal ElementSize) {
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+
+ auto ElementCount =
+ SVB.evalBinOp(State, BO_Div, Size, ElementSize, SVB.getArrayIndexType())
+ .getAs<DefinedOrUnknownSVal>();
+ return ElementCount.value_or(UnknownVal());
+}
+
DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State,
const MemRegion *MR,
SValBuilder &SVB,
QualType ElementTy) {
+ assert(MR != nullptr && "Not-null region expected");
MR = MR->StripCasts();
- DefinedOrUnknownSVal Size = getDynamicExtent(State, MR, SVB);
- SVal ElementSize = getElementExtent(ElementTy, SVB);
+ DefinedOrUnknownSVal ElementSize = getElementExtent(ElementTy, SVB);
+ if (ElementSize.isZeroConstant())
+ return getConstantArrayElementCount(SVB, MR);
- SVal ElementCount =
- SVB.evalBinOp(State, BO_Div, Size, ElementSize, SVB.getArrayIndexType());
-
- return ElementCount.castAs<DefinedOrUnknownSVal>();
+ return getDynamicElementCount(State, getDynamicExtent(State, MR, SVB),
+ ElementSize);
}
SVal getDynamicExtentWithOffset(ProgramStateRef State, SVal BufV) {
- SValBuilder &SvalBuilder = State->getStateManager().getSValBuilder();
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
const MemRegion *MRegion = BufV.getAsRegion();
if (!MRegion)
return UnknownVal();
@@ -67,15 +95,28 @@ SVal getDynamicExtentWithOffset(ProgramStateRef State, SVal BufV) {
if (!BaseRegion)
return UnknownVal();
- NonLoc OffsetInBytes = SvalBuilder.makeArrayIndex(
- Offset.getOffset() /
- MRegion->getMemRegionManager().getContext().getCharWidth());
- DefinedOrUnknownSVal ExtentInBytes =
- getDynamicExtent(State, BaseRegion, SvalBuilder);
+ NonLoc OffsetInChars =
+ SVB.makeArrayIndex(Offset.getOffset() / SVB.getContext().getCharWidth());
+ DefinedOrUnknownSVal ExtentInBytes = getDynamicExtent(State, BaseRegion, SVB);
+
+ return SVB.evalBinOp(State, BinaryOperator::Opcode::BO_Sub, ExtentInBytes,
+ OffsetInChars, SVB.getArrayIndexType());
+}
+
+DefinedOrUnknownSVal getDynamicElementCountWithOffset(ProgramStateRef State,
+ SVal BufV,
+ QualType ElementTy) {
+ const MemRegion *MR = BufV.getAsRegion();
+ if (!MR)
+ return UnknownVal();
+
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ DefinedOrUnknownSVal ElementSize = getElementExtent(ElementTy, SVB);
+ if (ElementSize.isZeroConstant())
+ return getConstantArrayElementCount(SVB, MR);
- return SvalBuilder.evalBinOp(State, BinaryOperator::Opcode::BO_Sub,
- ExtentInBytes, OffsetInBytes,
- SvalBuilder.getArrayIndexType());
+ return getDynamicElementCount(State, getDynamicExtentWithOffset(State, BufV),
+ ElementSize);
}
ProgramStateRef setDynamicExtent(ProgramStateRef State, const MemRegion *MR,
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicType.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicType.cpp
index 9ed915aafcab..06052cb99fd1 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicType.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicType.cpp
@@ -209,7 +209,7 @@ static raw_ostream &printJson(const DynamicTypeInfo &DTI, raw_ostream &Out,
if (ToPrint->isAnyPointerType())
ToPrint = ToPrint->getPointeeType();
- Out << '\"' << ToPrint.getAsString() << "\", \"sub_classable\": "
+ Out << '\"' << ToPrint << "\", \"sub_classable\": "
<< (DTI.canBeASubClass() ? "true" : "false");
}
return Out;
@@ -217,9 +217,9 @@ static raw_ostream &printJson(const DynamicTypeInfo &DTI, raw_ostream &Out,
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") << "\"";
+ return Out << "\"from\": \"" << DCI.from() << "\", \"to\": \"" << DCI.to()
+ << "\", \"kind\": \"" << (DCI.succeeds() ? "success" : "fail")
+ << "\"";
}
template <class T, class U>
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp
index ee7474592528..427f51109853 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp
@@ -17,9 +17,9 @@
#include "clang/AST/Stmt.h"
#include "clang/AST/StmtObjC.h"
#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Basic/JsonSupport.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
-#include "clang/Basic/JsonSupport.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
@@ -40,8 +40,11 @@ static const Expr *ignoreTransparentExprs(const Expr *E) {
switch (E->getStmtClass()) {
case Stmt::OpaqueValueExprClass:
- E = cast<OpaqueValueExpr>(E)->getSourceExpr();
- break;
+ if (const Expr *SE = cast<OpaqueValueExpr>(E)->getSourceExpr()) {
+ E = SE;
+ break;
+ }
+ return E;
case Stmt::ExprWithCleanupsClass:
E = cast<ExprWithCleanups>(E)->getSubExpr();
break;
@@ -88,7 +91,7 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry,
const Stmt *S = Entry.getStmt();
assert(!isa<ObjCForCollectionStmt>(S) &&
"Use ExprEngine::hasMoreIteration()!");
- assert((isa<Expr>(S) || isa<ReturnStmt>(S)) &&
+ assert((isa<Expr, ReturnStmt>(S)) &&
"Environment can only argue about Exprs, since only they express "
"a value! Any non-expression statement stored in Environment is a "
"result of a hack!");
@@ -98,7 +101,6 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry,
case Stmt::CXXBindTemporaryExprClass:
case Stmt::ExprWithCleanupsClass:
case Stmt::GenericSelectionExprClass:
- case Stmt::OpaqueValueExprClass:
case Stmt::ConstantExprClass:
case Stmt::ParenExprClass:
case Stmt::SubstNonTypeTemplateParmExprClass:
@@ -118,7 +120,7 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry,
case Stmt::SizeOfPackExprClass:
case Stmt::PredefinedExprClass:
// Known constants; defer to SValBuilder.
- return svalBuilder.getConstantVal(cast<Expr>(S)).getValue();
+ return *svalBuilder.getConstantVal(cast<Expr>(S));
case Stmt::ReturnStmtClass: {
const auto *RS = cast<ReturnStmt>(S);
@@ -193,7 +195,7 @@ EnvironmentManager::removeDeadBindings(Environment Env,
// Iterate over the block-expr bindings.
for (Environment::iterator I = Env.begin(), End = Env.end(); I != End; ++I) {
const EnvironmentEntry &BlkExpr = I.getKey();
- const SVal &X = I.getData();
+ SVal X = I.getData();
const Expr *E = dyn_cast<Expr>(BlkExpr.getStmt());
if (!E)
@@ -274,7 +276,8 @@ void Environment::printJson(raw_ostream &Out, const ASTContext &Ctx,
const Stmt *S = I->first.getStmt();
Indent(Out, InnerSpace, IsDot)
- << "{ \"stmt_id\": " << S->getID(Ctx) << ", \"pretty\": ";
+ << "{ \"stmt_id\": " << S->getID(Ctx) << ", \"kind\": \""
+ << S->getStmtClassName() << "\", \"pretty\": ";
S->printJson(Out, nullptr, PP, /*AddQuotes=*/true);
Out << ", \"value\": ";
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
index 635495e9bf60..f84da769d182 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
@@ -25,12 +25,12 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
#include <cassert>
#include <memory>
+#include <optional>
using namespace clang;
using namespace ento;
@@ -50,8 +50,7 @@ ExplodedGraph::~ExplodedGraph() = default;
bool ExplodedGraph::isInterestingLValueExpr(const Expr *Ex) {
if (!Ex->isLValue())
return false;
- return isa<DeclRefExpr>(Ex) || isa<MemberExpr>(Ex) ||
- isa<ObjCIvarRefExpr>(Ex) || isa<ArraySubscriptExpr>(Ex);
+ return isa<DeclRefExpr, MemberExpr, ObjCIvarRefExpr, ArraySubscriptExpr>(Ex);
}
bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
@@ -140,7 +139,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
// Condition 10.
const ProgramPoint SuccLoc = succ->getLocation();
- if (Optional<StmtPoint> SP = SuccLoc.getAs<StmtPoint>())
+ if (std::optional<StmtPoint> SP = SuccLoc.getAs<StmtPoint>())
if (CallEvent::isCallStmt(SP->getStmt()))
return false;
@@ -234,8 +233,7 @@ void ExplodedNode::NodeGroup::addNode(ExplodedNode *N, ExplodedGraph &G) {
ExplodedNode *Old = Storage.get<ExplodedNode *>();
BumpVectorContext &Ctx = G.getNodeAllocator();
- V = G.getAllocator().Allocate<ExplodedNodeVector>();
- new (V) ExplodedNodeVector(Ctx, 4);
+ V = new (G.getAllocator()) ExplodedNodeVector(Ctx, 4);
V->push_back(Old, Ctx);
Storage = V;
@@ -409,7 +407,7 @@ ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L,
}
else {
// Allocate a new node.
- V = (NodeTy*) getAllocator().Allocate<NodeTy>();
+ V = getAllocator().Allocate<NodeTy>();
}
++NumNodes;
@@ -433,7 +431,7 @@ ExplodedNode *ExplodedGraph::createUncachedNode(const ProgramPoint &L,
ProgramStateRef State,
int64_t Id,
bool IsSink) {
- NodeTy *V = (NodeTy *) getAllocator().Allocate<NodeTy>();
+ NodeTy *V = getAllocator().Allocate<NodeTy>();
new (V) NodeTy(L, State, Id, IsSink);
return V;
}
@@ -489,7 +487,7 @@ ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks,
const ExplodedNode *N = WL2.pop_back_val();
// Skip this node if we have already processed it.
- if (Pass2.find(N) != Pass2.end())
+ if (Pass2.contains(N))
continue;
// Create the corresponding node in the new graph and record the mapping
@@ -510,9 +508,8 @@ ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks,
// Walk through the predecessors of 'N' and hook up their corresponding
// nodes in the new graph (if any) to the freshly created node.
- for (ExplodedNode::pred_iterator I = N->Preds.begin(), E = N->Preds.end();
- I != E; ++I) {
- Pass2Ty::iterator PI = Pass2.find(*I);
+ for (const ExplodedNode *Pred : N->Preds) {
+ Pass2Ty::iterator PI = Pass2.find(Pred);
if (PI == Pass2.end())
continue;
@@ -523,17 +520,16 @@ ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks,
// been created, we should hook them up as successors. Otherwise, enqueue
// the new nodes from the original graph that should have nodes created
// in the new graph.
- for (ExplodedNode::succ_iterator I = N->Succs.begin(), E = N->Succs.end();
- I != E; ++I) {
- Pass2Ty::iterator PI = Pass2.find(*I);
+ for (const ExplodedNode *Succ : N->Succs) {
+ Pass2Ty::iterator PI = Pass2.find(Succ);
if (PI != Pass2.end()) {
const_cast<ExplodedNode *>(PI->second)->addPredecessor(NewN, *G);
continue;
}
// Enqueue nodes to the worklist that were marked during pass 1.
- if (Pass1.count(*I))
- WL2.push_back(*I);
+ if (Pass1.count(Succ))
+ WL2.push_back(Succ);
}
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 66332d3b848c..24e91a22fd68 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
//
// This file defines a meta-engine for path-sensitive dataflow analysis that
-// is built on GREngine, but provides the boilerplate to execute transfer
+// is built on CoreEngine, but provides the boilerplate to execute transfer
// functions and build the ExplodedGraph at the expression level.
//
//===----------------------------------------------------------------------===//
@@ -48,6 +48,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h"
@@ -64,7 +65,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/ImmutableSet.h"
-#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Casting.h"
@@ -77,6 +78,7 @@
#include <cassert>
#include <cstdint>
#include <memory>
+#include <optional>
#include <string>
#include <tuple>
#include <utility>
@@ -118,18 +120,10 @@ namespace {
/// the construction context was present and contained references to these
/// AST nodes.
class ConstructedObjectKey {
- typedef std::pair<ConstructionContextItem, const LocationContext *>
- ConstructedObjectKeyImpl;
-
+ using ConstructedObjectKeyImpl =
+ std::pair<ConstructionContextItem, const LocationContext *>;
const ConstructedObjectKeyImpl Impl;
- const void *getAnyASTNodePtr() const {
- if (const Stmt *S = getItem().getStmtOrNull())
- return S;
- else
- return getItem().getCXXCtorInitializer();
- }
-
public:
explicit ConstructedObjectKey(const ConstructionContextItem &Item,
const LocationContext *LC)
@@ -193,6 +187,31 @@ typedef llvm::ImmutableMap<ConstructedObjectKey, SVal>
REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction,
ObjectsUnderConstructionMap)
+// This trait is responsible for storing the index of the element that is to be
+// constructed in the next iteration. As a result a CXXConstructExpr is only
+// stored if it is array type. Also the index is the index of the continuous
+// memory region, which is important for multi-dimensional arrays. E.g:: int
+// arr[2][2]; assume arr[1][1] will be the next element under construction, so
+// the index is 3.
+typedef llvm::ImmutableMap<
+ std::pair<const CXXConstructExpr *, const LocationContext *>, unsigned>
+ IndexOfElementToConstructMap;
+REGISTER_TRAIT_WITH_PROGRAMSTATE(IndexOfElementToConstruct,
+ IndexOfElementToConstructMap)
+
+// This trait is responsible for holding our pending ArrayInitLoopExprs.
+// It pairs the LocationContext and the initializer CXXConstructExpr with
+// the size of the array that's being copy initialized.
+typedef llvm::ImmutableMap<
+ std::pair<const CXXConstructExpr *, const LocationContext *>, unsigned>
+ PendingInitLoopMap;
+REGISTER_TRAIT_WITH_PROGRAMSTATE(PendingInitLoop, PendingInitLoopMap)
+
+typedef llvm::ImmutableMap<const LocationContext *, unsigned>
+ PendingArrayDestructionMap;
+REGISTER_TRAIT_WITH_PROGRAMSTATE(PendingArrayDestruction,
+ PendingArrayDestructionMap)
+
//===----------------------------------------------------------------------===//
// Engine construction and deletion.
//===----------------------------------------------------------------------===//
@@ -200,24 +219,17 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction,
static const char* TagProviderName = "ExprEngine";
ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU,
- AnalysisManager &mgr,
- SetOfConstDecls *VisitedCalleesIn,
- FunctionSummariesTy *FS,
- InliningModes HowToInlineIn)
- : CTU(CTU), AMgr(mgr),
- AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()),
+ AnalysisManager &mgr, SetOfConstDecls *VisitedCalleesIn,
+ FunctionSummariesTy *FS, InliningModes HowToInlineIn)
+ : CTU(CTU), IsCTUEnabled(mgr.getAnalyzerOptions().IsNaiveCTUEnabled),
+ AMgr(mgr), AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()),
Engine(*this, FS, mgr.getAnalyzerOptions()), G(Engine.getGraph()),
StateMgr(getContext(), mgr.getStoreManagerCreator(),
- mgr.getConstraintManagerCreator(), G.getAllocator(),
- this),
- SymMgr(StateMgr.getSymbolManager()),
- MRMgr(StateMgr.getRegionManager()),
- svalBuilder(StateMgr.getSValBuilder()),
- ObjCNoRet(mgr.getASTContext()),
- BR(mgr, *this),
- VisitedCallees(VisitedCalleesIn),
- HowToInline(HowToInlineIn)
- {
+ mgr.getConstraintManagerCreator(), G.getAllocator(), this),
+ SymMgr(StateMgr.getSymbolManager()), MRMgr(StateMgr.getRegionManager()),
+ svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()),
+ BR(mgr, *this), VisitedCallees(VisitedCalleesIn),
+ HowToInline(HowToInlineIn) {
unsigned TrimInterval = mgr.options.GraphTrimInterval;
if (TrimInterval != 0) {
// Enable eager node reclamation when constructing the ExplodedGraph.
@@ -259,7 +271,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
svalBuilder.makeZeroVal(T),
svalBuilder.getConditionType());
- Optional<DefinedOrUnknownSVal> Constraint =
+ std::optional<DefinedOrUnknownSVal> Constraint =
Constraint_untested.getAs<DefinedOrUnknownSVal>();
if (!Constraint)
@@ -279,7 +291,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
const MemRegion *R = state->getRegion(SelfD, InitLoc);
SVal V = state->getSVal(loc::MemRegionVal(R));
- if (Optional<Loc> LV = V.getAs<Loc>()) {
+ if (std::optional<Loc> LV = V.getAs<Loc>()) {
// Assume that the pointer value in 'self' is non-null.
state = state->assume(*LV, true);
assert(state && "'self' cannot be null");
@@ -287,7 +299,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
}
if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
- if (!MD->isStatic()) {
+ if (MD->isImplicitObjectMemberFunction()) {
// Precondition: 'this' is always non-null upon entry to the
// top-level function. This is our starting assumption for
// analyzing an "open" program.
@@ -295,7 +307,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
if (SFC->getParent() == nullptr) {
loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC);
SVal V = state->getSVal(L);
- if (Optional<Loc> LV = V.getAs<Loc>()) {
+ if (std::optional<Loc> LV = V.getAs<Loc>()) {
state = state->assume(*LV, true);
assert(state && "'this' cannot be null");
}
@@ -319,16 +331,16 @@ ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
if (!Result) {
// If we don't have an explicit result expression, we're in "if needed"
// mode. Only create a region if the current value is a NonLoc.
- if (!InitValWithAdjustments.getAs<NonLoc>()) {
+ if (!isa<NonLoc>(InitValWithAdjustments)) {
if (OutRegionWithAdjustments)
*OutRegionWithAdjustments = nullptr;
return State;
}
Result = InitWithAdjustments;
} else {
- // We need to create a region no matter what. For sanity, make sure we don't
- // try to stuff a Loc into a non-pointer temporary region.
- assert(!InitValWithAdjustments.getAs<Loc>() ||
+ // We need to create a region no matter what. Make sure we don't try to
+ // stuff a Loc into a non-pointer temporary region.
+ assert(!isa<Loc>(InitValWithAdjustments) ||
Loc::isLocType(Result->getType()) ||
Result->getType()->isMemberPointerType());
}
@@ -371,19 +383,23 @@ ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
// into that region. This is not correct, but it is better than nothing.
const TypedValueRegion *TR = nullptr;
if (const auto *MT = dyn_cast<MaterializeTemporaryExpr>(Result)) {
- if (Optional<SVal> V = getObjectUnderConstruction(State, MT, LC)) {
+ if (std::optional<SVal> V = getObjectUnderConstruction(State, MT, LC)) {
State = finishObjectConstruction(State, MT, LC);
State = State->BindExpr(Result, LC, *V);
return State;
- } else {
+ } else if (const ValueDecl *VD = MT->getExtendingDecl()) {
StorageDuration SD = MT->getStorageDuration();
+ assert(SD != SD_FullExpression);
// If this object is bound to a reference with static storage duration, we
// put it in a different region to prevent "address leakage" warnings.
if (SD == SD_Static || SD == SD_Thread) {
- TR = MRMgr.getCXXStaticTempObjectRegion(Init);
+ TR = MRMgr.getCXXStaticLifetimeExtendedObjectRegion(Init, VD);
} else {
- TR = MRMgr.getCXXTempObjectRegion(Init, LC);
+ TR = MRMgr.getCXXLifetimeExtendedObjectRegion(Init, VD, LC);
}
+ } else {
+ assert(MT->getStorageDuration() == SD_FullExpression);
+ TR = MRMgr.getCXXTempObjectRegion(Init, LC);
}
} else {
TR = MRMgr.getCXXTempObjectRegion(Init, LC);
@@ -393,8 +409,7 @@ ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
SVal BaseReg = Reg;
// Make the necessary adjustments to obtain the sub-object.
- for (auto I = Adjustments.rbegin(), E = Adjustments.rend(); I != E; ++I) {
- const SubobjectAdjustment &Adj = *I;
+ for (const SubobjectAdjustment &Adj : llvm::reverse(Adjustments)) {
switch (Adj.Kind) {
case SubobjectAdjustment::DerivedToBaseAdjustment:
Reg = StoreMgr.evalDerivedToBase(Reg, Adj.DerivedToBase.BasePath);
@@ -457,25 +472,140 @@ ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
return State;
}
+ProgramStateRef ExprEngine::setIndexOfElementToConstruct(
+ ProgramStateRef State, const CXXConstructExpr *E,
+ const LocationContext *LCtx, unsigned Idx) {
+ auto Key = std::make_pair(E, LCtx->getStackFrame());
+
+ assert(!State->contains<IndexOfElementToConstruct>(Key) || Idx > 0);
+
+ return State->set<IndexOfElementToConstruct>(Key, Idx);
+}
+
+std::optional<unsigned>
+ExprEngine::getPendingInitLoop(ProgramStateRef State, const CXXConstructExpr *E,
+ const LocationContext *LCtx) {
+ const unsigned *V = State->get<PendingInitLoop>({E, LCtx->getStackFrame()});
+ return V ? std::make_optional(*V) : std::nullopt;
+}
+
+ProgramStateRef ExprEngine::removePendingInitLoop(ProgramStateRef State,
+ const CXXConstructExpr *E,
+ const LocationContext *LCtx) {
+ auto Key = std::make_pair(E, LCtx->getStackFrame());
+
+ assert(E && State->contains<PendingInitLoop>(Key));
+ return State->remove<PendingInitLoop>(Key);
+}
+
+ProgramStateRef ExprEngine::setPendingInitLoop(ProgramStateRef State,
+ const CXXConstructExpr *E,
+ const LocationContext *LCtx,
+ unsigned Size) {
+ auto Key = std::make_pair(E, LCtx->getStackFrame());
+
+ assert(!State->contains<PendingInitLoop>(Key) && Size > 0);
+
+ return State->set<PendingInitLoop>(Key, Size);
+}
+
+std::optional<unsigned>
+ExprEngine::getIndexOfElementToConstruct(ProgramStateRef State,
+ const CXXConstructExpr *E,
+ const LocationContext *LCtx) {
+ const unsigned *V =
+ State->get<IndexOfElementToConstruct>({E, LCtx->getStackFrame()});
+ return V ? std::make_optional(*V) : std::nullopt;
+}
+
+ProgramStateRef
+ExprEngine::removeIndexOfElementToConstruct(ProgramStateRef State,
+ const CXXConstructExpr *E,
+ const LocationContext *LCtx) {
+ auto Key = std::make_pair(E, LCtx->getStackFrame());
+
+ assert(E && State->contains<IndexOfElementToConstruct>(Key));
+ return State->remove<IndexOfElementToConstruct>(Key);
+}
+
+std::optional<unsigned>
+ExprEngine::getPendingArrayDestruction(ProgramStateRef State,
+ const LocationContext *LCtx) {
+ assert(LCtx && "LocationContext shouldn't be null!");
+
+ const unsigned *V =
+ State->get<PendingArrayDestruction>(LCtx->getStackFrame());
+ return V ? std::make_optional(*V) : std::nullopt;
+}
+
+ProgramStateRef ExprEngine::setPendingArrayDestruction(
+ ProgramStateRef State, const LocationContext *LCtx, unsigned Idx) {
+ assert(LCtx && "LocationContext shouldn't be null!");
+
+ auto Key = LCtx->getStackFrame();
+
+ return State->set<PendingArrayDestruction>(Key, Idx);
+}
+
+ProgramStateRef
+ExprEngine::removePendingArrayDestruction(ProgramStateRef State,
+ const LocationContext *LCtx) {
+ assert(LCtx && "LocationContext shouldn't be null!");
+
+ auto Key = LCtx->getStackFrame();
+
+ assert(LCtx && State->contains<PendingArrayDestruction>(Key));
+ return State->remove<PendingArrayDestruction>(Key);
+}
+
ProgramStateRef
ExprEngine::addObjectUnderConstruction(ProgramStateRef State,
const ConstructionContextItem &Item,
const LocationContext *LC, SVal V) {
ConstructedObjectKey Key(Item, LC->getStackFrame());
+
+ const Expr *Init = nullptr;
+
+ if (auto DS = dyn_cast_or_null<DeclStmt>(Item.getStmtOrNull())) {
+ if (auto VD = dyn_cast_or_null<VarDecl>(DS->getSingleDecl()))
+ Init = VD->getInit();
+ }
+
+ if (auto LE = dyn_cast_or_null<LambdaExpr>(Item.getStmtOrNull()))
+ Init = *(LE->capture_init_begin() + Item.getIndex());
+
+ if (!Init && !Item.getStmtOrNull())
+ Init = Item.getCXXCtorInitializer()->getInit();
+
+ // In an ArrayInitLoopExpr the real initializer is returned by
+ // getSubExpr(). Note that AILEs can be nested in case of
+ // multidimesnional arrays.
+ if (const auto *AILE = dyn_cast_or_null<ArrayInitLoopExpr>(Init))
+ Init = extractElementInitializerFromNestedAILE(AILE);
+
// FIXME: Currently the state might already contain the marker due to
// incorrect handling of temporaries bound to default parameters.
- assert(!State->get<ObjectsUnderConstruction>(Key) ||
- Key.getItem().getKind() ==
- ConstructionContextItem::TemporaryDestructorKind);
+ // The state will already contain the marker if we construct elements
+ // in an array, as we visit the same statement multiple times before
+ // the array declaration. The marker is removed when we exit the
+ // constructor call.
+ assert((!State->get<ObjectsUnderConstruction>(Key) ||
+ Key.getItem().getKind() ==
+ ConstructionContextItem::TemporaryDestructorKind ||
+ State->contains<IndexOfElementToConstruct>(
+ {dyn_cast_or_null<CXXConstructExpr>(Init), LC})) &&
+ "The object is already marked as `UnderConstruction`, when it's not "
+ "supposed to!");
return State->set<ObjectsUnderConstruction>(Key, V);
}
-Optional<SVal>
+std::optional<SVal>
ExprEngine::getObjectUnderConstruction(ProgramStateRef State,
const ConstructionContextItem &Item,
const LocationContext *LC) {
ConstructedObjectKey Key(Item, LC->getStackFrame());
- return Optional<SVal>::create(State->get<ObjectsUnderConstruction>(Key));
+ const SVal *V = State->get<ObjectsUnderConstruction>(Key);
+ return V ? std::make_optional(*V) : std::nullopt;
}
ProgramStateRef
@@ -569,7 +699,7 @@ printObjectsUnderConstructionJson(raw_ostream &Out, ProgramStateRef State,
continue;
if (!HasItem) {
- Out << "[" << NL;
+ Out << '[' << NL;
HasItem = true;
}
@@ -598,29 +728,238 @@ printObjectsUnderConstructionJson(raw_ostream &Out, ProgramStateRef State,
}
}
-void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State,
- const LocationContext *LCtx, const char *NL,
- unsigned int Space, bool IsDot) const {
- Indent(Out, Space, IsDot) << "\"constructing_objects\": ";
+static void printIndicesOfElementsToConstructJson(
+ raw_ostream &Out, ProgramStateRef State, const char *NL,
+ const LocationContext *LCtx, unsigned int Space = 0, bool IsDot = false) {
+ using KeyT = std::pair<const Expr *, const LocationContext *>;
+
+ const auto &Context = LCtx->getAnalysisDeclContext()->getASTContext();
+ PrintingPolicy PP = Context.getPrintingPolicy();
+
+ ++Space;
+ bool HasItem = false;
+
+ // Store the last key.
+ KeyT LastKey;
+ for (const auto &I : State->get<IndexOfElementToConstruct>()) {
+ const KeyT &Key = I.first;
+ if (Key.second != LCtx)
+ continue;
+
+ if (!HasItem) {
+ Out << '[' << NL;
+ HasItem = true;
+ }
+
+ LastKey = Key;
+ }
+
+ for (const auto &I : State->get<IndexOfElementToConstruct>()) {
+ const KeyT &Key = I.first;
+ unsigned Value = I.second;
+ if (Key.second != LCtx)
+ continue;
+
+ Indent(Out, Space, IsDot) << "{ ";
+
+ // Expr
+ const Expr *E = Key.first;
+ Out << "\"stmt_id\": " << E->getID(Context);
+
+ // Kind
+ Out << ", \"kind\": null";
+
+ // Pretty-print
+ Out << ", \"pretty\": ";
+ Out << "\"" << E->getStmtClassName() << ' '
+ << E->getSourceRange().printToString(Context.getSourceManager()) << " '"
+ << QualType::getAsString(E->getType().split(), PP);
+ Out << "'\"";
+
+ Out << ", \"value\": \"Current index: " << Value - 1 << "\" }";
+
+ if (Key != LastKey)
+ Out << ',';
+ Out << NL;
+ }
+
+ if (HasItem)
+ Indent(Out, --Space, IsDot) << ']'; // End of "location_context".
+ else {
+ Out << "null ";
+ }
+}
+
+static void printPendingInitLoopJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL,
+ const LocationContext *LCtx,
+ unsigned int Space = 0,
+ bool IsDot = false) {
+ using KeyT = std::pair<const CXXConstructExpr *, const LocationContext *>;
+
+ const auto &Context = LCtx->getAnalysisDeclContext()->getASTContext();
+ PrintingPolicy PP = Context.getPrintingPolicy();
+
+ ++Space;
+ bool HasItem = false;
+
+ // Store the last key.
+ KeyT LastKey;
+ for (const auto &I : State->get<PendingInitLoop>()) {
+ const KeyT &Key = I.first;
+ if (Key.second != LCtx)
+ continue;
+
+ if (!HasItem) {
+ Out << '[' << NL;
+ HasItem = true;
+ }
+
+ LastKey = Key;
+ }
+
+ for (const auto &I : State->get<PendingInitLoop>()) {
+ const KeyT &Key = I.first;
+ unsigned Value = I.second;
+ if (Key.second != LCtx)
+ continue;
+
+ Indent(Out, Space, IsDot) << "{ ";
+
+ const CXXConstructExpr *E = Key.first;
+ Out << "\"stmt_id\": " << E->getID(Context);
+
+ Out << ", \"kind\": null";
+ Out << ", \"pretty\": ";
+ Out << '\"' << E->getStmtClassName() << ' '
+ << E->getSourceRange().printToString(Context.getSourceManager()) << " '"
+ << QualType::getAsString(E->getType().split(), PP);
+ Out << "'\"";
+
+ Out << ", \"value\": \"Flattened size: " << Value << "\"}";
+
+ if (Key != LastKey)
+ Out << ',';
+ Out << NL;
+ }
+
+ if (HasItem)
+ Indent(Out, --Space, IsDot) << ']'; // End of "location_context".
+ else {
+ Out << "null ";
+ }
+}
+
+static void
+printPendingArrayDestructionsJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const LocationContext *LCtx,
+ unsigned int Space = 0, bool IsDot = false) {
+ using KeyT = const LocationContext *;
+
+ ++Space;
+ bool HasItem = false;
+
+ // Store the last key.
+ KeyT LastKey = nullptr;
+ for (const auto &I : State->get<PendingArrayDestruction>()) {
+ const KeyT &Key = I.first;
+ if (Key != LCtx)
+ continue;
+
+ if (!HasItem) {
+ Out << '[' << NL;
+ HasItem = true;
+ }
+
+ LastKey = Key;
+ }
+
+ for (const auto &I : State->get<PendingArrayDestruction>()) {
+ const KeyT &Key = I.first;
+ if (Key != LCtx)
+ continue;
+
+ Indent(Out, Space, IsDot) << "{ ";
+
+ Out << "\"stmt_id\": null";
+ Out << ", \"kind\": null";
+ Out << ", \"pretty\": \"Current index: \"";
+ Out << ", \"value\": \"" << I.second << "\" }";
- if (LCtx && !State->get<ObjectsUnderConstruction>().isEmpty()) {
+ if (Key != LastKey)
+ Out << ',';
+ Out << NL;
+ }
+
+ if (HasItem)
+ Indent(Out, --Space, IsDot) << ']'; // End of "location_context".
+ else {
+ Out << "null ";
+ }
+}
+
+/// A helper function to generalize program state trait printing.
+/// The function invokes Printer as 'Printer(Out, State, NL, LC, Space, IsDot,
+/// std::forward<Args>(args)...)'. \n One possible type for Printer is
+/// 'void()(raw_ostream &, ProgramStateRef, const char *, const LocationContext
+/// *, unsigned int, bool, ...)' \n \param Trait The state trait to be printed.
+/// \param Printer A void function that prints Trait.
+/// \param Args An additional parameter pack that is passed to Print upon
+/// invocation.
+template <typename Trait, typename Printer, typename... Args>
+static void printStateTraitWithLocationContextJson(
+ raw_ostream &Out, ProgramStateRef State, const LocationContext *LCtx,
+ const char *NL, unsigned int Space, bool IsDot,
+ const char *jsonPropertyName, Printer printer, Args &&...args) {
+
+ using RequiredType =
+ void (*)(raw_ostream &, ProgramStateRef, const char *,
+ const LocationContext *, unsigned int, bool, Args &&...);
+
+ // Try to do as much compile time checking as possible.
+ // FIXME: check for invocable instead of function?
+ static_assert(std::is_function_v<std::remove_pointer_t<Printer>>,
+ "Printer is not a function!");
+ static_assert(std::is_convertible_v<Printer, RequiredType>,
+ "Printer doesn't have the required type!");
+
+ if (LCtx && !State->get<Trait>().isEmpty()) {
+ Indent(Out, Space, IsDot) << '\"' << jsonPropertyName << "\": ";
++Space;
Out << '[' << NL;
LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) {
- printObjectsUnderConstructionJson(Out, State, NL, LC, Space, IsDot);
+ printer(Out, State, NL, LC, Space, IsDot, std::forward<Args>(args)...);
});
--Space;
- Indent(Out, Space, IsDot) << "]," << NL; // End of "constructing_objects".
- } else {
- Out << "null," << NL;
+ Indent(Out, Space, IsDot) << "]," << NL; // End of "jsonPropertyName".
}
+}
+
+void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State,
+ const LocationContext *LCtx, const char *NL,
+ unsigned int Space, bool IsDot) const {
+
+ printStateTraitWithLocationContextJson<ObjectsUnderConstruction>(
+ Out, State, LCtx, NL, Space, IsDot, "constructing_objects",
+ printObjectsUnderConstructionJson);
+ printStateTraitWithLocationContextJson<IndexOfElementToConstruct>(
+ Out, State, LCtx, NL, Space, IsDot, "index_of_element",
+ printIndicesOfElementsToConstructJson);
+ printStateTraitWithLocationContextJson<PendingInitLoop>(
+ Out, State, LCtx, NL, Space, IsDot, "pending_init_loops",
+ printPendingInitLoopJson);
+ printStateTraitWithLocationContextJson<PendingArrayDestruction>(
+ Out, State, LCtx, NL, Space, IsDot, "pending_destructors",
+ printPendingArrayDestructionsJson);
getCheckerManager().runCheckersForPrintStateJson(Out, State, NL, Space,
IsDot);
}
void ExprEngine::processEndWorklist() {
+ // This prints the name of the top-level function if we crash.
+ PrettyStackTraceLocationContext CrashInfo(getRootLocationContext());
getCheckerManager().runCheckersForEndAnalysis(G, BR, *this);
}
@@ -654,6 +993,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred,
ProcessLoopExit(E.castAs<CFGLoopExit>().getLoopStmt(), Pred);
return;
case CFGElement::LifetimeEnds:
+ case CFGElement::CleanupFunction:
case CFGElement::ScopeBegin:
case CFGElement::ScopeEnd:
return;
@@ -865,7 +1205,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit,
SVal LValue = State->getSVal(Init, stackFrame);
if (!Field->getType()->isReferenceType())
- if (Optional<Loc> LValueLoc = LValue.getAs<Loc>())
+ if (std::optional<Loc> LValueLoc = LValue.getAs<Loc>())
InitVal = State->getSVal(*LValueLoc);
// If we fail to get the value for some reason, use a symbolic value.
@@ -882,6 +1222,14 @@ void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit,
PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame);
evalBind(Tmp, Init, Pred, FieldLoc, InitVal, /*isInit=*/true, &PP);
}
+ } else if (BMI->isBaseInitializer() && isa<InitListExpr>(Init)) {
+ // When the base class is initialized with an initialization list and the
+ // base class does not have a ctor, there will not be a CXXConstructExpr to
+ // initialize the base region. Hence, we need to make the bind for it.
+ SVal BaseLoc = getStoreManager().evalDerivedToBase(
+ thisVal, QualType(BMI->getBaseClass(), 0), BMI->isBaseVirtual());
+ SVal InitVal = State->getSVal(Init, stackFrame);
+ evalBind(Tmp, Init, Pred, BaseLoc, InitVal, /*isInit=*/true);
} else {
assert(BMI->isBaseInitializer() || BMI->isDelegatingInitializer());
Tmp.insert(Pred);
@@ -902,6 +1250,43 @@ void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit,
Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx);
}
+std::pair<ProgramStateRef, uint64_t>
+ExprEngine::prepareStateForArrayDestruction(const ProgramStateRef State,
+ const MemRegion *Region,
+ const QualType &ElementTy,
+ const LocationContext *LCtx,
+ SVal *ElementCountVal) {
+ assert(Region != nullptr && "Not-null region expected");
+
+ QualType Ty = ElementTy.getDesugaredType(getContext());
+ while (const auto *NTy = dyn_cast<ArrayType>(Ty))
+ Ty = NTy->getElementType().getDesugaredType(getContext());
+
+ auto ElementCount = getDynamicElementCount(State, Region, svalBuilder, Ty);
+
+ if (ElementCountVal)
+ *ElementCountVal = ElementCount;
+
+ // Note: the destructors are called in reverse order.
+ unsigned Idx = 0;
+ if (auto OptionalIdx = getPendingArrayDestruction(State, LCtx)) {
+ Idx = *OptionalIdx;
+ } else {
+ // The element count is either unknown, or an SVal that's not an integer.
+ if (!ElementCount.isConstant())
+ return {State, 0};
+
+ Idx = ElementCount.getAsInteger()->getLimitedValue();
+ }
+
+ if (Idx == 0)
+ return {State, 0};
+
+ --Idx;
+
+ return {setPendingArrayDestruction(State, LCtx, Idx), Idx};
+}
+
void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D,
ExplodedNode *Pred) {
ExplodedNodeSet Dst;
@@ -942,7 +1327,8 @@ void ExprEngine::ProcessNewAllocator(const CXXNewExpr *NE,
else {
NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
const LocationContext *LCtx = Pred->getLocationContext();
- PostImplicitCall PP(NE->getOperatorNew(), NE->getBeginLoc(), LCtx);
+ PostImplicitCall PP(NE->getOperatorNew(), NE->getBeginLoc(), LCtx,
+ getCFGElementRef());
Bldr.generateNode(PP, Pred->getState(), Pred);
}
Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx);
@@ -951,11 +1337,14 @@ void ExprEngine::ProcessNewAllocator(const CXXNewExpr *NE,
void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ const auto *DtorDecl = Dtor.getDestructorDecl(getContext());
const VarDecl *varDecl = Dtor.getVarDecl();
QualType varType = varDecl->getType();
ProgramStateRef state = Pred->getState();
- SVal dest = state->getLValue(varDecl, Pred->getLocationContext());
+ const LocationContext *LCtx = Pred->getLocationContext();
+
+ SVal dest = state->getLValue(varDecl, LCtx);
const MemRegion *Region = dest.castAs<loc::MemRegionVal>().getRegion();
if (varType->isReferenceType()) {
@@ -971,12 +1360,47 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
varType = cast<TypedValueRegion>(Region)->getValueType();
}
- // FIXME: We need to run the same destructor on every element of the array.
- // This workaround will just run the first destructor (which will still
- // invalidate the entire array).
+ unsigned Idx = 0;
+ if (isa<ArrayType>(varType)) {
+ SVal ElementCount;
+ std::tie(state, Idx) = prepareStateForArrayDestruction(
+ state, Region, varType, LCtx, &ElementCount);
+
+ if (ElementCount.isConstant()) {
+ uint64_t ArrayLength = ElementCount.getAsInteger()->getLimitedValue();
+ assert(ArrayLength &&
+ "An automatic dtor for a 0 length array shouldn't be triggered!");
+
+ // Still handle this case if we don't have assertions enabled.
+ if (!ArrayLength) {
+ static SimpleProgramPointTag PT(
+ "ExprEngine", "Skipping automatic 0 length array destruction, "
+ "which shouldn't be in the CFG.");
+ PostImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx,
+ getCFGElementRef(), &PT);
+ NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ Bldr.generateSink(PP, Pred->getState(), Pred);
+ return;
+ }
+ }
+ }
+
EvalCallOptions CallOpts;
- Region = makeZeroElementRegion(state, loc::MemRegionVal(Region), varType,
- CallOpts.IsArrayCtorOrDtor).getAsRegion();
+ Region = makeElementRegion(state, loc::MemRegionVal(Region), varType,
+ CallOpts.IsArrayCtorOrDtor, Idx)
+ .getAsRegion();
+
+ NodeBuilder Bldr(Pred, Dst, getBuilderContext());
+
+ static SimpleProgramPointTag PT("ExprEngine",
+ "Prepare for object destruction");
+ PreImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, getCFGElementRef(),
+ &PT);
+ Pred = Bldr.generateNode(PP, state, Pred);
+
+ if (!Pred)
+ return;
+ Bldr.takeNodes(Pred);
VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(),
/*IsBase=*/false, Pred, Dst, CallOpts);
@@ -999,26 +1423,62 @@ void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor,
const CXXRecordDecl *RD = BTy->getAsCXXRecordDecl();
const CXXDestructorDecl *Dtor = RD->getDestructor();
- PostImplicitCall PP(Dtor, DE->getBeginLoc(), LCtx);
+ PostImplicitCall PP(Dtor, DE->getBeginLoc(), LCtx, getCFGElementRef());
NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
Bldr.generateNode(PP, Pred->getState(), Pred);
return;
}
+ auto getDtorDecl = [](const QualType &DTy) {
+ const CXXRecordDecl *RD = DTy->getAsCXXRecordDecl();
+ return RD->getDestructor();
+ };
+
+ unsigned Idx = 0;
EvalCallOptions CallOpts;
const MemRegion *ArgR = ArgVal.getAsRegion();
+
if (DE->isArrayForm()) {
- // FIXME: We need to run the same destructor on every element of the array.
- // This workaround will just run the first destructor (which will still
- // invalidate the entire array).
CallOpts.IsArrayCtorOrDtor = true;
// Yes, it may even be a multi-dimensional array.
while (const auto *AT = getContext().getAsArrayType(DTy))
DTy = AT->getElementType();
- if (ArgR)
- ArgR = getStoreManager().GetElementZeroRegion(cast<SubRegion>(ArgR), DTy);
+
+ if (ArgR) {
+ SVal ElementCount;
+ std::tie(State, Idx) = prepareStateForArrayDestruction(
+ State, ArgR, DTy, LCtx, &ElementCount);
+
+ // If we're about to destruct a 0 length array, don't run any of the
+ // destructors.
+ if (ElementCount.isConstant() &&
+ ElementCount.getAsInteger()->getLimitedValue() == 0) {
+
+ static SimpleProgramPointTag PT(
+ "ExprEngine", "Skipping 0 length array delete destruction");
+ PostImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx,
+ getCFGElementRef(), &PT);
+ NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ Bldr.generateNode(PP, Pred->getState(), Pred);
+ return;
+ }
+
+ ArgR = State->getLValue(DTy, svalBuilder.makeArrayIndex(Idx), ArgVal)
+ .getAsRegion();
+ }
}
+ NodeBuilder Bldr(Pred, Dst, getBuilderContext());
+ static SimpleProgramPointTag PT("ExprEngine",
+ "Prepare for object destruction");
+ PreImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx,
+ getCFGElementRef(), &PT);
+ Pred = Bldr.generateNode(PP, State, Pred);
+
+ if (!Pred)
+ return;
+ Bldr.takeNodes(Pred);
+
VisitCXXDestructor(DTy, ArgR, DE, /*IsBase=*/false, Pred, Dst, CallOpts);
}
@@ -1044,6 +1504,7 @@ void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D,
void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
+ const auto *DtorDecl = D.getDestructorDecl(getContext());
const FieldDecl *Member = D.getFieldDecl();
QualType T = Member->getType();
ProgramStateRef State = Pred->getState();
@@ -1055,12 +1516,46 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
Loc ThisLoc = State->getSVal(ThisStorageLoc).castAs<Loc>();
SVal FieldVal = State->getLValue(Member, ThisLoc);
- // FIXME: We need to run the same destructor on every element of the array.
- // This workaround will just run the first destructor (which will still
- // invalidate the entire array).
+ unsigned Idx = 0;
+ if (isa<ArrayType>(T)) {
+ SVal ElementCount;
+ std::tie(State, Idx) = prepareStateForArrayDestruction(
+ State, FieldVal.getAsRegion(), T, LCtx, &ElementCount);
+
+ if (ElementCount.isConstant()) {
+ uint64_t ArrayLength = ElementCount.getAsInteger()->getLimitedValue();
+ assert(ArrayLength &&
+ "A member dtor for a 0 length array shouldn't be triggered!");
+
+ // Still handle this case if we don't have assertions enabled.
+ if (!ArrayLength) {
+ static SimpleProgramPointTag PT(
+ "ExprEngine", "Skipping member 0 length array destruction, which "
+ "shouldn't be in the CFG.");
+ PostImplicitCall PP(DtorDecl, Member->getLocation(), LCtx,
+ getCFGElementRef(), &PT);
+ NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ Bldr.generateSink(PP, Pred->getState(), Pred);
+ return;
+ }
+ }
+ }
+
EvalCallOptions CallOpts;
- FieldVal = makeZeroElementRegion(State, FieldVal, T,
- CallOpts.IsArrayCtorOrDtor);
+ FieldVal =
+ makeElementRegion(State, FieldVal, T, CallOpts.IsArrayCtorOrDtor, Idx);
+
+ NodeBuilder Bldr(Pred, Dst, getBuilderContext());
+
+ static SimpleProgramPointTag PT("ExprEngine",
+ "Prepare for object destruction");
+ PreImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, getCFGElementRef(),
+ &PT);
+ Pred = Bldr.generateNode(PP, State, Pred);
+
+ if (!Pred)
+ return;
+ Bldr.takeNodes(Pred);
VisitCXXDestructor(T, FieldVal.getAsRegion(), CurDtor->getBody(),
/*IsBase=*/false, Pred, Dst, CallOpts);
@@ -1074,9 +1569,8 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
const LocationContext *LC = Pred->getLocationContext();
const MemRegion *MR = nullptr;
- if (Optional<SVal> V =
- getObjectUnderConstruction(State, D.getBindTemporaryExpr(),
- Pred->getLocationContext())) {
+ if (std::optional<SVal> V = getObjectUnderConstruction(
+ State, D.getBindTemporaryExpr(), Pred->getLocationContext())) {
// FIXME: Currently we insert temporary destructors for default parameters,
// but we don't insert the constructors, so the entry in
// ObjectsUnderConstruction may be missing.
@@ -1092,7 +1586,7 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
PostImplicitCall PP(D.getDestructorDecl(getContext()),
D.getBindTemporaryExpr()->getBeginLoc(),
- Pred->getLocationContext());
+ Pred->getLocationContext(), getCFGElementRef());
Bldr.generateNode(PP, State, Pred);
return;
}
@@ -1111,15 +1605,31 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
EvalCallOptions CallOpts;
CallOpts.IsTemporaryCtorOrDtor = true;
if (!MR) {
- // If we have no MR, we still need to unwrap the array to avoid destroying
- // the whole array at once. Regardless, we'd eventually need to model array
- // destructors properly, element-by-element.
+ // FIXME: If we have no MR, we still need to unwrap the array to avoid
+ // destroying the whole array at once.
+ //
+ // For this case there is no universal solution as there is no way to
+ // directly create an array of temporary objects. There are some expressions
+ // however which can create temporary objects and have an array type.
+ //
+ // E.g.: std::initializer_list<S>{S(), S()};
+ //
+ // The expression above has a type of 'const struct S[2]' but it's a single
+ // 'std::initializer_list<>'. The destructors of the 2 temporary 'S()'
+ // objects will be called anyway, because they are 2 separate objects in 2
+ // separate clusters, i.e.: not an array.
+ //
+ // Now the 'std::initializer_list<>' is not an array either even though it
+ // has the type of an array. The point is, we only want to invoke the
+ // destructor for the initializer list once not twice or so.
while (const ArrayType *AT = getContext().getAsArrayType(T)) {
T = AT->getElementType();
- CallOpts.IsArrayCtorOrDtor = true;
+
+ // FIXME: Enable this flag once we handle this case properly.
+ // CallOpts.IsArrayCtorOrDtor = true;
}
} else {
- // We'd eventually need to makeZeroElementRegion() trick here,
+ // FIXME: We'd eventually need to makeElementRegion() trick here,
// but for now we don't have the respective construction contexts,
// so MR would always be null in this case. Do nothing for now.
}
@@ -1245,6 +1755,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPForSimdDirectiveClass:
case Stmt::OMPSectionsDirectiveClass:
case Stmt::OMPSectionDirectiveClass:
+ case Stmt::OMPScopeDirectiveClass:
case Stmt::OMPSingleDirectiveClass:
case Stmt::OMPMasterDirectiveClass:
case Stmt::OMPCriticalDirectiveClass:
@@ -1252,10 +1763,12 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPParallelForSimdDirectiveClass:
case Stmt::OMPParallelSectionsDirectiveClass:
case Stmt::OMPParallelMasterDirectiveClass:
+ case Stmt::OMPParallelMaskedDirectiveClass:
case Stmt::OMPTaskDirectiveClass:
case Stmt::OMPTaskyieldDirectiveClass:
case Stmt::OMPBarrierDirectiveClass:
case Stmt::OMPTaskwaitDirectiveClass:
+ case Stmt::OMPErrorDirectiveClass:
case Stmt::OMPTaskgroupDirectiveClass:
case Stmt::OMPFlushDirectiveClass:
case Stmt::OMPDepobjDirectiveClass:
@@ -1275,9 +1788,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPTaskLoopDirectiveClass:
case Stmt::OMPTaskLoopSimdDirectiveClass:
case Stmt::OMPMasterTaskLoopDirectiveClass:
+ case Stmt::OMPMaskedTaskLoopDirectiveClass:
case Stmt::OMPMasterTaskLoopSimdDirectiveClass:
+ case Stmt::OMPMaskedTaskLoopSimdDirectiveClass:
case Stmt::OMPParallelMasterTaskLoopDirectiveClass:
+ case Stmt::OMPParallelMaskedTaskLoopDirectiveClass:
case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass:
+ case Stmt::OMPParallelMaskedTaskLoopSimdDirectiveClass:
case Stmt::OMPDistributeDirectiveClass:
case Stmt::OMPDistributeParallelForDirectiveClass:
case Stmt::OMPDistributeParallelForSimdDirectiveClass:
@@ -1297,8 +1814,14 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPInteropDirectiveClass:
case Stmt::OMPDispatchDirectiveClass:
case Stmt::OMPMaskedDirectiveClass:
+ case Stmt::OMPGenericLoopDirectiveClass:
+ case Stmt::OMPTeamsGenericLoopDirectiveClass:
+ case Stmt::OMPTargetTeamsGenericLoopDirectiveClass:
+ case Stmt::OMPParallelGenericLoopDirectiveClass:
+ case Stmt::OMPTargetParallelGenericLoopDirectiveClass:
case Stmt::CapturedStmtClass:
- case Stmt::OMPUnrollDirectiveClass: {
+ case Stmt::OMPUnrollDirectiveClass:
+ case Stmt::OMPMetaDirectiveClass: {
const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState());
Engine.addAbortedBlock(node, currBldrCtx->getBlock());
break;
@@ -1341,8 +1864,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::GNUNullExprClass: {
// GNU __null is a pointer-width integer, not an actual pointer.
ProgramStateRef state = Pred->getState();
- state = state->BindExpr(S, Pred->getLocationContext(),
- svalBuilder.makeIntValWithPtrWidth(0, false));
+ state = state->BindExpr(
+ S, Pred->getLocationContext(),
+ svalBuilder.makeIntValWithWidth(getContext().VoidPtrTy, 0));
Bldr.generateNode(S, Pred, state);
break;
}
@@ -1369,10 +1893,14 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
break;
}
+ case Stmt::ArrayInitLoopExprClass:
+ Bldr.takeNodes(Pred);
+ VisitArrayInitLoopExpr(cast<ArrayInitLoopExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
// Cases not handled yet; but will handle some day.
case Stmt::DesignatedInitExprClass:
case Stmt::DesignatedInitUpdateExprClass:
- case Stmt::ArrayInitLoopExprClass:
case Stmt::ArrayInitIndexExprClass:
case Stmt::ExtVectorElementExprClass:
case Stmt::ImaginaryLiteralClass:
@@ -1394,6 +1922,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::ConceptSpecializationExprClass:
case Stmt::CXXRewrittenBinaryOperatorClass:
case Stmt::RequiresExprClass:
+ case Expr::CXXParenListInitExprClass:
// Fall through.
// Cases we intentionally don't evaluate, since they don't need
@@ -1453,7 +1982,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
IsTemporary = true;
}
- Optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE);
+ std::optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE);
if (!ConstantVal)
ConstantVal = UnknownVal();
@@ -1593,7 +2122,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
// valid region.
const Decl *Callee = OCE->getCalleeDecl();
if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Callee)) {
- if (MD->isInstance()) {
+ if (MD->isImplicitObjectMemberFunction()) {
ProgramStateRef State = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
ProgramStateRef NewState =
@@ -1607,8 +2136,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
}
}
}
- // FALLTHROUGH
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
}
case Stmt::CallExprClass:
@@ -1919,7 +2447,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
continue;
if (L.getAs<CallEnter>())
continue;
- if (Optional<StmtPoint> SP = L.getAs<StmtPoint>())
+ if (std::optional<StmtPoint> SP = L.getAs<StmtPoint>())
if (SP->getStmt() == CE)
continue;
break;
@@ -1932,8 +2460,9 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
// Build an Epsilon node from which we will restart the analyzes.
// Note that CE is permitted to be NULL!
- ProgramPoint NewNodeLoc =
- EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE);
+ static SimpleProgramPointTag PT("ExprEngine", "Replay without inlining");
+ ProgramPoint NewNodeLoc = EpsilonPoint(
+ BeforeProcessingCall->getLocationContext(), CE, nullptr, &PT);
// Add the special flag to GDM to signal retrying with no inlining.
// Note, changing the state ensures that we are not going to cache out.
ProgramStateRef NewNodeState = BeforeProcessingCall->getState();
@@ -1988,8 +2517,7 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
if (BlockCount == AMgr.options.maxBlockVisitOnPath - 1 &&
AMgr.options.ShouldWidenLoops) {
const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminatorStmt();
- if (!(Term &&
- (isa<ForStmt>(Term) || isa<WhileStmt>(Term) || isa<DoStmt>(Term))))
+ if (!isa_and_nonnull<ForStmt, WhileStmt, DoStmt, CXXForRangeStmt>(Term))
return;
// Widen.
const LocationContext *LCtx = Pred->getLocationContext();
@@ -2123,10 +2651,8 @@ static const Stmt *ResolveCondition(const Stmt *Condition,
// The invariants are still shifting, but it is possible that the
// last element in a CFGBlock is not a CFGStmt. Look for the last
// CFGStmt as the value of the condition.
- CFGBlock::const_reverse_iterator I = B->rbegin(), E = B->rend();
- for (; I != E; ++I) {
- CFGElement Elem = *I;
- Optional<CFGStmt> CS = Elem.getAs<CFGStmt>();
+ for (CFGElement Elem : llvm::reverse(*B)) {
+ std::optional<CFGStmt> CS = Elem.getAs<CFGStmt>();
if (!CS)
continue;
const Stmt *LastStmt = CS->getStmt();
@@ -2164,9 +2690,9 @@ bool ExprEngine::hasMoreIteration(ProgramStateRef State,
}
/// Split the state on whether there are any more iterations left for this loop.
-/// Returns a (HasMoreIteration, HasNoMoreIteration) pair, or None when the
-/// acquisition of the loop condition value failed.
-static Optional<std::pair<ProgramStateRef, ProgramStateRef>>
+/// Returns a (HasMoreIteration, HasNoMoreIteration) pair, or std::nullopt when
+/// the acquisition of the loop condition value failed.
+static std::optional<std::pair<ProgramStateRef, ProgramStateRef>>
assumeCondition(const Stmt *Condition, ExplodedNode *N) {
ProgramStateRef State = N->getState();
if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(Condition)) {
@@ -2205,7 +2731,7 @@ assumeCondition(const Stmt *Condition, ExplodedNode *N) {
// If the condition is still unknown, give up.
if (X.isUnknownOrUndef())
- return None;
+ return std::nullopt;
DefinedSVal V = X.castAs<DefinedSVal>();
@@ -2265,7 +2791,7 @@ void ExprEngine::processBranch(const Stmt *Condition,
continue;
}
if (StTrue && StFalse)
- assert(!isa<ObjCForCollectionStmt>(Condition));;
+ assert(!isa<ObjCForCollectionStmt>(Condition));
// Process the true branch.
if (builder.isFeasible(true)) {
@@ -2330,12 +2856,12 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
using iterator = IndirectGotoNodeBuilder::iterator;
- if (Optional<loc::GotoLabel> LV = V.getAs<loc::GotoLabel>()) {
+ if (std::optional<loc::GotoLabel> LV = V.getAs<loc::GotoLabel>()) {
const LabelDecl *L = LV->getLabel();
- for (iterator I = builder.begin(), E = builder.end(); I != E; ++I) {
- if (I.getLabel() == L) {
- builder.generateNode(I, state);
+ for (iterator Succ : builder) {
+ if (Succ.getLabel() == L) {
+ builder.generateNode(Succ, state);
return;
}
}
@@ -2343,7 +2869,7 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
llvm_unreachable("No block with label.");
}
- if (V.getAs<loc::ConcreteInt>() || V.getAs<UndefinedVal>()) {
+ if (isa<UndefinedVal, loc::ConcreteInt>(V)) {
// Dispatch to the first target and mark it as a sink.
//ExplodedNode* N = builder.generateNode(builder.begin(), state, true);
// FIXME: add checker visit.
@@ -2354,8 +2880,8 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
// This is really a catch-all. We don't support symbolics yet.
// FIXME: Implement dispatch for symbolic pointers.
- for (iterator I = builder.begin(), E = builder.end(); I != E; ++I)
- builder.generateNode(I, state);
+ for (iterator Succ : builder)
+ builder.generateNode(Succ, state);
}
void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC,
@@ -2482,7 +3008,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
V2 = V1;
ProgramStateRef StateCase;
- if (Optional<NonLoc> NL = CondV.getAs<NonLoc>())
+ if (std::optional<NonLoc> NL = CondV.getAs<NonLoc>())
std::tie(StateCase, DefaultSt) =
DefaultSt->assumeInclusiveRange(*NL, V1, V2);
else // UnknownVal
@@ -2541,14 +3067,14 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
const Decl *D = LocCtxt->getDecl();
const auto *MD = dyn_cast_or_null<CXXMethodDecl>(D);
const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex);
- Optional<std::pair<SVal, QualType>> VInfo;
+ std::optional<std::pair<SVal, QualType>> VInfo;
if (AMgr.options.ShouldInlineLambdas && DeclRefEx &&
DeclRefEx->refersToEnclosingVariableOrCapture() && MD &&
MD->getParent()->isLambda()) {
// Lookup the field of the lambda.
const CXXRecordDecl *CXXRec = MD->getParent();
- llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
FieldDecl *LambdaThisCaptureField;
CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField);
@@ -2593,20 +3119,175 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
ProgramPoint::PostLValueKind);
return;
}
- if (isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D)) {
+ if (isa<FieldDecl, IndirectFieldDecl>(D)) {
// Delegate all work related to pointer to members to the surrounding
// operator&.
return;
}
- if (isa<BindingDecl>(D)) {
- // FIXME: proper support for bound declarations.
- // For now, let's just prevent crashing.
+ if (const auto *BD = dyn_cast<BindingDecl>(D)) {
+ const auto *DD = cast<DecompositionDecl>(BD->getDecomposedDecl());
+
+ SVal Base = state->getLValue(DD, LCtx);
+ if (DD->getType()->isReferenceType()) {
+ if (const MemRegion *R = Base.getAsRegion())
+ Base = state->getSVal(R);
+ else
+ Base = UnknownVal();
+ }
+
+ SVal V = UnknownVal();
+
+ // Handle binding to data members
+ if (const auto *ME = dyn_cast<MemberExpr>(BD->getBinding())) {
+ const auto *Field = cast<FieldDecl>(ME->getMemberDecl());
+ V = state->getLValue(Field, Base);
+ }
+ // Handle binding to arrays
+ else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(BD->getBinding())) {
+ SVal Idx = state->getSVal(ASE->getIdx(), LCtx);
+
+ // Note: the index of an element in a structured binding is automatically
+ // created and it is a unique identifier of the specific element. Thus it
+ // cannot be a value that varies at runtime.
+ assert(Idx.isConstant() && "BindingDecl array index is not a constant!");
+
+ V = state->getLValue(BD->getType(), Idx, Base);
+ }
+ // Handle binding to tuple-like structures
+ else if (const auto *HV = BD->getHoldingVar()) {
+ V = state->getLValue(HV, LCtx);
+
+ if (HV->getType()->isReferenceType()) {
+ if (const MemRegion *R = V.getAsRegion())
+ V = state->getSVal(R);
+ else
+ V = UnknownVal();
+ }
+ } else
+ llvm_unreachable("An unknown case of structured binding encountered!");
+
+ // In case of tuple-like types the references are already handled, so we
+ // don't want to handle them again.
+ if (BD->getType()->isReferenceType() && !BD->getHoldingVar()) {
+ if (const MemRegion *R = V.getAsRegion())
+ V = state->getSVal(R);
+ else
+ V = UnknownVal();
+ }
+
+ Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
+ ProgramPoint::PostLValueKind);
+
+ return;
+ }
+
+ if (const auto *TPO = dyn_cast<TemplateParamObjectDecl>(D)) {
+ // FIXME: We should meaningfully implement this.
+ (void)TPO;
return;
}
llvm_unreachable("Support for this Decl not implemented.");
}
+/// VisitArrayInitLoopExpr - Transfer function for array init loop.
+void ExprEngine::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *Ex,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ ExplodedNodeSet CheckerPreStmt;
+ getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, Ex, *this);
+
+ ExplodedNodeSet EvalSet;
+ StmtNodeBuilder Bldr(CheckerPreStmt, EvalSet, *currBldrCtx);
+
+ const Expr *Arr = Ex->getCommonExpr()->getSourceExpr();
+
+ for (auto *Node : CheckerPreStmt) {
+
+ // The constructor visitior has already taken care of everything.
+ if (isa<CXXConstructExpr>(Ex->getSubExpr()))
+ break;
+
+ const LocationContext *LCtx = Node->getLocationContext();
+ ProgramStateRef state = Node->getState();
+
+ SVal Base = UnknownVal();
+
+ // As in case of this expression the sub-expressions are not visited by any
+ // other transfer functions, they are handled by matching their AST.
+
+ // Case of implicit copy or move ctor of object with array member
+ //
+ // Note: ExprEngine::VisitMemberExpr is not able to bind the array to the
+ // environment.
+ //
+ // struct S {
+ // int arr[2];
+ // };
+ //
+ //
+ // S a;
+ // S b = a;
+ //
+ // The AST in case of a *copy constructor* looks like this:
+ // ArrayInitLoopExpr
+ // |-OpaqueValueExpr
+ // | `-MemberExpr <-- match this
+ // | `-DeclRefExpr
+ // ` ...
+ //
+ //
+ // S c;
+ // S d = std::move(d);
+ //
+ // In case of a *move constructor* the resulting AST looks like:
+ // ArrayInitLoopExpr
+ // |-OpaqueValueExpr
+ // | `-MemberExpr <-- match this first
+ // | `-CXXStaticCastExpr <-- match this after
+ // | `-DeclRefExpr
+ // ` ...
+ if (const auto *ME = dyn_cast<MemberExpr>(Arr)) {
+ Expr *MEBase = ME->getBase();
+
+ // Move ctor
+ if (auto CXXSCE = dyn_cast<CXXStaticCastExpr>(MEBase)) {
+ MEBase = CXXSCE->getSubExpr();
+ }
+
+ auto ObjDeclExpr = cast<DeclRefExpr>(MEBase);
+ SVal Obj = state->getLValue(cast<VarDecl>(ObjDeclExpr->getDecl()), LCtx);
+
+ Base = state->getLValue(cast<FieldDecl>(ME->getMemberDecl()), Obj);
+ }
+
+ // Case of lambda capture and decomposition declaration
+ //
+ // int arr[2];
+ //
+ // [arr]{ int a = arr[0]; }();
+ // auto[a, b] = arr;
+ //
+ // In both of these cases the AST looks like the following:
+ // ArrayInitLoopExpr
+ // |-OpaqueValueExpr
+ // | `-DeclRefExpr <-- match this
+ // ` ...
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Arr))
+ Base = state->getLValue(cast<VarDecl>(DRE->getDecl()), LCtx);
+
+ // Create a lazy compound value to the original array
+ if (const MemRegion *R = Base.getAsRegion())
+ Base = state->getSVal(R);
+ else
+ Base = UnknownVal();
+
+ Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, Base));
+ }
+
+ getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this);
+}
+
/// VisitArraySubscriptExpr - Transfer function for array accesses
void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A,
ExplodedNode *Pred,
@@ -2670,7 +3351,7 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
// Handle static member variables and enum constants accessed via
// member syntax.
- if (isa<VarDecl>(Member) || isa<EnumConstantDecl>(Member)) {
+ if (isa<VarDecl, EnumConstantDecl>(Member)) {
for (const auto I : CheckedSet)
VisitCommonDeclRefExpr(M, Member, I, EvalSet);
} else {
@@ -2684,7 +3365,7 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
// Handle C++ method calls.
if (const auto *MD = dyn_cast<CXXMethodDecl>(Member)) {
- if (MD->isInstance())
+ if (MD->isImplicitObjectMemberFunction())
state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr);
SVal MDVal = svalBuilder.getFunctionPointer(MD);
@@ -2702,6 +3383,14 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
SVal baseExprVal =
MR ? loc::MemRegionVal(MR) : state->getSVal(BaseExpr, LCtx);
+ // FIXME: Copied from RegionStoreManager::bind()
+ if (const auto *SR =
+ dyn_cast_or_null<SymbolicRegion>(baseExprVal.getAsRegion())) {
+ QualType T = SR->getPointeeStaticType();
+ baseExprVal =
+ loc::MemRegionVal(getStoreManager().GetElementZeroRegion(SR, T));
+ }
+
const auto *field = cast<FieldDecl>(Member);
SVal L = state->getLValue(field, baseExprVal);
@@ -2791,7 +3480,8 @@ ProgramStateRef ExprEngine::processPointerEscapedOnBind(
for (const std::pair<SVal, SVal> &LocAndVal : LocAndVals) {
// Cases (1) and (2).
const MemRegion *MR = LocAndVal.first.getAsRegion();
- if (!MR || !MR->hasStackStorage()) {
+ if (!MR ||
+ !isa<StackSpaceRegion, StaticGlobalSpaceRegion>(MR->getMemorySpace())) {
Escaped.push_back(LocAndVal.second);
continue;
}
@@ -2894,7 +3584,7 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
// If the location is not a 'Loc', it will already be handled by
// the checkers. There is nothing left to do.
- if (!location.getAs<Loc>()) {
+ if (!isa<Loc>(location)) {
const ProgramPoint L = PostStore(StoreE, LC, /*Loc*/nullptr,
/*tag*/nullptr);
ProgramStateRef state = Pred->getState();
@@ -2915,7 +3605,7 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
Val, LC, /* notifyChanges = */ !atDeclInit);
const MemRegion *LocReg = nullptr;
- if (Optional<loc::MemRegionVal> LocRegVal =
+ if (std::optional<loc::MemRegionVal> LocRegVal =
location.getAs<loc::MemRegionVal>()) {
LocReg = LocRegVal->getRegion();
}
@@ -2964,7 +3654,7 @@ void ExprEngine::evalLoad(ExplodedNodeSet &Dst,
SVal location,
const ProgramPointTag *tag,
QualType LoadTy) {
- assert(!location.getAs<NonLoc>() && "location cannot be a NonLoc.");
+ assert(!isa<NonLoc>(location) && "location cannot be a NonLoc.");
assert(NodeEx);
assert(BoundEx);
// Evaluate the location (checks for bad dereferences).
@@ -3056,7 +3746,7 @@ void ExprEngine::evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst,
ProgramStateRef state = Pred->getState();
SVal V = state->getSVal(Ex, Pred->getLocationContext());
- Optional<nonloc::SymbolVal> SEV = V.getAs<nonloc::SymbolVal>();
+ std::optional<nonloc::SymbolVal> SEV = V.getAs<nonloc::SymbolVal>();
if (SEV && SEV->isExpression()) {
const std::pair<const ProgramPointTag *, const ProgramPointTag*> &tags =
geteagerlyAssumeBinOpBifurcationTags();
@@ -3095,9 +3785,9 @@ void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred,
for (const Expr *O : A->outputs()) {
SVal X = state->getSVal(O, Pred->getLocationContext());
- assert(!X.getAs<NonLoc>()); // Should be an Lval, or unknown, undef.
+ assert(!isa<NonLoc>(X)); // Should be an Lval, or unknown, undef.
- if (Optional<Loc> LV = X.getAs<Loc>())
+ if (std::optional<Loc> LV = X.getAs<Loc>())
state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext());
}
@@ -3114,7 +3804,6 @@ void ExprEngine::VisitMSAsmStmt(const MSAsmStmt *A, ExplodedNode *Pred,
// Visualization.
//===----------------------------------------------------------------------===//
-#ifndef NDEBUG
namespace llvm {
template<>
@@ -3125,12 +3814,9 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
BugReporter &BR = static_cast<ExprEngine &>(
N->getState()->getStateManager().getOwningEngine()).getBugReporter();
- const auto EQClasses =
- llvm::make_range(BR.EQClasses_begin(), BR.EQClasses_end());
-
- for (const auto &EQ : EQClasses) {
- for (const auto &I : EQ.getReports()) {
- const auto *PR = dyn_cast<PathSensitiveBugReport>(I.get());
+ for (const auto &Class : BR.equivalenceClasses()) {
+ for (const auto &Report : Class.getReports()) {
+ const auto *PR = dyn_cast<PathSensitiveBugReport>(Report.get());
if (!PR)
continue;
const ExplodedNode *EN = PR->getErrorNode();
@@ -3190,7 +3876,7 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
OtherNode->getLocation().printJson(Out, /*NL=*/"\\l");
Out << ", \"tag\": ";
if (const ProgramPointTag *Tag = OtherNode->getLocation().getTag())
- Out << '\"' << Tag->getTagDescription() << "\"";
+ Out << '\"' << Tag->getTagDescription() << '\"';
else
Out << "null";
Out << ", \"node_id\": " << OtherNode->getID() <<
@@ -3212,72 +3898,51 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
};
} // namespace llvm
-#endif
void ExprEngine::ViewGraph(bool trim) {
-#ifndef NDEBUG
std::string Filename = DumpGraph(trim);
llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT);
-#else
- llvm::errs() << "Warning: viewing graph requires assertions" << "\n";
-#endif
}
-
-void ExprEngine::ViewGraph(ArrayRef<const ExplodedNode*> Nodes) {
-#ifndef NDEBUG
+void ExprEngine::ViewGraph(ArrayRef<const ExplodedNode *> Nodes) {
std::string Filename = DumpGraph(Nodes);
llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT);
-#else
- llvm::errs() << "Warning: viewing graph requires assertions" << "\n";
-#endif
}
std::string ExprEngine::DumpGraph(bool trim, StringRef Filename) {
-#ifndef NDEBUG
if (trim) {
std::vector<const ExplodedNode *> Src;
// Iterate through the reports and get their nodes.
- for (BugReporter::EQClasses_iterator
- EI = BR.EQClasses_begin(), EE = BR.EQClasses_end(); EI != EE; ++EI) {
+ for (const auto &Class : BR.equivalenceClasses()) {
const auto *R =
- dyn_cast<PathSensitiveBugReport>(EI->getReports()[0].get());
+ dyn_cast<PathSensitiveBugReport>(Class.getReports()[0].get());
if (!R)
continue;
const auto *N = const_cast<ExplodedNode *>(R->getErrorNode());
Src.push_back(N);
}
return DumpGraph(Src, Filename);
- } else {
- return llvm::WriteGraph(&G, "ExprEngine", /*ShortNames=*/false,
- /*Title=*/"Exploded Graph",
- /*Filename=*/std::string(Filename));
}
-#else
- llvm::errs() << "Warning: dumping graph requires assertions" << "\n";
- return "";
-#endif
+
+ return llvm::WriteGraph(&G, "ExprEngine", /*ShortNames=*/false,
+ /*Title=*/"Exploded Graph",
+ /*Filename=*/std::string(Filename));
}
-std::string ExprEngine::DumpGraph(ArrayRef<const ExplodedNode*> Nodes,
+std::string ExprEngine::DumpGraph(ArrayRef<const ExplodedNode *> Nodes,
StringRef Filename) {
-#ifndef NDEBUG
std::unique_ptr<ExplodedGraph> TrimmedG(G.trim(Nodes));
if (!TrimmedG.get()) {
llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n";
return "";
- } else {
- return llvm::WriteGraph(TrimmedG.get(), "TrimmedExprEngine",
- /*ShortNames=*/false,
- /*Title=*/"Trimmed Exploded Graph",
- /*Filename=*/std::string(Filename));
- }
-#else
- llvm::errs() << "Warning: dumping graph requires assertions" << "\n";
- return "";
-#endif
+ }
+
+ return llvm::WriteGraph(TrimmedG.get(), "TrimmedExprEngine",
+ /*ShortNames=*/false,
+ /*Title=*/"Trimmed Exploded Graph",
+ /*Filename=*/std::string(Filename));
}
void *ProgramStateTrait<ReplayWithoutInlining>::GDMIndex() {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 7ad3dca831ac..7e431f7e598c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -10,10 +10,11 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/AST/ExprCXX.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -29,8 +30,7 @@ static SVal conjureOffsetSymbolOnLocation(
SVal Symbol, SVal Other, Expr* Expression, SValBuilder &svalBuilder,
unsigned Count, const LocationContext *LCtx) {
QualType Ty = Expression->getType();
- if (Other.getAs<Loc>() &&
- Ty->isIntegralOrEnumerationType() &&
+ if (isa<Loc>(Other) && Ty->isIntegralOrEnumerationType() &&
Symbol.isUnknown()) {
return svalBuilder.conjureSymbolVal(Expression, LCtx, Ty, Count);
}
@@ -133,11 +133,9 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
SVal location = LeftV;
evalLoad(Tmp, B, LHS, *it, state, location);
- for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E;
- ++I) {
-
- state = (*I)->getState();
- const LocationContext *LCtx = (*I)->getLocationContext();
+ for (ExplodedNode *N : Tmp) {
+ state = N->getState();
+ const LocationContext *LCtx = N->getLocationContext();
SVal V = state->getSVal(LHS, LCtx);
// Get the computation type.
@@ -171,8 +169,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
currBldrCtx->blockCount());
// However, we need to convert the symbol to the computation type.
Result = svalBuilder.evalCast(LHSVal, CTy, LTy);
- }
- else {
+ } else {
// The left-hand side may bind to a different value then the
// computation type.
LHSVal = svalBuilder.evalCast(Result, LTy, CTy);
@@ -185,7 +182,7 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
else
state = state->BindExpr(B, LCtx, Result);
- evalStore(Tmp2, B, LHS, *I, state, location, LHSVal);
+ evalStore(Tmp2, B, LHS, N, state, location, LHSVal);
}
}
@@ -211,14 +208,12 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
if (const BlockDataRegion *BDR =
dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) {
- BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(),
- E = BDR->referenced_vars_end();
-
+ auto ReferencedVars = BDR->referenced_vars();
auto CI = BD->capture_begin();
auto CE = BD->capture_end();
- for (; I != E; ++I) {
- const VarRegion *capturedR = I.getCapturedRegion();
- const TypedValueRegion *originalR = I.getOriginalRegion();
+ for (auto Var : ReferencedVars) {
+ const VarRegion *capturedR = Var.getCapturedRegion();
+ const TypedValueRegion *originalR = Var.getOriginalRegion();
// If the capture had a copy expression, use the result of evaluating
// that expression, otherwise use the original value.
@@ -269,10 +264,12 @@ ProgramStateRef ExprEngine::handleLValueBitCast(
}
// Delegate to SValBuilder to process.
SVal OrigV = state->getSVal(Ex, LCtx);
- SVal V = svalBuilder.evalCast(OrigV, T, ExTy);
+ SVal SimplifiedOrigV = svalBuilder.simplifySVal(state, OrigV);
+ SVal V = svalBuilder.evalCast(SimplifiedOrigV, T, ExTy);
// Negate the result if we're treating the boolean as a signed i1
- if (CastE->getCastKind() == CK_BooleanToSignedIntegral)
- V = evalMinus(V);
+ if (CastE->getCastKind() == CK_BooleanToSignedIntegral && V.isValid())
+ V = svalBuilder.evalMinus(V.castAs<NonLoc>());
+
state = state->BindExpr(CastE, LCtx, V);
if (V.isUnknown() && !OrigV.isUnknown()) {
state = escapeValues(state, OrigV, PSK_EscapeOther);
@@ -290,9 +287,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
if (CastE->getCastKind() == CK_LValueToRValue ||
CastE->getCastKind() == CK_LValueToRValueBitCast) {
- for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end();
- I!=E; ++I) {
- ExplodedNode *subExprNode = *I;
+ for (ExplodedNode *subExprNode : dstPreStmt) {
ProgramStateRef state = subExprNode->getState();
const LocationContext *LCtx = subExprNode->getLocationContext();
evalLoad(Dst, CastE, CastE, subExprNode, state, state->getSVal(Ex, LCtx));
@@ -308,10 +303,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
T = ExCast->getTypeAsWritten();
StmtNodeBuilder Bldr(dstPreStmt, Dst, *currBldrCtx);
- for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end();
- I != E; ++I) {
-
- Pred = *I;
+ for (ExplodedNode *Pred : dstPreStmt) {
ProgramStateRef state = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
@@ -371,7 +363,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_IntegralToPointer:
case CK_PointerToIntegral: {
SVal V = state->getSVal(Ex, LCtx);
- if (V.getAs<nonloc::PointerToMember>()) {
+ if (isa<nonloc::PointerToMember>(V)) {
state = state->BindExpr(CastE, LCtx, UnknownVal());
Bldr.generateNode(CastE, Pred, state);
continue;
@@ -416,7 +408,10 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_IntegralCast: {
// Delegate to SValBuilder to process.
SVal V = state->getSVal(Ex, LCtx);
- V = svalBuilder.evalIntegralCast(state, V, T, ExTy);
+ if (AMgr.options.ShouldSupportSymbolicIntegerCasts)
+ V = svalBuilder.evalCast(V, T, ExTy);
+ else
+ V = svalBuilder.evalIntegralCast(state, V, T, ExTy);
state = state->BindExpr(CastE, LCtx, V);
Bldr.generateNode(CastE, Pred, state);
continue;
@@ -439,14 +434,15 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
if (CastE->isGLValue())
resultType = getContext().getPointerType(resultType);
- bool Failed = false;
+ bool Failed = true;
- // Check if the value being cast evaluates to 0.
- if (val.isZeroConstant())
- Failed = true;
- // Else, evaluate the cast.
- else
- val = getStoreManager().attemptDownCast(val, T, Failed);
+ // Check if the value being cast does not evaluates to 0.
+ if (!val.isZeroConstant())
+ if (std::optional<SVal> V =
+ StateMgr.getStoreManager().evalBaseToDerived(val, T)) {
+ val = *V;
+ Failed = false;
+ }
if (Failed) {
if (T->isReferenceType()) {
@@ -456,7 +452,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
continue;
} else {
// If the cast fails on a pointer, bind to 0.
- state = state->BindExpr(CastE, LCtx, svalBuilder.makeNull());
+ state = state->BindExpr(CastE, LCtx,
+ svalBuilder.makeNullWithType(resultType));
}
} else {
// If we don't know if the cast succeeded, conjure a new symbol.
@@ -478,14 +475,13 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
if (CastE->isGLValue())
resultType = getContext().getPointerType(resultType);
- bool Failed = false;
-
if (!val.isConstant()) {
- val = getStoreManager().attemptDownCast(val, T, Failed);
+ std::optional<SVal> V = getStoreManager().evalBaseToDerived(val, T);
+ val = V ? *V : UnknownVal();
}
// Failed to cast or the result is unknown, fall back to conservative.
- if (Failed || val.isUnknown()) {
+ if (val.isUnknown()) {
val =
svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx, resultType,
currBldrCtx->blockCount());
@@ -495,7 +491,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
continue;
}
case CK_NullToPointer: {
- SVal V = svalBuilder.makeNull();
+ SVal V = svalBuilder.makeNullWithType(CastE->getType());
state = state->BindExpr(CastE, LCtx, V);
Bldr.generateNode(CastE, Pred, state);
continue;
@@ -520,7 +516,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
}
// Explicitly proceed with default handler for this case cascade.
}
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
// Various C++ casts that are not handled yet.
case CK_ToUnion:
case CK_MatrixCast:
@@ -550,7 +546,7 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL,
const Expr *Init = CL->getInitializer();
SVal V = State->getSVal(CL->getInitializer(), LCtx);
- if (isa<CXXConstructExpr>(Init) || isa<CXXStdInitializerListExpr>(Init)) {
+ if (isa<CXXConstructExpr, CXXStdInitializerListExpr>(Init)) {
// No work needed. Just pass the value up to this expression.
} else {
assert(isa<InitListExpr>(Init));
@@ -757,9 +753,8 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE,
return;
}
- for (InitListExpr::const_reverse_iterator it = IE->rbegin(),
- ei = IE->rend(); it != ei; ++it) {
- SVal V = state->getSVal(cast<Expr>(*it), LCtx);
+ for (const Stmt *S : llvm::reverse(*IE)) {
+ SVal V = state->getSVal(cast<Expr>(S), LCtx);
vals = getBasicVals().prependSVal(V, vals);
}
@@ -820,7 +815,7 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex,
SVal V;
for (CFGElement CE : llvm::reverse(*SrcBlock)) {
- if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {
+ if (std::optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {
const Expr *ValEx = cast<Expr>(CS->getStmt());
ValEx = ValEx->IgnoreParens();
@@ -879,8 +874,7 @@ VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex,
QualType T = Ex->getTypeOfArgument();
- for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
- I != E; ++I) {
+ for (ExplodedNode *N : CheckedSet) {
if (Ex->getKind() == UETT_SizeOf) {
if (!T->isIncompleteType() && !T->isConstantSizeType()) {
assert(T->isVariableArrayType() && "Unknown non-constant-sized type.");
@@ -899,18 +893,17 @@ VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex,
APSInt Value = Ex->EvaluateKnownConstInt(getContext());
CharUnits amt = CharUnits::fromQuantity(Value.getZExtValue());
- ProgramStateRef state = (*I)->getState();
- state = state->BindExpr(Ex, (*I)->getLocationContext(),
- svalBuilder.makeIntVal(amt.getQuantity(),
- Ex->getType()));
- Bldr.generateNode(Ex, *I, state);
+ ProgramStateRef state = N->getState();
+ state = state->BindExpr(
+ Ex, N->getLocationContext(),
+ svalBuilder.makeIntVal(amt.getQuantity(), Ex->getType()));
+ Bldr.generateNode(Ex, N, state);
}
getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this);
}
-void ExprEngine::handleUOExtension(ExplodedNodeSet::iterator I,
- const UnaryOperator *U,
+void ExprEngine::handleUOExtension(ExplodedNode *N, const UnaryOperator *U,
StmtNodeBuilder &Bldr) {
// FIXME: We can probably just have some magic in Environment::getSVal()
// that propagates values, instead of creating a new node here.
@@ -920,10 +913,9 @@ void ExprEngine::handleUOExtension(ExplodedNodeSet::iterator I,
// generate an extra node that just propagates the value of the
// subexpression.
const Expr *Ex = U->getSubExpr()->IgnoreParens();
- ProgramStateRef state = (*I)->getState();
- const LocationContext *LCtx = (*I)->getLocationContext();
- Bldr.generateNode(U, *I, state->BindExpr(U, LCtx,
- state->getSVal(Ex, LCtx)));
+ ProgramStateRef state = N->getState();
+ const LocationContext *LCtx = N->getLocationContext();
+ Bldr.generateNode(U, N, state->BindExpr(U, LCtx, state->getSVal(Ex, LCtx)));
}
void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
@@ -935,13 +927,12 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
ExplodedNodeSet EvalSet;
StmtNodeBuilder Bldr(CheckedSet, EvalSet, *currBldrCtx);
- for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
- I != E; ++I) {
+ for (ExplodedNode *N : CheckedSet) {
switch (U->getOpcode()) {
default: {
- Bldr.takeNodes(*I);
+ Bldr.takeNodes(N);
ExplodedNodeSet Tmp;
- VisitIncrementDecrementOperator(U, *I, Tmp);
+ VisitIncrementDecrementOperator(U, N, Tmp);
Bldr.addNodes(Tmp);
break;
}
@@ -956,10 +947,10 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
// For all other types, UO_Real is an identity operation.
assert (U->getType() == Ex->getType());
- ProgramStateRef state = (*I)->getState();
- const LocationContext *LCtx = (*I)->getLocationContext();
- Bldr.generateNode(U, *I, state->BindExpr(U, LCtx,
- state->getSVal(Ex, LCtx)));
+ ProgramStateRef state = N->getState();
+ const LocationContext *LCtx = N->getLocationContext();
+ Bldr.generateNode(U, N,
+ state->BindExpr(U, LCtx, state->getSVal(Ex, LCtx)));
break;
}
@@ -971,10 +962,10 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
break;
}
// For all other types, UO_Imag returns 0.
- ProgramStateRef state = (*I)->getState();
- const LocationContext *LCtx = (*I)->getLocationContext();
+ ProgramStateRef state = N->getState();
+ const LocationContext *LCtx = N->getLocationContext();
SVal X = svalBuilder.makeZeroVal(Ex->getType());
- Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, X));
+ Bldr.generateNode(U, N, state->BindExpr(U, LCtx, X));
break;
}
@@ -984,25 +975,24 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Ex)) {
const ValueDecl *VD = DRE->getDecl();
- if (isa<CXXMethodDecl>(VD) || isa<FieldDecl>(VD) ||
- isa<IndirectFieldDecl>(VD)) {
- ProgramStateRef State = (*I)->getState();
- const LocationContext *LCtx = (*I)->getLocationContext();
+ if (isa<CXXMethodDecl, FieldDecl, IndirectFieldDecl>(VD)) {
+ ProgramStateRef State = N->getState();
+ const LocationContext *LCtx = N->getLocationContext();
SVal SV = svalBuilder.getMemberPointer(cast<NamedDecl>(VD));
- Bldr.generateNode(U, *I, State->BindExpr(U, LCtx, SV));
+ Bldr.generateNode(U, N, State->BindExpr(U, LCtx, SV));
break;
}
}
// Explicitly proceed with default handler for this case cascade.
- handleUOExtension(I, U, Bldr);
+ handleUOExtension(N, U, Bldr);
break;
}
case UO_Plus:
assert(!U->isGLValue());
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case UO_Deref:
case UO_Extension: {
- handleUOExtension(I, U, Bldr);
+ handleUOExtension(N, U, Bldr);
break;
}
@@ -1011,14 +1001,14 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
case UO_Not: {
assert (!U->isGLValue());
const Expr *Ex = U->getSubExpr()->IgnoreParens();
- ProgramStateRef state = (*I)->getState();
- const LocationContext *LCtx = (*I)->getLocationContext();
+ ProgramStateRef state = N->getState();
+ const LocationContext *LCtx = N->getLocationContext();
// Get the value of the subexpression.
SVal V = state->getSVal(Ex, LCtx);
if (V.isUnknownOrUndef()) {
- Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, V));
+ Bldr.generateNode(U, N, state->BindExpr(U, LCtx, V));
break;
}
@@ -1027,11 +1017,13 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
llvm_unreachable("Invalid Opcode.");
case UO_Not:
// FIXME: Do we need to handle promotions?
- state = state->BindExpr(U, LCtx, evalComplement(V.castAs<NonLoc>()));
+ state = state->BindExpr(
+ U, LCtx, svalBuilder.evalComplement(V.castAs<NonLoc>()));
break;
case UO_Minus:
// FIXME: Do we need to handle promotions?
- state = state->BindExpr(U, LCtx, evalMinus(V.castAs<NonLoc>()));
+ state = state->BindExpr(U, LCtx,
+ svalBuilder.evalMinus(V.castAs<NonLoc>()));
break;
case UO_LNot:
// C99 6.5.3.3: "The expression !E is equivalent to (0==E)."
@@ -1039,22 +1031,21 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
// Note: technically we do "E == 0", but this is the same in the
// transfer functions as "0 == E".
SVal Result;
- if (Optional<Loc> LV = V.getAs<Loc>()) {
- Loc X = svalBuilder.makeNullWithType(Ex->getType());
- Result = evalBinOp(state, BO_EQ, *LV, X, U->getType());
+ if (std::optional<Loc> LV = V.getAs<Loc>()) {
+ Loc X = svalBuilder.makeNullWithType(Ex->getType());
+ Result = evalBinOp(state, BO_EQ, *LV, X, U->getType());
} else if (Ex->getType()->isFloatingType()) {
- // FIXME: handle floating point types.
- Result = UnknownVal();
+ // FIXME: handle floating point types.
+ Result = UnknownVal();
} else {
- nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType()));
- Result = evalBinOp(state, BO_EQ, V.castAs<NonLoc>(), X,
- U->getType());
+ nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType()));
+ Result = evalBinOp(state, BO_EQ, V.castAs<NonLoc>(), X, U->getType());
}
state = state->BindExpr(U, LCtx, Result);
break;
}
- Bldr.generateNode(U, *I, state);
+ Bldr.generateNode(U, N, state);
break;
}
}
@@ -1080,10 +1071,9 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U,
ExplodedNodeSet Dst2;
StmtNodeBuilder Bldr(Tmp, Dst2, *currBldrCtx);
- for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end();I!=E;++I) {
-
- state = (*I)->getState();
- assert(LCtx == (*I)->getLocationContext());
+ for (ExplodedNode *N : Tmp) {
+ state = N->getState();
+ assert(LCtx == N->getLocationContext());
SVal V2_untested = state->getSVal(Ex, LCtx);
// Propagate unknown and undefined values.
@@ -1091,9 +1081,9 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U,
state = state->BindExpr(U, LCtx, V2_untested);
// Perform the store, so that the uninitialized value detection happens.
- Bldr.takeNodes(*I);
+ Bldr.takeNodes(N);
ExplodedNodeSet Dst3;
- evalStore(Dst3, U, Ex, *I, state, loc, V2_untested);
+ evalStore(Dst3, U, Ex, N, state, loc, V2_untested);
Bldr.addNodes(Dst3);
continue;
@@ -1159,9 +1149,9 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U,
state = state->BindExpr(U, LCtx, U->isPostfix() ? V2 : Result);
// Perform the store.
- Bldr.takeNodes(*I);
+ Bldr.takeNodes(N);
ExplodedNodeSet Dst3;
- evalStore(Dst3, U, Ex, *I, state, loc, Result);
+ evalStore(Dst3, U, Ex, N, state, loc, Result);
Bldr.addNodes(Dst3);
}
Dst.insert(Dst2);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index cab65687444b..504fd7f05e0f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -10,15 +10,19 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/Analysis/ConstructionContext.h"
#include "clang/AST/DeclCXX.h"
-#include "clang/AST/StmtCXX.h"
#include "clang/AST/ParentMap.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/Analysis/ConstructionContext.h"
#include "clang/Basic/PrettyStackTrace.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Sequence.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -57,61 +61,68 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred,
AlwaysReturnsLValue = true;
}
- assert(ThisRD);
- if (ThisRD->isEmpty()) {
- // Do nothing for empty classes. Otherwise it'd retrieve an UnknownVal
- // and bind it and RegionStore would think that the actual value
- // in this region at this offset is unknown.
- return;
- }
-
const LocationContext *LCtx = Pred->getLocationContext();
+ const Expr *CallExpr = Call.getOriginExpr();
ExplodedNodeSet Dst;
Bldr.takeNodes(Pred);
- SVal V = Call.getArgSVal(0);
-
- // If the value being copied is not unknown, load from its location to get
- // an aggregate rvalue.
- if (Optional<Loc> L = V.getAs<Loc>())
- V = Pred->getState()->getSVal(*L);
- else
- assert(V.isUnknownOrUndef());
+ assert(ThisRD);
+ if (!ThisRD->isEmpty()) {
+ // Load the source value only for non-empty classes.
+ // Otherwise it'd retrieve an UnknownVal
+ // and bind it and RegionStore would think that the actual value
+ // in this region at this offset is unknown.
+ SVal V = Call.getArgSVal(0);
- const Expr *CallExpr = Call.getOriginExpr();
- evalBind(Dst, CallExpr, Pred, ThisVal, V, true);
+ // If the value being copied is not unknown, load from its location to get
+ // an aggregate rvalue.
+ if (std::optional<Loc> L = V.getAs<Loc>())
+ V = Pred->getState()->getSVal(*L);
+ else
+ assert(V.isUnknownOrUndef());
+ evalBind(Dst, CallExpr, Pred, ThisVal, V, true);
+ } else {
+ Dst.Add(Pred);
+ }
PostStmt PS(CallExpr, LCtx);
- for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end();
- I != E; ++I) {
- ProgramStateRef State = (*I)->getState();
+ for (ExplodedNode *N : Dst) {
+ ProgramStateRef State = N->getState();
if (AlwaysReturnsLValue)
State = State->BindExpr(CallExpr, LCtx, ThisVal);
else
State = bindReturnValue(Call, LCtx, State);
- Bldr.generateNode(PS, State, *I);
+ Bldr.generateNode(PS, State, N);
}
}
-
-SVal ExprEngine::makeZeroElementRegion(ProgramStateRef State, SVal LValue,
- QualType &Ty, bool &IsArray) {
+SVal ExprEngine::makeElementRegion(ProgramStateRef State, SVal LValue,
+ QualType &Ty, bool &IsArray, unsigned Idx) {
SValBuilder &SVB = State->getStateManager().getSValBuilder();
ASTContext &Ctx = SVB.getContext();
- while (const ArrayType *AT = Ctx.getAsArrayType(Ty)) {
- Ty = AT->getElementType();
- LValue = State->getLValue(Ty, SVB.makeZeroArrayIndex(), LValue);
+ if (const ArrayType *AT = Ctx.getAsArrayType(Ty)) {
+ while (AT) {
+ Ty = AT->getElementType();
+ AT = dyn_cast<ArrayType>(AT->getElementType());
+ }
+ LValue = State->getLValue(Ty, SVB.makeArrayIndex(Idx), LValue);
IsArray = true;
}
return LValue;
}
+// In case when the prvalue is returned from the function (kind is one of
+// SimpleReturnedValueKind, CXX17ElidedCopyReturnedValueKind), then
+// it's materialization happens in context of the caller.
+// We pass BldrCtx explicitly, as currBldrCtx always refers to callee's context.
SVal ExprEngine::computeObjectUnderConstruction(
- const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
- const ConstructionContext *CC, EvalCallOptions &CallOpts) {
+ const Expr *E, ProgramStateRef State, const NodeBuilderContext *BldrCtx,
+ const LocationContext *LCtx, const ConstructionContext *CC,
+ EvalCallOptions &CallOpts, unsigned Idx) {
+
SValBuilder &SVB = getSValBuilder();
MemRegionManager &MRMgr = SVB.getRegionManager();
ASTContext &ACtx = SVB.getContext();
@@ -125,8 +136,8 @@ SVal ExprEngine::computeObjectUnderConstruction(
const auto *DS = DSCC->getDeclStmt();
const auto *Var = cast<VarDecl>(DS->getSingleDecl());
QualType Ty = Var->getType();
- return makeZeroElementRegion(State, State->getLValue(Var, LCtx), Ty,
- CallOpts.IsArrayCtorOrDtor);
+ return makeElementRegion(State, State->getLValue(Var, LCtx), Ty,
+ CallOpts.IsArrayCtorOrDtor, Idx);
}
case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind:
case ConstructionContext::SimpleConstructorInitializerKind: {
@@ -158,8 +169,8 @@ SVal ExprEngine::computeObjectUnderConstruction(
}
QualType Ty = Field->getType();
- return makeZeroElementRegion(State, FieldVal, Ty,
- CallOpts.IsArrayCtorOrDtor);
+ return makeElementRegion(State, FieldVal, Ty, CallOpts.IsArrayCtorOrDtor,
+ Idx);
}
case ConstructionContext::NewAllocatedObjectKind: {
if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator) {
@@ -169,11 +180,16 @@ SVal ExprEngine::computeObjectUnderConstruction(
if (const SubRegion *MR =
dyn_cast_or_null<SubRegion>(V.getAsRegion())) {
if (NE->isArray()) {
- // TODO: In fact, we need to call the constructor for every
- // allocated element, not just the first one!
CallOpts.IsArrayCtorOrDtor = true;
- return loc::MemRegionVal(getStoreManager().GetElementZeroRegion(
- MR, NE->getType()->getPointeeType()));
+
+ auto Ty = NE->getType()->getPointeeType();
+ while (const auto *AT = getContext().getAsArrayType(Ty))
+ Ty = AT->getElementType();
+
+ auto R = MRMgr.getElementRegion(Ty, svalBuilder.makeArrayIndex(Idx),
+ MR, SVB.getContext());
+
+ return loc::MemRegionVal(R);
}
return V;
}
@@ -203,8 +219,11 @@ SVal ExprEngine::computeObjectUnderConstruction(
CallerLCtx = CallerLCtx->getParent();
assert(!isa<BlockInvocationContext>(CallerLCtx));
}
+
+ NodeBuilderContext CallerBldrCtx(getCoreEngine(),
+ SFC->getCallSiteBlock(), CallerLCtx);
return computeObjectUnderConstruction(
- cast<Expr>(SFC->getCallSite()), State, CallerLCtx,
+ cast<Expr>(SFC->getCallSite()), State, &CallerBldrCtx, CallerLCtx,
RTC->getConstructionContext(), CallOpts);
} else {
// We are on the top frame of the analysis. We do not know where is the
@@ -244,7 +263,7 @@ SVal ExprEngine::computeObjectUnderConstruction(
EvalCallOptions PreElideCallOpts = CallOpts;
SVal V = computeObjectUnderConstruction(
- TCC->getConstructorAfterElision(), State, LCtx,
+ TCC->getConstructorAfterElision(), State, BldrCtx, LCtx,
TCC->getConstructionContextAfterElision(), CallOpts);
// FIXME: This definition of "copy elision has not failed" is unreliable.
@@ -257,7 +276,7 @@ SVal ExprEngine::computeObjectUnderConstruction(
// a simple temporary.
CallOpts = PreElideCallOpts;
CallOpts.IsElidableCtorThatHasNotBeenElided = true;
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
}
case ConstructionContext::SimpleTemporaryObjectKind: {
const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
@@ -266,7 +285,8 @@ SVal ExprEngine::computeObjectUnderConstruction(
CallOpts.IsTemporaryCtorOrDtor = true;
if (MTE) {
if (const ValueDecl *VD = MTE->getExtendingDecl()) {
- assert(MTE->getStorageDuration() != SD_FullExpression);
+ StorageDuration SD = MTE->getStorageDuration();
+ assert(SD != SD_FullExpression);
if (!VD->getType()->isReferenceType()) {
// We're lifetime-extended by a surrounding aggregate.
// Automatic destructors aren't quite working in this case
@@ -275,15 +295,36 @@ SVal ExprEngine::computeObjectUnderConstruction(
// the MaterializeTemporaryExpr?
CallOpts.IsTemporaryLifetimeExtendedViaAggregate = true;
}
- }
- if (MTE->getStorageDuration() == SD_Static ||
- MTE->getStorageDuration() == SD_Thread)
- return loc::MemRegionVal(MRMgr.getCXXStaticTempObjectRegion(E));
+ if (SD == SD_Static || SD == SD_Thread)
+ return loc::MemRegionVal(
+ MRMgr.getCXXStaticLifetimeExtendedObjectRegion(E, VD));
+
+ return loc::MemRegionVal(
+ MRMgr.getCXXLifetimeExtendedObjectRegion(E, VD, LCtx));
+ }
+ assert(MTE->getStorageDuration() == SD_FullExpression);
}
return loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
}
+ case ConstructionContext::LambdaCaptureKind: {
+ CallOpts.IsTemporaryCtorOrDtor = true;
+
+ const auto *LCC = cast<LambdaCaptureConstructionContext>(CC);
+
+ SVal Base = loc::MemRegionVal(
+ MRMgr.getCXXTempObjectRegion(LCC->getInitializer(), LCtx));
+
+ const auto *CE = dyn_cast_or_null<CXXConstructExpr>(E);
+ if (getIndexOfElementToConstruct(State, CE, LCtx)) {
+ CallOpts.IsArrayCtorOrDtor = true;
+ Base = State->getLValue(E->getType(), svalBuilder.makeArrayIndex(Idx),
+ Base);
+ }
+
+ return Base;
+ }
case ConstructionContext::ArgumentKind: {
// Arguments are technically temporaries.
CallOpts.IsTemporaryCtorOrDtor = true;
@@ -293,13 +334,13 @@ SVal ExprEngine::computeObjectUnderConstruction(
unsigned Idx = ACC->getIndex();
CallEventManager &CEMgr = getStateManager().getCallEventManager();
- auto getArgLoc = [&](CallEventRef<> Caller) -> Optional<SVal> {
+ auto getArgLoc = [&](CallEventRef<> Caller) -> std::optional<SVal> {
const LocationContext *FutureSFC =
- Caller->getCalleeStackFrame(currBldrCtx->blockCount());
+ Caller->getCalleeStackFrame(BldrCtx->blockCount());
// Return early if we are unable to reliably foresee
// the future stack frame.
if (!FutureSFC)
- return None;
+ return std::nullopt;
// This should be equivalent to Caller->getDecl() for now, but
// FutureSFC->getDecl() is likely to support better stuff (like
@@ -308,37 +349,39 @@ SVal ExprEngine::computeObjectUnderConstruction(
// FIXME: Support for variadic arguments is not implemented here yet.
if (CallEvent::isVariadic(CalleeD))
- return None;
+ return std::nullopt;
// 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 TypedValueRegion *TVR = Caller->getParameterLocation(
- *Caller->getAdjustedParameterIndex(Idx), currBldrCtx->blockCount());
+ *Caller->getAdjustedParameterIndex(Idx), BldrCtx->blockCount());
if (!TVR)
- return None;
+ return std::nullopt;
return loc::MemRegionVal(TVR);
};
if (const auto *CE = dyn_cast<CallExpr>(E)) {
- CallEventRef<> Caller = CEMgr.getSimpleCall(CE, State, LCtx);
- if (Optional<SVal> V = getArgLoc(Caller))
+ CallEventRef<> Caller =
+ CEMgr.getSimpleCall(CE, State, LCtx, getCFGElementRef());
+ if (std::optional<SVal> V = getArgLoc(Caller))
return *V;
else
break;
} 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 (Optional<SVal> V = getArgLoc(Caller))
+ CallEventRef<> Caller = CEMgr.getCXXConstructorCall(
+ CCE, /*Target=*/nullptr, State, LCtx, getCFGElementRef());
+ if (std::optional<SVal> V = getArgLoc(Caller))
return *V;
else
break;
} else if (const auto *ME = dyn_cast<ObjCMessageExpr>(E)) {
- CallEventRef<> Caller = CEMgr.getObjCMethodCall(ME, State, LCtx);
- if (Optional<SVal> V = getArgLoc(Caller))
+ CallEventRef<> Caller =
+ CEMgr.getObjCMethodCall(ME, State, LCtx, getCFGElementRef());
+ if (std::optional<SVal> V = getArgLoc(Caller))
return *V;
else
break;
@@ -432,7 +475,7 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction(
}
// If we decided not to elide the constructor, proceed as if
// it's a simple temporary.
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
}
case ConstructionContext::SimpleTemporaryObjectKind: {
const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
@@ -444,6 +487,17 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction(
return State;
}
+ case ConstructionContext::LambdaCaptureKind: {
+ const auto *LCC = cast<LambdaCaptureConstructionContext>(CC);
+
+ // If we capture and array, we want to store the super region, not a
+ // sub-region.
+ if (const auto *EL = dyn_cast_or_null<ElementRegion>(V.getAsRegion()))
+ V = loc::MemRegionVal(EL->getSuperRegion());
+
+ return addObjectUnderConstruction(
+ State, {LCC->getLambdaExpr(), LCC->getIndex()}, LCtx, V);
+ }
case ConstructionContext::ArgumentKind: {
const auto *ACC = cast<ArgumentConstructionContext>(CC);
if (const auto *BTE = ACC->getCXXBindTemporaryExpr())
@@ -456,6 +510,75 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction(
llvm_unreachable("Unhandled construction context!");
}
+static ProgramStateRef
+bindRequiredArrayElementToEnvironment(ProgramStateRef State,
+ const ArrayInitLoopExpr *AILE,
+ const LocationContext *LCtx, SVal Idx) {
+ // The ctor in this case is guaranteed to be a copy ctor, otherwise we hit a
+ // compile time error.
+ //
+ // -ArrayInitLoopExpr <-- we're here
+ // |-OpaqueValueExpr
+ // | `-DeclRefExpr <-- match this
+ // `-CXXConstructExpr
+ // `-ImplicitCastExpr
+ // `-ArraySubscriptExpr
+ // |-ImplicitCastExpr
+ // | `-OpaqueValueExpr
+ // | `-DeclRefExpr
+ // `-ArrayInitIndexExpr
+ //
+ // The resulting expression might look like the one below in an implicit
+ // copy/move ctor.
+ //
+ // ArrayInitLoopExpr <-- we're here
+ // |-OpaqueValueExpr
+ // | `-MemberExpr <-- match this
+ // | (`-CXXStaticCastExpr) <-- move ctor only
+ // | `-DeclRefExpr
+ // `-CXXConstructExpr
+ // `-ArraySubscriptExpr
+ // |-ImplicitCastExpr
+ // | `-OpaqueValueExpr
+ // | `-MemberExpr
+ // | `-DeclRefExpr
+ // `-ArrayInitIndexExpr
+ //
+ // The resulting expression for a multidimensional array.
+ // ArrayInitLoopExpr <-- we're here
+ // |-OpaqueValueExpr
+ // | `-DeclRefExpr <-- match this
+ // `-ArrayInitLoopExpr
+ // |-OpaqueValueExpr
+ // | `-ArraySubscriptExpr
+ // | |-ImplicitCastExpr
+ // | | `-OpaqueValueExpr
+ // | | `-DeclRefExpr
+ // | `-ArrayInitIndexExpr
+ // `-CXXConstructExpr <-- extract this
+ // ` ...
+
+ const auto *OVESrc = AILE->getCommonExpr()->getSourceExpr();
+
+ // HACK: There is no way we can put the index of the array element into the
+ // CFG unless we unroll the loop, so we manually select and bind the required
+ // parameter to the environment.
+ const auto *CE =
+ cast<CXXConstructExpr>(extractElementInitializerFromNestedAILE(AILE));
+
+ SVal Base = UnknownVal();
+ if (const auto *ME = dyn_cast<MemberExpr>(OVESrc))
+ Base = State->getSVal(ME, LCtx);
+ else if (const auto *DRE = dyn_cast<DeclRefExpr>(OVESrc))
+ Base = State->getLValue(cast<VarDecl>(DRE->getDecl()), LCtx);
+ else
+ llvm_unreachable("ArrayInitLoopExpr contains unexpected source expression");
+
+ SVal NthElem = State->getLValue(CE->getType(), Idx, Base);
+
+ return State->BindExpr(CE->getArg(0), LCtx, NthElem);
+}
+
void ExprEngine::handleConstructor(const Expr *E,
ExplodedNode *Pred,
ExplodedNodeSet &destNodes) {
@@ -469,57 +592,95 @@ void ExprEngine::handleConstructor(const Expr *E,
SVal Target = UnknownVal();
if (CE) {
- if (Optional<SVal> ElidedTarget =
+ if (std::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;
+ // We've previously modeled an elidable constructor by pretending that
+ // it in fact constructs into the correct target. This constructor can
+ // therefore be skipped.
+ Target = *ElidedTarget;
+ StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx);
+ State = finishObjectConstruction(State, CE, LCtx);
+ if (auto L = Target.getAs<Loc>())
+ State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType()));
+ Bldr.generateNode(CE, Pred, State);
+ return;
}
}
- // FIXME: Handle arrays, which run the same constructor for every element.
- // For now, we just run the first constructor (which should still invalidate
- // the entire array).
-
EvalCallOptions CallOpts;
auto C = getCurrentCFGElement().getAs<CFGConstructor>();
assert(C || getCurrentCFGElement().getAs<CFGStmt>());
const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr;
- const CXXConstructExpr::ConstructionKind CK =
+ const CXXConstructionKind CK =
CE ? CE->getConstructionKind() : CIE->getConstructionKind();
switch (CK) {
- case CXXConstructExpr::CK_Complete: {
+ case CXXConstructionKind::Complete: {
// Inherited constructors are always base class constructors.
assert(CE && !CIE && "A complete constructor is inherited?!");
+ // If the ctor is part of an ArrayInitLoopExpr, we want to handle it
+ // differently.
+ auto *AILE = CC ? CC->getArrayInitLoop() : nullptr;
+
+ unsigned Idx = 0;
+ if (CE->getType()->isArrayType() || AILE) {
+
+ auto isZeroSizeArray = [&] {
+ uint64_t Size = 1;
+
+ if (const auto *CAT = dyn_cast<ConstantArrayType>(CE->getType()))
+ Size = getContext().getConstantArrayElementCount(CAT);
+ else if (AILE)
+ Size = getContext().getArrayInitLoopExprElementCount(AILE);
+
+ return Size == 0;
+ };
+
+ // No element construction will happen in a 0 size array.
+ if (isZeroSizeArray()) {
+ StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx);
+ static SimpleProgramPointTag T{"ExprEngine",
+ "Skipping 0 size array construction"};
+ Bldr.generateNode(CE, Pred, State, &T);
+ return;
+ }
+
+ Idx = getIndexOfElementToConstruct(State, CE, LCtx).value_or(0u);
+ State = setIndexOfElementToConstruct(State, CE, LCtx, Idx + 1);
+ }
+
+ if (AILE) {
+ // Only set this once even though we loop through it multiple times.
+ if (!getPendingInitLoop(State, CE, LCtx))
+ State = setPendingInitLoop(
+ State, CE, LCtx,
+ getContext().getArrayInitLoopExprElementCount(AILE));
+
+ State = bindRequiredArrayElementToEnvironment(
+ State, AILE, LCtx, svalBuilder.makeArrayIndex(Idx));
+ }
+
// The target region is found from construction context.
- std::tie(State, Target) =
- handleConstructionContext(CE, State, LCtx, CC, CallOpts);
+ std::tie(State, Target) = handleConstructionContext(
+ CE, State, currBldrCtx, LCtx, CC, CallOpts, Idx);
break;
}
- case CXXConstructExpr::CK_VirtualBase: {
+ case CXXConstructionKind::VirtualBase: {
// Make sure we are not calling virtual base class initializers twice.
// Only the most-derived object should initialize virtual base classes.
const auto *OuterCtor = dyn_cast_or_null<CXXConstructExpr>(
LCtx->getStackFrame()->getCallSite());
assert(
(!OuterCtor ||
- OuterCtor->getConstructionKind() == CXXConstructExpr::CK_Complete ||
- OuterCtor->getConstructionKind() == CXXConstructExpr::CK_Delegating) &&
+ OuterCtor->getConstructionKind() == CXXConstructionKind::Complete ||
+ OuterCtor->getConstructionKind() == CXXConstructionKind::Delegating) &&
("This virtual base should have already been initialized by "
"the most derived class!"));
(void)OuterCtor;
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
}
- case CXXConstructExpr::CK_NonVirtualBase:
+ case CXXConstructionKind::NonVirtualBase:
// In C++17, classes with non-virtual bases may be aggregates, so they would
// be initialized as aggregates without a constructor call, so we may have
// a base class constructed directly into an initializer list without
@@ -531,24 +692,24 @@ void ExprEngine::handleConstructor(const Expr *E,
// 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(E))) {
+ if (isa_and_nonnull<InitListExpr>(LCtx->getParentMap().getParent(E))) {
MemRegionManager &MRMgr = getSValBuilder().getRegionManager();
Target = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
break;
}
- LLVM_FALLTHROUGH;
- case CXXConstructExpr::CK_Delegating: {
+ [[fallthrough]];
+ case CXXConstructionKind::Delegating: {
const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor,
LCtx->getStackFrame());
SVal ThisVal = State->getSVal(ThisPtr);
- if (CK == CXXConstructExpr::CK_Delegating) {
+ if (CK == CXXConstructionKind::Delegating) {
Target = ThisVal;
} else {
// Cast to the base type.
- bool IsVirtual = (CK == CXXConstructExpr::CK_VirtualBase);
+ bool IsVirtual = (CK == CXXConstructionKind::VirtualBase);
SVal BaseVal =
getStoreManager().evalDerivedToBase(ThisVal, E->getType(), IsVirtual);
Target = BaseVal;
@@ -573,9 +734,9 @@ void ExprEngine::handleConstructor(const Expr *E,
CallEventManager &CEMgr = getStateManager().getCallEventManager();
CallEventRef<> Call =
CIE ? (CallEventRef<>)CEMgr.getCXXInheritedConstructorCall(
- CIE, TargetRegion, State, LCtx)
+ CIE, TargetRegion, State, LCtx, getCFGElementRef())
: (CallEventRef<>)CEMgr.getCXXConstructorCall(
- CE, TargetRegion, State, LCtx);
+ CE, TargetRegion, State, LCtx, getCFGElementRef());
ExplodedNodeSet DstPreVisit;
getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, E, *this);
@@ -584,10 +745,8 @@ void ExprEngine::handleConstructor(const Expr *E,
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();
- I != E; ++I) {
- ProgramStateRef State = (*I)->getState();
+ for (ExplodedNode *N : DstPreVisit) {
+ ProgramStateRef State = N->getState();
if (CE->requiresZeroInitialization()) {
// FIXME: Once we properly handle constructors in new-expressions, we'll
// need to invalidate the region before setting a default value, to make
@@ -604,7 +763,7 @@ void ExprEngine::handleConstructor(const Expr *E,
State = State->bindDefaultZero(Target, LCtx);
}
- Bldr.generateNode(CE, *I, State, /*tag=*/nullptr,
+ Bldr.generateNode(CE, N, State, /*tag=*/nullptr,
ProgramPoint::PreStmtKind);
}
} else {
@@ -622,14 +781,12 @@ void ExprEngine::handleConstructor(const Expr *E,
!CallOpts.IsArrayCtorOrDtor) {
StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx);
// FIXME: Handle other kinds of trivial constructors as well.
- for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
- I != E; ++I)
- performTrivialCopy(Bldr, *I, *Call);
+ for (ExplodedNode *N : DstPreCall)
+ performTrivialCopy(Bldr, N, *Call);
} else {
- for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
- I != E; ++I)
- getCheckerManager().runCheckersForEvalCall(DstEvaluated, *I, *Call, *this,
+ for (ExplodedNode *N : DstPreCall)
+ getCheckerManager().runCheckersForEvalCall(DstEvaluated, N, *Call, *this,
CallOpts);
}
@@ -644,7 +801,8 @@ void ExprEngine::handleConstructor(const Expr *E,
StmtNodeBuilder Bldr(DstEvaluated, DstEvaluatedPostProcessed, *currBldrCtx);
const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext();
if (!ADC->getCFGBuildOptions().AddTemporaryDtors) {
- if (llvm::isa_and_nonnull<CXXTempObjectRegion>(TargetRegion) &&
+ if (llvm::isa_and_nonnull<CXXTempObjectRegion,
+ CXXLifetimeExtendedObjectRegion>(TargetRegion) &&
cast<CXXConstructorDecl>(Call->getDecl())
->getParent()
->isAnyDestructorNoReturn()) {
@@ -716,7 +874,8 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
// it would interrupt the analysis instead.
static SimpleProgramPointTag T("ExprEngine", "SkipInvalidDestructor");
// FIXME: PostImplicitCall with a null decl may crash elsewhere anyway.
- PostImplicitCall PP(/*Decl=*/nullptr, S->getEndLoc(), LCtx, &T);
+ PostImplicitCall PP(/*Decl=*/nullptr, S->getEndLoc(), LCtx,
+ getCFGElementRef(), &T);
NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
Bldr.generateNode(PP, Pred->getState(), Pred);
return;
@@ -741,8 +900,8 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
}
CallEventManager &CEMgr = getStateManager().getCallEventManager();
- CallEventRef<CXXDestructorCall> Call =
- CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx);
+ CallEventRef<CXXDestructorCall> Call = CEMgr.getCXXDestructorCall(
+ DtorDecl, S, Dest, IsBaseDtor, State, LCtx, getCFGElementRef());
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
Call->getSourceRange().getBegin(),
@@ -754,9 +913,8 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
ExplodedNodeSet DstInvalidated;
StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currBldrCtx);
- for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
- I != E; ++I)
- defaultEvalCall(Bldr, *I, *Call, CallOpts);
+ for (ExplodedNode *N : DstPreCall)
+ defaultEvalCall(Bldr, N, *Call, CallOpts);
getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated,
*Call, *this);
@@ -772,7 +930,7 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
"Error evaluating New Allocator Call");
CallEventManager &CEMgr = getStateManager().getCallEventManager();
CallEventRef<CXXAllocatorCall> Call =
- CEMgr.getCXXAllocatorCall(CNE, State, LCtx);
+ CEMgr.getCXXAllocatorCall(CNE, State, LCtx, getCFGElementRef());
ExplodedNodeSet DstPreCall;
getCheckerManager().runCheckersForPreCall(DstPreCall, Pred,
@@ -802,6 +960,11 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
// skip it for now.
ProgramStateRef State = I->getState();
SVal RetVal = State->getSVal(CNE, LCtx);
+ // [basic.stc.dynamic.allocation] (on the return value of an allocation
+ // function):
+ // "The order, contiguity, and initial value of storage allocated by
+ // successive calls to an allocation function are unspecified."
+ State = State->bindDefaultInitial(RetVal, UndefinedVal{}, LCtx);
// If this allocation function is not declared as non-throwing, failures
// /must/ be signalled by exceptions, and thus the return value will never
@@ -865,7 +1028,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
CallEventManager &CEMgr = getStateManager().getCallEventManager();
CallEventRef<CXXAllocatorCall> Call =
- CEMgr.getCXXAllocatorCall(CNE, State, LCtx);
+ CEMgr.getCXXAllocatorCall(CNE, State, LCtx, getCFGElementRef());
if (!AMgr.getAnalyzerOptions().MayInlineCXXAllocator) {
// Invalidate placement args.
@@ -883,13 +1046,10 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
// where new can return NULL. If we end up supporting that option, we can
// consider adding a check for it here.
// C++11 [basic.stc.dynamic.allocation]p3.
- if (FD) {
- QualType Ty = FD->getType();
- if (const auto *ProtoType = Ty->getAs<FunctionProtoType>())
- if (!ProtoType->isNothrow())
- if (auto dSymVal = symVal.getAs<DefinedOrUnknownSVal>())
- State = State->assume(*dSymVal, true);
- }
+ if (const auto *ProtoType = FD->getType()->getAs<FunctionProtoType>())
+ if (!ProtoType->isNothrow())
+ if (auto dSymVal = symVal.getAs<DefinedOrUnknownSVal>())
+ State = State->assume(*dSymVal, true);
}
StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
@@ -897,14 +1057,39 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
SVal Result = symVal;
if (CNE->isArray()) {
- // FIXME: allocating an array requires simulating the constructors.
- // For now, just return a symbolicated region.
+
if (const auto *NewReg = cast_or_null<SubRegion>(symVal.getAsRegion())) {
- QualType ObjTy = CNE->getType()->getPointeeType();
+ // If each element is initialized by their default constructor, the field
+ // values are properly placed inside the required region, however if an
+ // initializer list is used, this doesn't happen automatically.
+ auto *Init = CNE->getInitializer();
+ bool isInitList = isa_and_nonnull<InitListExpr>(Init);
+
+ QualType ObjTy =
+ isInitList ? Init->getType() : CNE->getType()->getPointeeType();
const ElementRegion *EleReg =
- getStoreManager().GetElementZeroRegion(NewReg, ObjTy);
+ MRMgr.getElementRegion(ObjTy, svalBuilder.makeArrayIndex(0), NewReg,
+ svalBuilder.getContext());
Result = loc::MemRegionVal(EleReg);
+
+ // If the array is list initialized, we bind the initializer list to the
+ // memory region here, otherwise we would lose it.
+ if (isInitList) {
+ Bldr.takeNodes(Pred);
+ Pred = Bldr.generateNode(CNE, Pred, State);
+
+ SVal V = State->getSVal(Init, LCtx);
+ ExplodedNodeSet evaluated;
+ evalBind(evaluated, CNE, Pred, Result, V, true);
+
+ Bldr.takeNodes(Pred);
+ Bldr.addNodes(evaluated);
+
+ Pred = *evaluated.begin();
+ State = Pred->getState();
+ }
}
+
State = State->BindExpr(CNE, Pred->getLocationContext(), Result);
Bldr.generateNode(CNE, Pred, State);
return;
@@ -914,7 +1099,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
// CXXNewExpr, we need to make sure that the constructed object is not
// immediately invalidated here. (The placement call should happen before
// the constructor call anyway.)
- if (FD && FD->isReservedGlobalPlacementOperator()) {
+ if (FD->isReservedGlobalPlacementOperator()) {
// Non-array placement new should always return the placement location.
SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx);
Result = svalBuilder.evalCast(PlacementLoc, CNE->getType(),
@@ -944,12 +1129,21 @@ void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE,
CallEventManager &CEMgr = getStateManager().getCallEventManager();
CallEventRef<CXXDeallocatorCall> Call = CEMgr.getCXXDeallocatorCall(
- CDE, Pred->getState(), Pred->getLocationContext());
+ CDE, Pred->getState(), Pred->getLocationContext(), getCFGElementRef());
ExplodedNodeSet DstPreCall;
getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, *Call, *this);
+ ExplodedNodeSet DstPostCall;
- getCheckerManager().runCheckersForPostCall(Dst, DstPreCall, *Call, *this);
+ if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator) {
+ StmtNodeBuilder Bldr(DstPreCall, DstPostCall, *currBldrCtx);
+ for (ExplodedNode *I : DstPreCall) {
+ defaultEvalCall(Bldr, I, *Call);
+ }
+ } else {
+ DstPostCall = DstPreCall;
+ }
+ getCheckerManager().runCheckersForPostCall(Dst, DstPostCall, *Call, *this);
}
void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred,
@@ -999,19 +1193,41 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred,
// If we created a new MemRegion for the lambda, we should explicitly bind
// the captures.
- CXXRecordDecl::field_iterator CurField = LE->getLambdaClass()->field_begin();
- for (LambdaExpr::const_capture_init_iterator i = LE->capture_init_begin(),
- e = LE->capture_init_end();
- i != e; ++i, ++CurField) {
- FieldDecl *FieldForCapture = *CurField;
+ for (auto const [Idx, FieldForCapture, InitExpr] :
+ llvm::zip(llvm::seq<unsigned>(0, -1), LE->getLambdaClass()->fields(),
+ LE->capture_inits())) {
SVal FieldLoc = State->getLValue(FieldForCapture, V);
SVal InitVal;
if (!FieldForCapture->hasCapturedVLAType()) {
- Expr *InitExpr = *i;
assert(InitExpr && "Capture missing initialization expression");
- InitVal = State->getSVal(InitExpr, LocCtxt);
+
+ // Capturing a 0 length array is a no-op, so we ignore it to get a more
+ // accurate analysis. If it's not ignored, it would set the default
+ // binding of the lambda to 'Unknown', which can lead to falsely detecting
+ // 'Uninitialized' values as 'Unknown' and not reporting a warning.
+ const auto FTy = FieldForCapture->getType();
+ if (FTy->isConstantArrayType() &&
+ getContext().getConstantArrayElementCount(
+ getContext().getAsConstantArrayType(FTy)) == 0)
+ continue;
+
+ // With C++17 copy elision the InitExpr can be anything, so instead of
+ // pattern matching all cases, we simple check if the current field is
+ // under construction or not, regardless what it's InitExpr is.
+ if (const auto OUC =
+ getObjectUnderConstruction(State, {LE, Idx}, LocCtxt)) {
+ InitVal = State->getSVal(OUC->getAsRegion());
+
+ State = finishObjectConstruction(State, {LE, Idx}, LocCtxt);
+ } else
+ InitVal = State->getSVal(InitExpr, LocCtxt);
+
} else {
+
+ assert(!getObjectUnderConstruction(State, {LE, Idx}, LocCtxt) &&
+ "VLA capture by value is a compile time error!");
+
// The field stores the length of a captured variable-length array.
// These captures don't have initialization expressions; instead we
// get the length from the VLAType size expression.
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index e6918e071a4f..4755b6bfa6dc 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -25,6 +25,7 @@
#include "llvm/Support/Casting.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/SaveAndRestore.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -85,10 +86,10 @@ static std::pair<const Stmt*,
const ProgramPoint &PP = Node->getLocation();
if (PP.getStackFrame() == SF) {
- if (Optional<StmtPoint> SP = PP.getAs<StmtPoint>()) {
+ if (std::optional<StmtPoint> SP = PP.getAs<StmtPoint>()) {
S = SP->getStmt();
break;
- } else if (Optional<CallExitEnd> CEE = PP.getAs<CallExitEnd>()) {
+ } else if (std::optional<CallExitEnd> CEE = PP.getAs<CallExitEnd>()) {
S = CEE->getCalleeContext()->getCallSite();
if (S)
break;
@@ -96,17 +97,17 @@ static std::pair<const Stmt*,
// If there is no statement, this is an implicitly-generated call.
// We'll walk backwards over it and then continue the loop to find
// an actual statement.
- Optional<CallEnter> CE;
+ std::optional<CallEnter> CE;
do {
Node = Node->getFirstPred();
CE = Node->getLocationAs<CallEnter>();
} while (!CE || CE->getCalleeContext() != CEE->getCalleeContext());
// Continue searching the graph.
- } else if (Optional<BlockEdge> BE = PP.getAs<BlockEdge>()) {
+ } else if (std::optional<BlockEdge> BE = PP.getAs<BlockEdge>()) {
Blk = BE->getSrc();
}
- } else if (Optional<CallEnter> CE = PP.getAs<CallEnter>()) {
+ } else if (std::optional<CallEnter> CE = PP.getAs<CallEnter>()) {
// If we reached the CallEnter for this function, it has no statements.
if (CE->getCalleeContext() == SF)
break;
@@ -129,7 +130,7 @@ static std::pair<const Stmt*,
static SVal adjustReturnValue(SVal V, QualType ExpectedTy, QualType ActualTy,
StoreManager &StoreMgr) {
// For now, the only adjustments we handle apply only to locations.
- if (!V.getAs<Loc>())
+ if (!isa<Loc>(V))
return V;
// If the types already match, don't do any unnecessary work.
@@ -195,6 +196,53 @@ static bool wasDifferentDeclUsedForInlining(CallEventRef<> Call,
return RuntimeCallee->getCanonicalDecl() != StaticDecl->getCanonicalDecl();
}
+// Returns the number of elements in the array currently being destructed.
+// If the element count is not found 0 will be returned.
+static unsigned getElementCountOfArrayBeingDestructed(
+ const CallEvent &Call, const ProgramStateRef State, SValBuilder &SVB) {
+ assert(isa<CXXDestructorCall>(Call) &&
+ "The call event is not a destructor call!");
+
+ const auto &DtorCall = cast<CXXDestructorCall>(Call);
+
+ auto ThisVal = DtorCall.getCXXThisVal();
+
+ if (auto ThisElementRegion = dyn_cast<ElementRegion>(ThisVal.getAsRegion())) {
+ auto ArrayRegion = ThisElementRegion->getAsArrayOffset().getRegion();
+ auto ElementType = ThisElementRegion->getElementType();
+
+ auto ElementCount =
+ getDynamicElementCount(State, ArrayRegion, SVB, ElementType);
+
+ if (!ElementCount.isConstant())
+ return 0;
+
+ return ElementCount.getAsInteger()->getLimitedValue();
+ }
+
+ return 0;
+}
+
+ProgramStateRef ExprEngine::removeStateTraitsUsedForArrayEvaluation(
+ ProgramStateRef State, const CXXConstructExpr *E,
+ const LocationContext *LCtx) {
+
+ assert(LCtx && "Location context must be provided!");
+
+ if (E) {
+ if (getPendingInitLoop(State, E, LCtx))
+ State = removePendingInitLoop(State, E, LCtx);
+
+ if (getIndexOfElementToConstruct(State, E, LCtx))
+ State = removeIndexOfElementToConstruct(State, E, LCtx);
+ }
+
+ if (getPendingArrayDestruction(State, LCtx))
+ State = removePendingArrayDestruction(State, LCtx);
+
+ return State;
+}
+
/// The call exit is simulated with a sequence of nodes, which occur between
/// CallExitBegin and CallExitEnd. The following operations occur between the
/// two program points:
@@ -227,6 +275,23 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
// Step 2: generate node with bound return value: CEBNode -> BindedRetNode.
+ // If this variable is set to 'true' the analyzer will evaluate the call
+ // statement we are about to exit again, instead of continuing the execution
+ // from the statement after the call. This is useful for non-POD type array
+ // construction where the CXXConstructExpr is referenced only once in the CFG,
+ // but we want to evaluate it as many times as many elements the array has.
+ bool ShouldRepeatCall = false;
+
+ if (const auto *DtorDecl =
+ dyn_cast_or_null<CXXDestructorDecl>(Call->getDecl())) {
+ if (auto Idx = getPendingArrayDestruction(state, callerCtx)) {
+ ShouldRepeatCall = *Idx > 0;
+
+ auto ThisVal = svalBuilder.getCXXThis(DtorDecl->getParent(), calleeCtx);
+ state = state->killBinding(ThisVal);
+ }
+ }
+
// If the callee returns an expression, bind its value to CallExpr.
if (CE) {
if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) {
@@ -255,6 +320,8 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
SVal ThisV = state->getSVal(This);
ThisV = state->getSVal(ThisV.castAs<Loc>());
state = state->BindExpr(CCE, callerCtx, ThisV);
+
+ ShouldRepeatCall = shouldRepeatCtorCall(state, CCE, callerCtx);
}
if (const auto *CNE = dyn_cast<CXXNewExpr>(CE)) {
@@ -273,6 +340,11 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
}
}
+ if (!ShouldRepeatCall) {
+ state = removeStateTraitsUsedForArrayEvaluation(
+ state, dyn_cast_or_null<CXXConstructExpr>(CE), callerCtx);
+ }
+
// Step 3: BindedRetNode -> CleanedNodes
// If we can find a statement and a block in the inlined function, run remove
// dead bindings before returning from the call. This is important to ensure
@@ -302,17 +374,15 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
CleanedNodes.Add(CEBNode);
}
- for (ExplodedNodeSet::iterator I = CleanedNodes.begin(),
- E = CleanedNodes.end(); I != E; ++I) {
-
+ for (ExplodedNode *N : CleanedNodes) {
// Step 4: Generate the CallExit and leave the callee's context.
// CleanedNodes -> CEENode
CallExitEnd Loc(calleeCtx, callerCtx);
bool isNew;
- ProgramStateRef CEEState = (*I == CEBNode) ? state : (*I)->getState();
+ ProgramStateRef CEEState = (N == CEBNode) ? state : N->getState();
ExplodedNode *CEENode = G.getNode(Loc, CEEState, false, &isNew);
- CEENode->addPredecessor(*I, G);
+ CEENode->addPredecessor(N, G);
if (!isNew)
return;
@@ -320,9 +390,8 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
// result onto the work list.
// CEENode -> Dst -> WorkList
NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode);
- SaveAndRestore<const NodeBuilderContext*> NBCSave(currBldrCtx,
- &Ctx);
- SaveAndRestore<unsigned> CBISave(currStmtIdx, calleeCtx->getIndex());
+ SaveAndRestore<const NodeBuilderContext *> NBCSave(currBldrCtx, &Ctx);
+ SaveAndRestore CBISave(currStmtIdx, calleeCtx->getIndex());
CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState);
@@ -358,9 +427,10 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
// Enqueue the next element in the block.
for (ExplodedNodeSet::iterator PSI = Dst.begin(), PSE = Dst.end();
- PSI != PSE; ++PSI) {
- Engine.getWorkList()->enqueue(*PSI, calleeCtx->getCallSiteBlock(),
- calleeCtx->getIndex()+1);
+ PSI != PSE; ++PSI) {
+ unsigned Idx = calleeCtx->getIndex() + (ShouldRepeatCall ? 0 : 1);
+
+ Engine.getWorkList()->enqueue(*PSI, calleeCtx->getCallSiteBlock(), Idx);
}
}
}
@@ -427,10 +497,39 @@ namespace {
REGISTER_MAP_WITH_PROGRAMSTATE(DynamicDispatchBifurcationMap,
const MemRegion *, unsigned)
+REGISTER_TRAIT_WITH_PROGRAMSTATE(CTUDispatchBifurcation, bool)
+
+void ExprEngine::ctuBifurcate(const CallEvent &Call, const Decl *D,
+ NodeBuilder &Bldr, ExplodedNode *Pred,
+ ProgramStateRef State) {
+ ProgramStateRef ConservativeEvalState = nullptr;
+ if (Call.isForeign() && !isSecondPhaseCTU()) {
+ const auto IK = AMgr.options.getCTUPhase1Inlining();
+ const bool DoInline = IK == CTUPhase1InliningKind::All ||
+ (IK == CTUPhase1InliningKind::Small &&
+ isSmall(AMgr.getAnalysisDeclContext(D)));
+ if (DoInline) {
+ inlineCall(Engine.getWorkList(), Call, D, Bldr, Pred, State);
+ return;
+ }
+ const bool BState = State->get<CTUDispatchBifurcation>();
+ if (!BState) { // This is the first time we see this foreign function.
+ // Enqueue it to be analyzed in the second (ctu) phase.
+ inlineCall(Engine.getCTUWorkList(), Call, D, Bldr, Pred, State);
+ // Conservatively evaluate in the first phase.
+ ConservativeEvalState = State->set<CTUDispatchBifurcation>(true);
+ conservativeEvalCall(Call, Bldr, Pred, ConservativeEvalState);
+ } else {
+ conservativeEvalCall(Call, Bldr, Pred, State);
+ }
+ return;
+ }
+ inlineCall(Engine.getWorkList(), Call, D, Bldr, Pred, State);
+}
-bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D,
- NodeBuilder &Bldr, ExplodedNode *Pred,
- ProgramStateRef State) {
+void ExprEngine::inlineCall(WorkList *WList, const CallEvent &Call,
+ const Decl *D, NodeBuilder &Bldr,
+ ExplodedNode *Pred, ProgramStateRef State) {
assert(D);
const LocationContext *CurLC = Pred->getLocationContext();
@@ -465,7 +564,7 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D,
if (ExplodedNode *N = G.getNode(Loc, State, false, &isNew)) {
N->addPredecessor(Pred, G);
if (isNew)
- Engine.getWorkList()->enqueue(N);
+ WList->enqueue(N);
}
// If we decided to inline the call, the successor has been manually
@@ -475,11 +574,17 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D,
NumInlinedCalls++;
Engine.FunctionSummaries->bumpNumTimesInlined(D);
- // Mark the decl as visited.
- if (VisitedCallees)
- VisitedCallees->insert(D);
-
- return true;
+ // Do not mark as visited in the 2nd run (CTUWList), so the function will
+ // be visited as top-level, this way we won't loose reports in non-ctu
+ // mode. Considering the case when a function in a foreign TU calls back
+ // into the main TU.
+ // Note, during the 1st run, it doesn't matter if we mark the foreign
+ // functions as visited (or not) because they can never appear as a top level
+ // function in the main TU.
+ if (!isSecondPhaseCTU())
+ // Mark the decl as visited.
+ if (VisitedCallees)
+ VisitedCallees->insert(D);
}
static ProgramStateRef getInlineFailedState(ProgramStateRef State,
@@ -503,15 +608,14 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
// Get the call in its initial state. We use this as a template to perform
// all the checks.
CallEventManager &CEMgr = getStateManager().getCallEventManager();
- CallEventRef<> CallTemplate
- = CEMgr.getSimpleCall(CE, Pred->getState(), Pred->getLocationContext());
+ CallEventRef<> CallTemplate = CEMgr.getSimpleCall(
+ CE, Pred->getState(), Pred->getLocationContext(), getCFGElementRef());
// Evaluate the function call. We try each of the checkers
// to see if the can evaluate the function call.
ExplodedNodeSet dstCallEvaluated;
- for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end();
- I != E; ++I) {
- evalCall(dstCallEvaluated, *I, *CallTemplate);
+ for (ExplodedNode *N : dstPreVisit) {
+ evalCall(dstCallEvaluated, N, *CallTemplate);
}
// Finally, perform the post-condition check of the CallExpr and store
@@ -533,8 +637,7 @@ ProgramStateRef ExprEngine::finishArgumentConstruction(ProgramStateRef State,
const LocationContext *LC = Call.getLocationContext();
for (unsigned CallI = 0, CallN = Call.getNumArgs(); CallI != CallN; ++CallI) {
unsigned I = Call.getASTArgumentIndex(CallI);
- if (Optional<SVal> V =
- getObjectUnderConstruction(State, {E, I}, LC)) {
+ if (std::optional<SVal> V = getObjectUnderConstruction(State, {E, I}, LC)) {
SVal VV = *V;
(void)VV;
assert(cast<VarRegion>(VV.castAs<loc::MemRegionVal>().getRegion())
@@ -667,9 +770,9 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
SVal Target;
assert(RTC->getStmt() == Call.getOriginExpr());
EvalCallOptions CallOpts; // FIXME: We won't really need those.
- std::tie(State, Target) =
- handleConstructionContext(Call.getOriginExpr(), State, LCtx,
- RTC->getConstructionContext(), CallOpts);
+ std::tie(State, Target) = handleConstructionContext(
+ Call.getOriginExpr(), State, currBldrCtx, LCtx,
+ RTC->getConstructionContext(), CallOpts);
const MemRegion *TargetR = Target.getAsRegion();
assert(TargetR);
// Invalidate the region so that it didn't look uninitialized. If this is
@@ -697,7 +800,7 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
// Store the extent of the allocated object(s).
SVal ElementCount;
- if (const Expr *SizeExpr = CNE->getArraySize().getValueOr(nullptr)) {
+ if (const Expr *SizeExpr = CNE->getArraySize().value_or(nullptr)) {
ElementCount = State->getSVal(SizeExpr, LCtx);
} else {
ElementCount = svalBuilder.makeIntVal(1, /*IsUnsigned=*/true);
@@ -709,6 +812,11 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
svalBuilder.evalBinOp(State, BO_Mul, ElementCount, ElementSize,
svalBuilder.getArrayIndexType());
+ // FIXME: This line is to prevent a crash. For more details please check
+ // issue #56264.
+ if (Size.isUndef())
+ Size = UnknownVal();
+
State = setDynamicExtent(State, MR, Size.castAs<DefinedOrUnknownSVal>(),
svalBuilder);
} else {
@@ -726,7 +834,8 @@ void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr,
State = bindReturnValue(Call, Pred->getLocationContext(), State);
// And make the result node.
- Bldr.generateNode(Call.getProgramPoint(), State, Pred);
+ static SimpleProgramPointTag PT("ExprEngine", "Conservative eval call");
+ Bldr.generateNode(Call.getProgramPoint(false, &PT), State, Pred);
}
ExprEngine::CallInlinePolicy
@@ -760,13 +869,10 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
!Opts.MayInlineCXXAllocator)
return CIP_DisallowedOnce;
- // FIXME: We don't handle constructors or destructors for arrays properly.
- // Even once we do, we still need to be careful about implicitly-generated
- // initializers for array fields in default move/copy constructors.
- // We still allow construction into ElementRegion targets when they don't
- // represent array elements.
- if (CallOpts.IsArrayCtorOrDtor)
- return CIP_DisallowedOnce;
+ if (CallOpts.IsArrayCtorOrDtor) {
+ if (!shouldInlineArrayConstruction(Pred->getState(), CtorExpr, CurLC))
+ return CIP_DisallowedOnce;
+ }
// Inlining constructors requires including initializers in the CFG.
const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext();
@@ -782,7 +888,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors))
return CIP_DisallowedAlways;
- if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete) {
+ if (CtorExpr->getConstructionKind() == CXXConstructionKind::Complete) {
// If we don't handle temporary destructors, we shouldn't inline
// their constructors.
if (CallOpts.IsTemporaryCtorOrDtor &&
@@ -817,9 +923,12 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
assert(ADC->getCFGBuildOptions().AddImplicitDtors && "No CFG destructors");
(void)ADC;
- // FIXME: We don't handle constructors or destructors for arrays properly.
- if (CallOpts.IsArrayCtorOrDtor)
- return CIP_DisallowedOnce;
+ if (CallOpts.IsArrayCtorOrDtor) {
+ if (!shouldInlineArrayDestruction(getElementCountOfArrayBeingDestructed(
+ Call, Pred->getState(), svalBuilder))) {
+ return CIP_DisallowedOnce;
+ }
+ }
// Allow disabling temporary destructor inlining with a separate option.
if (CallOpts.IsTemporaryCtorOrDtor &&
@@ -834,7 +943,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
break;
}
case CE_CXXDeallocator:
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case CE_CXXAllocator:
if (Opts.MayInlineCXXAllocator)
break;
@@ -979,9 +1088,9 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D,
return false;
// Check if this function has been marked as non-inlinable.
- Optional<bool> MayInline = Engine.FunctionSummaries->mayInline(D);
- if (MayInline.hasValue()) {
- if (!MayInline.getValue())
+ std::optional<bool> MayInline = Engine.FunctionSummaries->mayInline(D);
+ if (MayInline) {
+ if (!*MayInline)
return false;
} else {
@@ -1002,7 +1111,7 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D,
CallInlinePolicy CIP = mayInlineCallKind(Call, Pred, Opts, CallOpts);
if (CIP != CIP_Allowed) {
if (CIP == CIP_DisallowedAlways) {
- assert(!MayInline.hasValue() || MayInline.getValue());
+ assert(!MayInline || *MayInline);
Engine.FunctionSummaries->markShouldNotInline(D);
}
return false;
@@ -1030,6 +1139,63 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D,
return true;
}
+bool ExprEngine::shouldInlineArrayConstruction(const ProgramStateRef State,
+ const CXXConstructExpr *CE,
+ const LocationContext *LCtx) {
+ if (!CE)
+ return false;
+
+ // FIXME: Handle other arrays types.
+ if (const auto *CAT = dyn_cast<ConstantArrayType>(CE->getType())) {
+ unsigned ArrSize = getContext().getConstantArrayElementCount(CAT);
+
+ // This might seem conter-intuitive at first glance, but the functions are
+ // closely related. Reasoning about destructors depends only on the type
+ // of the expression that initialized the memory region, which is the
+ // CXXConstructExpr. So to avoid code repetition, the work is delegated
+ // to the function that reasons about destructor inlining. Also note that
+ // if the constructors of the array elements are inlined, the destructors
+ // can also be inlined and if the destructors can be inline, it's safe to
+ // inline the constructors.
+ return shouldInlineArrayDestruction(ArrSize);
+ }
+
+ // Check if we're inside an ArrayInitLoopExpr, and it's sufficiently small.
+ if (auto Size = getPendingInitLoop(State, CE, LCtx))
+ return shouldInlineArrayDestruction(*Size);
+
+ return false;
+}
+
+bool ExprEngine::shouldInlineArrayDestruction(uint64_t Size) {
+
+ uint64_t maxAllowedSize = AMgr.options.maxBlockVisitOnPath;
+
+ // Declaring a 0 element array is also possible.
+ return Size <= maxAllowedSize && Size > 0;
+}
+
+bool ExprEngine::shouldRepeatCtorCall(ProgramStateRef State,
+ const CXXConstructExpr *E,
+ const LocationContext *LCtx) {
+
+ if (!E)
+ return false;
+
+ auto Ty = E->getType();
+
+ // FIXME: Handle non constant array types
+ if (const auto *CAT = dyn_cast<ConstantArrayType>(Ty)) {
+ unsigned Size = getContext().getConstantArrayElementCount(CAT);
+ return Size > getIndexOfElementToConstruct(State, E, LCtx);
+ }
+
+ if (auto Size = getPendingInitLoop(State, E, LCtx))
+ return Size > getIndexOfElementToConstruct(State, E, LCtx);
+
+ return false;
+}
+
static bool isTrivialObjectAssignment(const CallEvent &Call) {
const CXXInstanceCall *ICall = dyn_cast<CXXInstanceCall>(&Call);
if (!ICall)
@@ -1068,6 +1234,7 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred,
State = InlinedFailedState;
} else {
RuntimeDefinition RD = Call->getRuntimeDefinition();
+ Call->setForeign(RD.isForeign());
const Decl *D = RD.getDecl();
if (shouldInlineCall(*Call, D, Pred, CallOpts)) {
if (RD.mayHaveOtherDefinitions()) {
@@ -1085,14 +1252,17 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred,
return;
}
}
-
- // We are not bifurcating and we do have a Decl, so just inline.
- if (inlineCall(*Call, D, Bldr, Pred, State))
- return;
+ ctuBifurcate(*Call, D, Bldr, Pred, State);
+ return;
}
}
- // If we can't inline it, handle the return value and invalidate the regions.
+ // If we can't inline it, clean up the state traits used only if the function
+ // is inlined.
+ State = removeStateTraitsUsedForArrayEvaluation(
+ State, dyn_cast_or_null<CXXConstructExpr>(E), Call->getLocationContext());
+
+ // Also handle the return value and invalidate the regions.
conservativeEvalCall(*Call, Bldr, Pred, State);
}
@@ -1110,8 +1280,7 @@ void ExprEngine::BifurcateCall(const MemRegion *BifurReg,
if (BState) {
// If we are on "inline path", keep inlining if possible.
if (*BState == DynamicDispatchModeInlined)
- if (inlineCall(Call, D, Bldr, Pred, State))
- return;
+ ctuBifurcate(Call, D, Bldr, Pred, State);
// If inline failed, or we are on the path where we assume we
// don't have enough info about the receiver to inline, conjure the
// return value and invalidate the regions.
@@ -1124,7 +1293,7 @@ void ExprEngine::BifurcateCall(const MemRegion *BifurReg,
ProgramStateRef IState =
State->set<DynamicDispatchBifurcationMap>(BifurReg,
DynamicDispatchModeInlined);
- inlineCall(Call, D, Bldr, Pred, IState);
+ ctuBifurcate(Call, D, Bldr, Pred, IState);
ProgramStateRef NoIState =
State->set<DynamicDispatchBifurcationMap>(BifurReg,
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
index 5a55e81497b0..f075df3ab5e4 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -148,8 +148,8 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
CallEventManager &CEMgr = getStateManager().getCallEventManager();
- CallEventRef<ObjCMethodCall> Msg =
- CEMgr.getObjCMethodCall(ME, Pred->getState(), Pred->getLocationContext());
+ CallEventRef<ObjCMethodCall> Msg = CEMgr.getObjCMethodCall(
+ ME, Pred->getState(), Pred->getLocationContext(), getCFGElementRef());
// There are three cases for the receiver:
// (1) it is definitely nil,
@@ -167,19 +167,32 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
// intentionally drops coverage in order to prevent false alarms
// in the following scenario:
//
- // id result = [o someMethod]
- // if (result) {
- // if (!o) {
- // // <-- This program point should be unreachable because if o is nil
- // // it must the case that result is nil as well.
+ // id result = [o someMethod]
+ // if (result) {
+ // if (!o) {
+ // // <-- This program point should be unreachable because if o is nil
+ // // it must the case that result is nil as well.
+ // }
// }
- // }
//
- // We could avoid dropping coverage by performing an explicit case split
- // on each method call -- but this would get very expensive. An alternative
- // would be to introduce lazy constraints.
- // FIXME: This ignores many potential bugs (<rdar://problem/11733396>).
- // Revisit once we have lazier constraints.
+ // However, it also loses coverage of the nil path prematurely,
+ // leading to missed reports.
+ //
+ // It's possible to handle this by performing a state split on every call:
+ // explore the state where the receiver is non-nil, and independently
+ // explore the state where it's nil. But this is not only slow, but
+ // completely unwarranted. The mere presence of the message syntax in the code
+ // isn't sufficient evidence that nil is a realistic possibility.
+ //
+ // An ideal solution would be to add the following constraint that captures
+ // both possibilities without splitting the state:
+ //
+ // ($x == 0) => ($y == 0) (1)
+ //
+ // where in our case '$x' is the receiver symbol, '$y' is the returned symbol,
+ // and '=>' is logical implication. But RangeConstraintManager can't handle
+ // such constraints yet, so for now we go with a simpler, more restrictive
+ // constraint: $x != 0, from which (1) follows as a vacuous truth.
if (Msg->isInstanceMessage()) {
SVal recVal = Msg->getReceiverSVal();
if (!recVal.isUndef()) {
@@ -206,7 +219,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
ExplodedNodeSet dstPostCheckers;
getCheckerManager().runCheckersForObjCMessageNil(dstPostCheckers, Pred,
*Msg, *this);
- for (auto I : dstPostCheckers)
+ for (auto *I : dstPostCheckers)
finishArgumentConstruction(Dst, I, *Msg);
return;
}
@@ -270,7 +283,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
// If there were constructors called for object-type arguments, clean them up.
ExplodedNodeSet dstArgCleanup;
- for (auto I : dstEval)
+ for (auto *I : dstEval)
finishArgumentConstruction(dstArgCleanup, I, *Msg);
ExplodedNodeSet dstPostvisit;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
index 64fc32ea7554..86947b7929e9 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -27,6 +27,8 @@
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator_range.h"
@@ -57,6 +59,8 @@ using namespace ento;
namespace {
+class ArrowMap;
+
class HTMLDiagnostics : public PathDiagnosticConsumer {
PathDiagnosticConsumerOptions DiagOpts;
std::string Directory;
@@ -64,6 +68,7 @@ class HTMLDiagnostics : public PathDiagnosticConsumer {
bool noDir = false;
const Preprocessor &PP;
const bool SupportsCrossFileDiagnostics;
+ llvm::StringSet<> EmittedHashes;
public:
HTMLDiagnostics(PathDiagnosticConsumerOptions DiagOpts,
@@ -77,60 +82,93 @@ public:
void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
FilesMade *filesMade) override;
- StringRef getName() const override {
- return "HTMLDiagnostics";
- }
+ StringRef getName() const override { return "HTMLDiagnostics"; }
bool supportsCrossFileDiagnostics() const override {
return SupportsCrossFileDiagnostics;
}
- unsigned ProcessMacroPiece(raw_ostream &os,
- const PathDiagnosticMacroPiece& P,
+ unsigned ProcessMacroPiece(raw_ostream &os, const PathDiagnosticMacroPiece &P,
unsigned num);
+ unsigned ProcessControlFlowPiece(Rewriter &R, FileID BugFileID,
+ const PathDiagnosticControlFlowPiece &P,
+ unsigned Number);
+
void HandlePiece(Rewriter &R, FileID BugFileID, const PathDiagnosticPiece &P,
const std::vector<SourceRange> &PopUpRanges, unsigned num,
unsigned max);
- void HighlightRange(Rewriter& R, FileID BugFileID, SourceRange Range,
+ void HighlightRange(Rewriter &R, FileID BugFileID, SourceRange Range,
const char *HighlightStart = "<span class=\"mrange\">",
const char *HighlightEnd = "</span>");
- void ReportDiag(const PathDiagnostic& D,
- FilesMade *filesMade);
+ void ReportDiag(const PathDiagnostic &D, FilesMade *filesMade);
// Generate the full HTML report
- std::string GenerateHTML(const PathDiagnostic& D, Rewriter &R,
- const SourceManager& SMgr, const PathPieces& path,
+ std::string GenerateHTML(const PathDiagnostic &D, Rewriter &R,
+ const SourceManager &SMgr, const PathPieces &path,
const char *declName);
// Add HTML header/footers to file specified by FID
- void FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
- const SourceManager& SMgr, const PathPieces& path,
- FileID FID, const FileEntry *Entry, const char *declName);
+ void FinalizeHTML(const PathDiagnostic &D, Rewriter &R,
+ const SourceManager &SMgr, const PathPieces &path,
+ FileID FID, FileEntryRef Entry, const char *declName);
// Rewrite the file specified by FID with HTML formatting.
- void RewriteFile(Rewriter &R, const PathPieces& path, FileID FID);
+ void RewriteFile(Rewriter &R, const PathPieces &path, FileID FID);
+ PathGenerationScheme getGenerationScheme() const override {
+ return Everything;
+ }
private:
+ void addArrowSVGs(Rewriter &R, FileID BugFileID,
+ const ArrowMap &ArrowIndices);
+
/// \return Javascript for displaying shortcuts help;
StringRef showHelpJavascript();
/// \return Javascript for navigating the HTML report using j/k keys.
StringRef generateKeyboardNavigationJavascript();
+ /// \return Javascript for drawing control-flow arrows.
+ StringRef generateArrowDrawingJavascript();
+
/// \return JavaScript for an option to only show relevant lines.
- std::string showRelevantLinesJavascript(
- const PathDiagnostic &D, const PathPieces &path);
+ std::string showRelevantLinesJavascript(const PathDiagnostic &D,
+ const PathPieces &path);
/// Write executed lines from \p D in JSON format into \p os.
- void dumpCoverageData(const PathDiagnostic &D,
- const PathPieces &path,
+ void dumpCoverageData(const PathDiagnostic &D, const PathPieces &path,
llvm::raw_string_ostream &os);
};
+bool isArrowPiece(const PathDiagnosticPiece &P) {
+ return isa<PathDiagnosticControlFlowPiece>(P) && P.getString().empty();
+}
+
+unsigned getPathSizeWithoutArrows(const PathPieces &Path) {
+ unsigned TotalPieces = Path.size();
+ unsigned TotalArrowPieces = llvm::count_if(
+ Path, [](const PathDiagnosticPieceRef &P) { return isArrowPiece(*P); });
+ return TotalPieces - TotalArrowPieces;
+}
+
+class ArrowMap : public std::vector<unsigned> {
+ using Base = std::vector<unsigned>;
+
+public:
+ ArrowMap(unsigned Size) : Base(Size, 0) {}
+ unsigned getTotalNumberOfArrows() const { return at(0); }
+};
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const ArrowMap &Indices) {
+ OS << "[ ";
+ llvm::interleave(Indices, OS, ",");
+ return OS << " ]";
+}
+
} // namespace
void ento::createHTMLDiagnosticConsumer(
@@ -208,6 +246,18 @@ void HTMLDiagnostics::FlushDiagnosticsImpl(
ReportDiag(*Diag, filesMade);
}
+static llvm::SmallString<32> getIssueHash(const PathDiagnostic &D,
+ const Preprocessor &PP) {
+ SourceManager &SMgr = PP.getSourceManager();
+ PathDiagnosticLocation UPDLoc = D.getUniqueingLoc();
+ FullSourceLoc L(SMgr.getExpansionLoc(UPDLoc.isValid()
+ ? UPDLoc.asLocation()
+ : D.getLocation().asLocation()),
+ SMgr);
+ return getIssueHash(L, D.getCheckerName(), D.getBugType(),
+ D.getDeclWithIssue(), PP.getLangOpts());
+}
+
void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
FilesMade *filesMade) {
// Create the HTML directory if it is missing.
@@ -234,11 +284,6 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
// Create a new rewriter to generate HTML.
Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOpts());
- // The file for the first path element is considered the main report file, it
- // will usually be equivalent to SMgr.getMainFileID(); however, it might be a
- // header when -analyzer-opt-analyze-headers is used.
- FileID ReportFile = path.front()->getLocation().asLocation().getExpansionLoc().getFileID();
-
// Get the function/method name
SmallString<128> declName("unknown");
int offsetDecl = 0;
@@ -257,6 +302,17 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
}
}
+ SmallString<32> IssueHash = getIssueHash(D, PP);
+ auto [It, IsNew] = EmittedHashes.insert(IssueHash);
+ if (!IsNew) {
+ // We've already emitted a duplicate issue. It'll get overwritten anyway.
+ return;
+ }
+
+ // FIXME: This causes each file to be re-parsed and syntax-highlighted
+ // and macro-expanded separately for each report. We could cache such rewrites
+ // across all reports and only re-do the part that's actually different:
+ // the warning/note bubbles.
std::string report = GenerateHTML(D, R, SMgr, path, declName.c_str());
if (report.empty()) {
llvm::errs() << "warning: no diagnostics generated for main file.\n";
@@ -265,46 +321,52 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
// Create a path for the target HTML file.
int FD;
- SmallString<128> Model, ResultPath;
-
- if (!DiagOpts.ShouldWriteStableReportFilename) {
- llvm::sys::path::append(Model, Directory, "report-%%%%%%.html");
- if (std::error_code EC =
- llvm::sys::fs::make_absolute(Model)) {
- llvm::errs() << "warning: could not make '" << Model
- << "' absolute: " << EC.message() << '\n';
- return;
- }
- if (std::error_code EC = llvm::sys::fs::createUniqueFile(
- Model, FD, ResultPath, llvm::sys::fs::OF_Text)) {
- llvm::errs() << "warning: could not create file in '" << Directory
- << "': " << EC.message() << '\n';
- return;
- }
- } else {
- int i = 1;
- std::error_code EC;
- do {
- // Find a filename which is not already used
- const FileEntry* Entry = SMgr.getFileEntryForID(ReportFile);
- std::stringstream filename;
- Model = "";
- filename << "report-"
- << llvm::sys::path::filename(Entry->getName()).str()
- << "-" << declName.c_str()
- << "-" << offsetDecl
- << "-" << i << ".html";
- llvm::sys::path::append(Model, Directory,
- filename.str());
- EC = llvm::sys::fs::openFileForReadWrite(
- Model, FD, llvm::sys::fs::CD_CreateNew, llvm::sys::fs::OF_None);
- if (EC && EC != llvm::errc::file_exists) {
- llvm::errs() << "warning: could not create file '" << Model
- << "': " << EC.message() << '\n';
- return;
- }
- i++;
- } while (EC);
+
+ SmallString<128> FileNameStr;
+ llvm::raw_svector_ostream FileName(FileNameStr);
+ FileName << "report-";
+
+ // Historically, neither the stable report filename nor the unstable report
+ // filename were actually stable. That said, the stable report filename
+ // was more stable because it was mostly composed of information
+ // about the bug report instead of being completely random.
+ // Now both stable and unstable report filenames are in fact stable
+ // but the stable report filename is still more verbose.
+ if (DiagOpts.ShouldWriteVerboseReportFilename) {
+ // FIXME: This code relies on knowing what constitutes the issue hash.
+ // Otherwise deduplication won't work correctly.
+ FileID ReportFile =
+ path.back()->getLocation().asLocation().getExpansionLoc().getFileID();
+
+ OptionalFileEntryRef Entry = SMgr.getFileEntryRefForID(ReportFile);
+
+ FileName << llvm::sys::path::filename(Entry->getName()).str() << "-"
+ << declName.c_str() << "-" << offsetDecl << "-";
+ }
+
+ FileName << StringRef(IssueHash).substr(0, 6).str() << ".html";
+
+ SmallString<128> ResultPath;
+ llvm::sys::path::append(ResultPath, Directory, FileName.str());
+ if (std::error_code EC = llvm::sys::fs::make_absolute(ResultPath)) {
+ llvm::errs() << "warning: could not make '" << ResultPath
+ << "' absolute: " << EC.message() << '\n';
+ return;
+ }
+
+ if (std::error_code EC = llvm::sys::fs::openFileForReadWrite(
+ ResultPath, FD, llvm::sys::fs::CD_CreateNew,
+ llvm::sys::fs::OF_Text)) {
+ // Existence of the file corresponds to the situation where a different
+ // Clang instance has emitted a bug report with the same issue hash.
+ // This is an entirely normal situation that does not deserve a warning,
+ // as apart from hash collisions this can happen because the reports
+ // are in fact similar enough to be considered duplicates of each other.
+ if (EC != llvm::errc::file_exists) {
+ llvm::errs() << "warning: could not create file in '" << Directory
+ << "': " << EC.message() << '\n';
+ }
+ return;
}
llvm::raw_fd_ostream os(FD, true);
@@ -346,7 +408,7 @@ std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R,
os << "<div class=FileNav><a href=\"#File" << (I - 1)->getHashValue()
<< "\">&#x2190;</a></div>";
- os << "<h4 class=FileName>" << SMgr.getFileEntryForID(*I)->getName()
+ os << "<h4 class=FileName>" << SMgr.getFileEntryRefForID(*I)->getName()
<< "</h4>\n";
// Right nav arrow
@@ -360,7 +422,7 @@ std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R,
}
// Append files to the main report file in the order they appear in the path
- for (auto I : llvm::make_range(FileIDs.begin() + 1, FileIDs.end())) {
+ for (auto I : llvm::drop_begin(FileIDs)) {
std::string s;
llvm::raw_string_ostream os(s);
@@ -379,15 +441,15 @@ std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R,
// Add CSS, header, and footer.
FileID FID =
path.back()->getLocation().asLocation().getExpansionLoc().getFileID();
- const FileEntry* Entry = SMgr.getFileEntryForID(FID);
- FinalizeHTML(D, R, SMgr, path, FileIDs[0], Entry, declName);
+ OptionalFileEntryRef Entry = SMgr.getFileEntryRefForID(FID);
+ FinalizeHTML(D, R, SMgr, path, FileIDs[0], *Entry, declName);
std::string file;
llvm::raw_string_ostream os(file);
for (auto BI : *Buf)
os << BI;
- return os.str();
+ return file;
}
void HTMLDiagnostics::dumpCoverageData(
@@ -452,10 +514,11 @@ window.addEventListener("keydown", function (event) {
if (event.defaultPrevented) {
return;
}
- if (event.key == "S") {
+ // SHIFT + S
+ if (event.shiftKey && event.keyCode == 83) {
var checked = document.getElementsByName("showCounterexample")[0].checked;
filterCounterexample(!checked);
- document.getElementsByName("showCounterexample")[0].checked = !checked;
+ document.getElementsByName("showCounterexample")[0].click();
} else {
return;
}
@@ -475,22 +538,28 @@ document.addEventListener("DOMContentLoaded", function() {
<label for="showCounterexample">
Show only relevant lines
</label>
+ <input type="checkbox" name="showArrows"
+ id="showArrows" style="margin-left: 10px" />
+ <label for="showArrows">
+ Show control flow arrows
+ </label>
</form>
)<<<";
- return os.str();
+ return s;
}
-void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
- const SourceManager& SMgr, const PathPieces& path, FileID FID,
- const FileEntry *Entry, const char *declName) {
+void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic &D, Rewriter &R,
+ const SourceManager &SMgr,
+ const PathPieces &path, FileID FID,
+ FileEntryRef Entry, const char *declName) {
// This is a cludge; basically we want to append either the full
// working directory if we have no directory information. This is
// a work in progress.
llvm::SmallString<0> DirName;
- if (llvm::sys::path::is_relative(Entry->getName())) {
+ if (llvm::sys::path::is_relative(Entry.getName())) {
llvm::sys::fs::current_path(DirName);
DirName += '/';
}
@@ -503,6 +572,9 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
generateKeyboardNavigationJavascript());
+ R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
+ generateArrowDrawingJavascript());
+
// Checkbox and javascript for filtering the output to the counterexample.
R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
showRelevantLinesJavascript(D, path));
@@ -516,7 +588,7 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
<< "<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n"
"<tr><td class=\"rowname\">File:</td><td>"
<< html::EscapeText(DirName)
- << html::EscapeText(Entry->getName())
+ << html::EscapeText(Entry.getName())
<< "</td></tr>\n<tr><td class=\"rowname\">Warning:</td><td>"
"<a href=\"#EndPath\">line "
<< LineNumber
@@ -533,19 +605,19 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
P->getLocation().asLocation().getExpansionLineNumber();
int ColumnNumber =
P->getLocation().asLocation().getExpansionColumnNumber();
+ ++NumExtraPieces;
os << "<tr><td class=\"rowname\">Note:</td><td>"
<< "<a href=\"#Note" << NumExtraPieces << "\">line "
<< LineNumber << ", column " << ColumnNumber << "</a><br />"
<< P->getString() << "</td></tr>";
- ++NumExtraPieces;
}
}
// Output any other meta data.
- for (PathDiagnostic::meta_iterator I = D.meta_begin(), E = D.meta_end();
- I != E; ++I) {
- os << "<tr><td></td><td>" << html::EscapeText(*I) << "</td></tr>\n";
+ for (const std::string &Metadata :
+ llvm::make_range(D.meta_begin(), D.meta_end())) {
+ os << "<tr><td></td><td>" << html::EscapeText(Metadata) << "</td></tr>\n";
}
os << R"<<<(
@@ -570,6 +642,7 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
<a href="#" onclick="toggleHelp(); return false;">Close</a>
</div>
)<<<";
+
R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str());
}
@@ -591,21 +664,18 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
? UPDLoc.asLocation()
: D.getLocation().asLocation()),
SMgr);
- const Decl *DeclWithIssue = D.getDeclWithIssue();
StringRef BugCategory = D.getCategory();
if (!BugCategory.empty())
os << "\n<!-- BUGCATEGORY " << BugCategory << " -->\n";
- os << "\n<!-- BUGFILE " << DirName << Entry->getName() << " -->\n";
+ os << "\n<!-- BUGFILE " << DirName << Entry.getName() << " -->\n";
- os << "\n<!-- FILENAME " << llvm::sys::path::filename(Entry->getName()) << " -->\n";
+ os << "\n<!-- FILENAME " << llvm::sys::path::filename(Entry.getName()) << " -->\n";
os << "\n<!-- FUNCTIONNAME " << declName << " -->\n";
- os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT "
- << getIssueHash(L, D.getCheckerName(), D.getBugType(), DeclWithIssue,
- PP.getLangOpts())
+ os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " << getIssueHash(D, PP)
<< " -->\n";
os << "\n<!-- BUGLINE "
@@ -616,7 +686,7 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
<< ColumnNumber
<< " -->\n";
- os << "\n<!-- BUGPATHLENGTH " << path.size() << " -->\n";
+ os << "\n<!-- BUGPATHLENGTH " << getPathSizeWithoutArrows(path) << " -->\n";
// Mark the end of the tags.
os << "\n<!-- BUGMETAEND -->\n";
@@ -625,7 +695,7 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str());
}
- html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry->getName());
+ html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry.getName());
}
StringRef HTMLDiagnostics::showHelpJavascript() {
@@ -695,8 +765,7 @@ static void HandlePopUpPieceEndTag(Rewriter &R,
Out << "</div></td><td>" << Piece.getString() << "</td></tr>";
// If no report made at this range mark the variable and add the end tags.
- if (std::find(PopUpRanges.begin(), PopUpRanges.end(), Range) ==
- PopUpRanges.end()) {
+ if (!llvm::is_contained(PopUpRanges, Range)) {
// Store that we create a report at this range.
PopUpRanges.push_back(Range);
@@ -711,30 +780,33 @@ static void HandlePopUpPieceEndTag(Rewriter &R,
}
}
-void HTMLDiagnostics::RewriteFile(Rewriter &R,
- const PathPieces& path, FileID FID) {
+void HTMLDiagnostics::RewriteFile(Rewriter &R, const PathPieces &path,
+ FileID FID) {
+
// Process the path.
// Maintain the counts of extra note pieces separately.
- unsigned TotalPieces = path.size();
- unsigned TotalNotePieces = std::count_if(
- path.begin(), path.end(), [](const PathDiagnosticPieceRef &p) {
+ unsigned TotalPieces = getPathSizeWithoutArrows(path);
+ unsigned TotalNotePieces =
+ llvm::count_if(path, [](const PathDiagnosticPieceRef &p) {
return isa<PathDiagnosticNotePiece>(*p);
});
- unsigned PopUpPieceCount = std::count_if(
- path.begin(), path.end(), [](const PathDiagnosticPieceRef &p) {
+ unsigned PopUpPieceCount =
+ llvm::count_if(path, [](const PathDiagnosticPieceRef &p) {
return isa<PathDiagnosticPopUpPiece>(*p);
});
unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount;
unsigned NumRegularPieces = TotalRegularPieces;
unsigned NumNotePieces = TotalNotePieces;
+ unsigned NumberOfArrows = 0;
// Stores the count of the regular piece indices.
std::map<int, int> IndexMap;
+ ArrowMap ArrowIndices(TotalRegularPieces + 1);
// Stores the different ranges where we have reported something.
std::vector<SourceRange> PopUpRanges;
- for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) {
- const auto &Piece = *I->get();
+ for (const PathDiagnosticPieceRef &I : llvm::reverse(path)) {
+ const auto &Piece = *I.get();
if (isa<PathDiagnosticPopUpPiece>(Piece)) {
++IndexMap[NumRegularPieces];
@@ -744,18 +816,40 @@ void HTMLDiagnostics::RewriteFile(Rewriter &R,
// as a separate pass through the piece list.
HandlePiece(R, FID, Piece, PopUpRanges, NumNotePieces, TotalNotePieces);
--NumNotePieces;
+
+ } else if (isArrowPiece(Piece)) {
+ NumberOfArrows = ProcessControlFlowPiece(
+ R, FID, cast<PathDiagnosticControlFlowPiece>(Piece), NumberOfArrows);
+ ArrowIndices[NumRegularPieces] = NumberOfArrows;
+
} else {
HandlePiece(R, FID, Piece, PopUpRanges, NumRegularPieces,
TotalRegularPieces);
--NumRegularPieces;
+ ArrowIndices[NumRegularPieces] = ArrowIndices[NumRegularPieces + 1];
}
}
+ ArrowIndices[0] = NumberOfArrows;
+
+ // At this point ArrowIndices represent the following data structure:
+ // [a_0, a_1, ..., a_N]
+ // where N is the number of events in the path.
+ //
+ // Then for every event with index i \in [0, N - 1], we can say that
+ // arrows with indices \in [a_(i+1), a_i) correspond to that event.
+ // We can say that because arrows with these indices appeared in the
+ // path in between the i-th and the (i+1)-th events.
+ assert(ArrowIndices.back() == 0 &&
+ "No arrows should be after the last event");
+ // This assertion also guarantees that all indices in are <= NumberOfArrows.
+ assert(llvm::is_sorted(ArrowIndices, std::greater<unsigned>()) &&
+ "Incorrect arrow indices map");
// Secondary indexing if we are having multiple pop-ups between two notes.
// (e.g. [(13) 'a' is 'true']; [(13.1) 'b' is 'false']; [(13.2) 'c' is...)
NumRegularPieces = TotalRegularPieces;
- for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) {
- const auto &Piece = *I->get();
+ for (const PathDiagnosticPieceRef &I : llvm::reverse(path)) {
+ const auto &Piece = *I.get();
if (const auto *PopUpP = dyn_cast<PathDiagnosticPopUpPiece>(&Piece)) {
int PopUpPieceIndex = IndexMap[NumRegularPieces];
@@ -771,7 +865,7 @@ void HTMLDiagnostics::RewriteFile(Rewriter &R,
if (PopUpPieceIndex > 0)
--IndexMap[NumRegularPieces];
- } else if (!isa<PathDiagnosticNotePiece>(Piece)) {
+ } else if (!isa<PathDiagnosticNotePiece>(Piece) && !isArrowPiece(Piece)) {
--NumRegularPieces;
}
}
@@ -783,6 +877,8 @@ void HTMLDiagnostics::RewriteFile(Rewriter &R,
html::EscapeText(R, FID);
html::AddLineNumbers(R, FID);
+ addArrowSVGs(R, FID, ArrowIndices);
+
// If we have a preprocessor, relex the file and syntax highlight.
// We might not have a preprocessor if we come from a deserialized AST file,
// for example.
@@ -1007,8 +1103,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID,
ArrayRef<SourceRange> Ranges = P.getRanges();
for (const auto &Range : Ranges) {
// If we have already highlighted the range as a pop-up there is no work.
- if (std::find(PopUpRanges.begin(), PopUpRanges.end(), Range) !=
- PopUpRanges.end())
+ if (llvm::is_contained(PopUpRanges, Range))
continue;
HighlightRange(R, LPosInfo.first, Range);
@@ -1049,6 +1144,104 @@ unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,
return num;
}
+void HTMLDiagnostics::addArrowSVGs(Rewriter &R, FileID BugFileID,
+ const ArrowMap &ArrowIndices) {
+ std::string S;
+ llvm::raw_string_ostream OS(S);
+
+ OS << R"<<<(
+<style type="text/css">
+ svg {
+ position:absolute;
+ top:0;
+ left:0;
+ height:100%;
+ width:100%;
+ pointer-events: none;
+ overflow: visible
+ }
+ .arrow {
+ stroke-opacity: 0.2;
+ stroke-width: 1;
+ marker-end: url(#arrowhead);
+ }
+
+ .arrow.selected {
+ stroke-opacity: 0.6;
+ stroke-width: 2;
+ marker-end: url(#arrowheadSelected);
+ }
+
+ .arrowhead {
+ orient: auto;
+ stroke: none;
+ opacity: 0.6;
+ fill: blue;
+ }
+</style>
+<svg xmlns="http://www.w3.org/2000/svg">
+ <defs>
+ <marker id="arrowheadSelected" class="arrowhead" opacity="0.6"
+ viewBox="0 0 10 10" refX="3" refY="5"
+ markerWidth="4" markerHeight="4">
+ <path d="M 0 0 L 10 5 L 0 10 z" />
+ </marker>
+ <marker id="arrowhead" class="arrowhead" opacity="0.2"
+ viewBox="0 0 10 10" refX="3" refY="5"
+ markerWidth="4" markerHeight="4">
+ <path d="M 0 0 L 10 5 L 0 10 z" />
+ </marker>
+ </defs>
+ <g id="arrows" fill="none" stroke="blue" visibility="hidden">
+)<<<";
+
+ for (unsigned Index : llvm::seq(0u, ArrowIndices.getTotalNumberOfArrows())) {
+ OS << " <path class=\"arrow\" id=\"arrow" << Index << "\"/>\n";
+ }
+
+ OS << R"<<<(
+ </g>
+</svg>
+<script type='text/javascript'>
+const arrowIndices = )<<<";
+
+ OS << ArrowIndices << "\n</script>\n";
+
+ R.InsertTextBefore(R.getSourceMgr().getLocForStartOfFile(BugFileID),
+ OS.str());
+}
+
+std::string getSpanBeginForControl(const char *ClassName, unsigned Index) {
+ std::string Result;
+ llvm::raw_string_ostream OS(Result);
+ OS << "<span id=\"" << ClassName << Index << "\">";
+ return Result;
+}
+
+std::string getSpanBeginForControlStart(unsigned Index) {
+ return getSpanBeginForControl("start", Index);
+}
+
+std::string getSpanBeginForControlEnd(unsigned Index) {
+ return getSpanBeginForControl("end", Index);
+}
+
+unsigned HTMLDiagnostics::ProcessControlFlowPiece(
+ Rewriter &R, FileID BugFileID, const PathDiagnosticControlFlowPiece &P,
+ unsigned Number) {
+ for (const PathDiagnosticLocationPair &LPair : P) {
+ std::string Start = getSpanBeginForControlStart(Number),
+ End = getSpanBeginForControlEnd(Number++);
+
+ HighlightRange(R, BugFileID, LPair.getStart().asRange().getBegin(),
+ Start.c_str());
+ HighlightRange(R, BugFileID, LPair.getEnd().asRange().getBegin(),
+ End.c_str());
+ }
+
+ return Number;
+}
+
void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID,
SourceRange Range,
const char *HighlightStart,
@@ -1109,7 +1302,7 @@ document.addEventListener("DOMContentLoaded", function() {
});
var findNum = function() {
- var s = document.querySelector(".selected");
+ var s = document.querySelector(".msg.selected");
if (!s || s.id == "EndPath") {
return 0;
}
@@ -1117,14 +1310,32 @@ var findNum = function() {
return out;
};
+var classListAdd = function(el, theClass) {
+ if(!el.className.baseVal)
+ el.className += " " + theClass;
+ else
+ el.className.baseVal += " " + theClass;
+};
+
+var classListRemove = function(el, theClass) {
+ var className = (!el.className.baseVal) ?
+ el.className : el.className.baseVal;
+ className = className.replace(" " + theClass, "");
+ if(!el.className.baseVal)
+ el.className = className;
+ else
+ el.className.baseVal = className;
+};
+
var scrollTo = function(el) {
querySelectorAllArray(".selected").forEach(function(s) {
- s.classList.remove("selected");
+ classListRemove(s, "selected");
});
- el.classList.add("selected");
+ classListAdd(el, "selected");
window.scrollBy(0, el.getBoundingClientRect().top -
(window.innerHeight / 2));
-}
+ highlightArrowsForSelectedEvent();
+};
var move = function(num, up, numItems) {
if (num == 1 && up || num == numItems - 1 && !up) {
@@ -1159,9 +1370,11 @@ window.addEventListener("keydown", function (event) {
if (event.defaultPrevented) {
return;
}
- if (event.key == "j") {
+ // key 'j'
+ if (event.keyCode == 74) {
navigateTo(/*up=*/false);
- } else if (event.key == "k") {
+ // key 'k'
+ } else if (event.keyCode == 75) {
navigateTo(/*up=*/true);
} else {
return;
@@ -1171,3 +1384,258 @@ window.addEventListener("keydown", function (event) {
</script>
)<<<";
}
+
+StringRef HTMLDiagnostics::generateArrowDrawingJavascript() {
+ return R"<<<(
+<script type='text/javascript'>
+// Return range of numbers from a range [lower, upper).
+function range(lower, upper) {
+ var array = [];
+ for (var i = lower; i <= upper; ++i) {
+ array.push(i);
+ }
+ return array;
+}
+
+var getRelatedArrowIndices = function(pathId) {
+ // HTML numeration of events is a bit different than it is in the path.
+ // Everything is rotated one step to the right, so the last element
+ // (error diagnostic) has index 0.
+ if (pathId == 0) {
+ // arrowIndices has at least 2 elements
+ pathId = arrowIndices.length - 1;
+ }
+
+ return range(arrowIndices[pathId], arrowIndices[pathId - 1]);
+}
+
+var highlightArrowsForSelectedEvent = function() {
+ const selectedNum = findNum();
+ const arrowIndicesToHighlight = getRelatedArrowIndices(selectedNum);
+ arrowIndicesToHighlight.forEach((index) => {
+ var arrow = document.querySelector("#arrow" + index);
+ if(arrow) {
+ classListAdd(arrow, "selected")
+ }
+ });
+}
+
+var getAbsoluteBoundingRect = function(element) {
+ const relative = element.getBoundingClientRect();
+ return {
+ left: relative.left + window.pageXOffset,
+ right: relative.right + window.pageXOffset,
+ top: relative.top + window.pageYOffset,
+ bottom: relative.bottom + window.pageYOffset,
+ height: relative.height,
+ width: relative.width
+ };
+}
+
+var drawArrow = function(index) {
+ // This function is based on the great answer from SO:
+ // https://stackoverflow.com/a/39575674/11582326
+ var start = document.querySelector("#start" + index);
+ var end = document.querySelector("#end" + index);
+ var arrow = document.querySelector("#arrow" + index);
+
+ var startRect = getAbsoluteBoundingRect(start);
+ var endRect = getAbsoluteBoundingRect(end);
+
+ // It is an arrow from a token to itself, no need to visualize it.
+ if (startRect.top == endRect.top &&
+ startRect.left == endRect.left)
+ return;
+
+ // Each arrow is a very simple Bézier curve, with two nodes and
+ // two handles. So, we need to calculate four points in the window:
+ // * start node
+ var posStart = { x: 0, y: 0 };
+ // * end node
+ var posEnd = { x: 0, y: 0 };
+ // * handle for the start node
+ var startHandle = { x: 0, y: 0 };
+ // * handle for the end node
+ var endHandle = { x: 0, y: 0 };
+ // One can visualize it as follows:
+ //
+ // start handle
+ // /
+ // X"""_.-""""X
+ // .' \
+ // / start node
+ // |
+ // |
+ // | end node
+ // \ /
+ // `->X
+ // X-'
+ // \
+ // end handle
+ //
+ // NOTE: (0, 0) is the top left corner of the window.
+
+ // We have 3 similar, but still different scenarios to cover:
+ //
+ // 1. Two tokens on different lines.
+ // -xxx
+ // /
+ // \
+ // -> xxx
+ // In this situation, we draw arrow on the left curving to the left.
+ // 2. Two tokens on the same line, and the destination is on the right.
+ // ____
+ // / \
+ // / V
+ // xxx xxx
+ // In this situation, we draw arrow above curving upwards.
+ // 3. Two tokens on the same line, and the destination is on the left.
+ // xxx xxx
+ // ^ /
+ // \____/
+ // In this situation, we draw arrow below curving downwards.
+ const onDifferentLines = startRect.top <= endRect.top - 5 ||
+ startRect.top >= endRect.top + 5;
+ const leftToRight = startRect.left < endRect.left;
+
+ // NOTE: various magic constants are chosen empirically for
+ // better positioning and look
+ if (onDifferentLines) {
+ // Case #1
+ const topToBottom = startRect.top < endRect.top;
+ posStart.x = startRect.left - 1;
+ // We don't want to start it at the top left corner of the token,
+ // it doesn't feel like this is where the arrow comes from.
+ // For this reason, we start it in the middle of the left side
+ // of the token.
+ posStart.y = startRect.top + startRect.height / 2;
+
+ // End node has arrow head and we give it a bit more space.
+ posEnd.x = endRect.left - 4;
+ posEnd.y = endRect.top;
+
+ // Utility object with x and y offsets for handles.
+ var curvature = {
+ // We want bottom-to-top arrow to curve a bit more, so it doesn't
+ // overlap much with top-to-bottom curves (much more frequent).
+ x: topToBottom ? 15 : 25,
+ y: Math.min((posEnd.y - posStart.y) / 3, 10)
+ }
+
+ // When destination is on the different line, we can make a
+ // curvier arrow because we have space for it.
+ // So, instead of using
+ //
+ // startHandle.x = posStart.x - curvature.x
+ // endHandle.x = posEnd.x - curvature.x
+ //
+ // We use the leftmost of these two values for both handles.
+ startHandle.x = Math.min(posStart.x, posEnd.x) - curvature.x;
+ endHandle.x = startHandle.x;
+
+ // Curving downwards from the start node...
+ startHandle.y = posStart.y + curvature.y;
+ // ... and upwards from the end node.
+ endHandle.y = posEnd.y - curvature.y;
+
+ } else if (leftToRight) {
+ // Case #2
+ // Starting from the top right corner...
+ posStart.x = startRect.right - 1;
+ posStart.y = startRect.top;
+
+ // ...and ending at the top left corner of the end token.
+ posEnd.x = endRect.left + 1;
+ posEnd.y = endRect.top - 1;
+
+ // Utility object with x and y offsets for handles.
+ var curvature = {
+ x: Math.min((posEnd.x - posStart.x) / 3, 15),
+ y: 5
+ }
+
+ // Curving to the right...
+ startHandle.x = posStart.x + curvature.x;
+ // ... and upwards from the start node.
+ startHandle.y = posStart.y - curvature.y;
+
+ // And to the left...
+ endHandle.x = posEnd.x - curvature.x;
+ // ... and upwards from the end node.
+ endHandle.y = posEnd.y - curvature.y;
+
+ } else {
+ // Case #3
+ // Starting from the bottom right corner...
+ posStart.x = startRect.right;
+ posStart.y = startRect.bottom;
+
+ // ...and ending also at the bottom right corner, but of the end token.
+ posEnd.x = endRect.right - 1;
+ posEnd.y = endRect.bottom + 1;
+
+ // Utility object with x and y offsets for handles.
+ var curvature = {
+ x: Math.min((posStart.x - posEnd.x) / 3, 15),
+ y: 5
+ }
+
+ // Curving to the left...
+ startHandle.x = posStart.x - curvature.x;
+ // ... and downwards from the start node.
+ startHandle.y = posStart.y + curvature.y;
+
+ // And to the right...
+ endHandle.x = posEnd.x + curvature.x;
+ // ... and downwards from the end node.
+ endHandle.y = posEnd.y + curvature.y;
+ }
+
+ // Put it all together into a path.
+ // More information on the format:
+ // https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
+ var pathStr = "M" + posStart.x + "," + posStart.y + " " +
+ "C" + startHandle.x + "," + startHandle.y + " " +
+ endHandle.x + "," + endHandle.y + " " +
+ posEnd.x + "," + posEnd.y;
+
+ arrow.setAttribute("d", pathStr);
+};
+
+var drawArrows = function() {
+ const numOfArrows = document.querySelectorAll("path[id^=arrow]").length;
+ for (var i = 0; i < numOfArrows; ++i) {
+ drawArrow(i);
+ }
+}
+
+var toggleArrows = function(event) {
+ const arrows = document.querySelector("#arrows");
+ if (event.target.checked) {
+ arrows.setAttribute("visibility", "visible");
+ } else {
+ arrows.setAttribute("visibility", "hidden");
+ }
+}
+
+window.addEventListener("resize", drawArrows);
+document.addEventListener("DOMContentLoaded", function() {
+ // Whenever we show invocation, locations change, i.e. we
+ // need to redraw arrows.
+ document
+ .querySelector('input[id="showinvocation"]')
+ .addEventListener("click", drawArrows);
+ // Hiding irrelevant lines also should cause arrow rerender.
+ document
+ .querySelector('input[name="showCounterexample"]')
+ .addEventListener("change", drawArrows);
+ document
+ .querySelector('input[name="showArrows"]')
+ .addEventListener("change", toggleArrows);
+ drawArrows();
+ // Default highlighting for the last event.
+ highlightArrowsForSelectedEvent();
+});
+</script>
+ )<<<";
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
index e5f4e9ea30c9..a80352816be6 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -24,6 +25,7 @@ using namespace clang::ast_matchers;
static const int MAXIMUM_STEP_UNROLLED = 128;
+namespace {
struct LoopState {
private:
enum Kind { Normal, Unrolled } K;
@@ -56,6 +58,7 @@ public:
ID.AddInteger(maxStep);
}
};
+} // namespace
// The tracked stack of loops. The stack indicates that which loops the
// simulated element contained by. The loops are marked depending if we decided
@@ -69,7 +72,7 @@ namespace clang {
namespace ento {
static bool isLoopStmt(const Stmt *S) {
- return S && (isa<ForStmt>(S) || isa<WhileStmt>(S) || isa<DoStmt>(S));
+ return isa_and_nonnull<ForStmt, WhileStmt, DoStmt>(S);
}
ProgramStateRef processLoopEnd(const Stmt *LoopStmt, ProgramStateRef State) {
@@ -175,7 +178,7 @@ static bool isCapturedByReference(ExplodedNode *N, const DeclRefExpr *DR) {
const CXXRecordDecl *LambdaCXXRec = MD->getParent();
// Lookup the fields of the lambda
- llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
FieldDecl *LambdaThisCaptureField;
LambdaCXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField);
@@ -264,8 +267,8 @@ bool shouldCompletelyUnroll(const Stmt *LoopStmt, ASTContext &ASTCtx,
Matches[0].getNodeAs<IntegerLiteral>("initNum")->getValue();
auto CondOp = Matches[0].getNodeAs<BinaryOperator>("conditionOperator");
if (InitNum.getBitWidth() != BoundNum.getBitWidth()) {
- InitNum = InitNum.zextOrSelf(BoundNum.getBitWidth());
- BoundNum = BoundNum.zextOrSelf(InitNum.getBitWidth());
+ InitNum = InitNum.zext(BoundNum.getBitWidth());
+ BoundNum = BoundNum.zext(InitNum.getBitWidth());
}
if (CondOp->getOpcode() == BO_GE || CondOp->getOpcode() == BO_LE)
@@ -284,7 +287,7 @@ bool madeNewBranch(ExplodedNode *N, const Stmt *LoopStmt) {
return true;
ProgramPoint P = N->getLocation();
- if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>())
+ if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>())
S = BE->getBlock()->getTerminatorStmt();
if (S == LoopStmt)
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
index 47e34dd84b9a..9e4280176062 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
@@ -35,6 +35,8 @@ static const Expr *getLoopCondition(const Stmt *LoopStmt) {
return cast<WhileStmt>(LoopStmt)->getCond();
case Stmt::DoStmtClass:
return cast<DoStmt>(LoopStmt)->getCond();
+ case Stmt::CXXForRangeStmtClass:
+ return cast<CXXForRangeStmt>(LoopStmt)->getCond();
}
}
@@ -45,8 +47,7 @@ ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState,
const LocationContext *LCtx,
unsigned BlockCount, const Stmt *LoopStmt) {
- assert(isa<ForStmt>(LoopStmt) || isa<WhileStmt>(LoopStmt) ||
- isa<DoStmt>(LoopStmt));
+ assert((isa<ForStmt, WhileStmt, DoStmt, CXXForRangeStmt>(LoopStmt)));
// Invalidate values in the current state.
// TODO Make this more conservative by only invalidating values that might
@@ -85,7 +86,7 @@ ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState,
// pointer should remain unchanged. Ignore static methods, since they do not
// have 'this' pointers.
const CXXMethodDecl *CXXMD = dyn_cast<CXXMethodDecl>(STC->getDecl());
- if (CXXMD && !CXXMD->isStatic()) {
+ if (CXXMD && CXXMD->isImplicitObjectMemberFunction()) {
const CXXThisRegion *ThisR =
MRMgr.getCXXThisRegion(CXXMD->getThisType(), STC);
ITraits.setTrait(ThisR,
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
index bd725ee9eaa3..16db6b249dc9 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -28,17 +28,18 @@
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
+#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CheckedArithmetic.h"
@@ -50,6 +51,7 @@
#include <cstdint>
#include <functional>
#include <iterator>
+#include <optional>
#include <string>
#include <tuple>
#include <utility>
@@ -72,8 +74,7 @@ RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1,
auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos));
if (!R) {
- R = A.Allocate<RegionTy>();
- new (R) RegionTy(arg1, superRegion);
+ R = new (A) RegionTy(arg1, superRegion);
Regions.InsertNode(R, InsertPos);
}
@@ -89,8 +90,7 @@ RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2,
auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos));
if (!R) {
- R = A.Allocate<RegionTy>();
- new (R) RegionTy(arg1, arg2, superRegion);
+ R = new (A) RegionTy(arg1, arg2, superRegion);
Regions.InsertNode(R, InsertPos);
}
@@ -108,8 +108,7 @@ RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2,
auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos));
if (!R) {
- R = A.Allocate<RegionTy>();
- new (R) RegionTy(arg1, arg2, arg3, superRegion);
+ R = new (A) RegionTy(arg1, arg2, arg3, superRegion);
Regions.InsertNode(R, InsertPos);
}
@@ -160,8 +159,22 @@ const StackFrameContext *VarRegion::getStackFrame() const {
return SSR ? SSR->getStackFrame() : nullptr;
}
+const StackFrameContext *
+CXXLifetimeExtendedObjectRegion::getStackFrame() const {
+ const auto *SSR = dyn_cast<StackSpaceRegion>(getMemorySpace());
+ return SSR ? SSR->getStackFrame() : nullptr;
+}
+
+const StackFrameContext *CXXTempObjectRegion::getStackFrame() const {
+ assert(isa<StackSpaceRegion>(getMemorySpace()) &&
+ "A temporary object can only be allocated on the stack");
+ return cast<StackSpaceRegion>(getMemorySpace())->getStackFrame();
+}
+
ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const SubRegion *sReg)
- : DeclRegion(sReg, ObjCIvarRegionKind), IVD(ivd) {}
+ : DeclRegion(sReg, ObjCIvarRegionKind), IVD(ivd) {
+ assert(IVD);
+}
const ObjCIvarDecl *ObjCIvarRegion::getDecl() const { return IVD; }
@@ -389,6 +402,20 @@ void CXXTempObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const {
ProfileRegion(ID, Ex, getSuperRegion());
}
+void CXXLifetimeExtendedObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID,
+ const Expr *E,
+ const ValueDecl *D,
+ const MemRegion *sReg) {
+ ID.AddPointer(E);
+ ID.AddPointer(D);
+ ID.AddPointer(sReg);
+}
+
+void CXXLifetimeExtendedObjectRegion::Profile(
+ llvm::FoldingSetNodeID &ID) const {
+ ProfileRegion(ID, Ex, ExD, getSuperRegion());
+}
+
void CXXBaseObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID,
const CXXRecordDecl *RD,
bool IsVirtual,
@@ -443,7 +470,7 @@ std::string MemRegion::getString() const {
std::string s;
llvm::raw_string_ostream os(s);
dumpToStream(os);
- return os.str();
+ return s;
}
void MemRegion::dumpToStream(raw_ostream &os) const {
@@ -465,11 +492,9 @@ void BlockCodeRegion::dumpToStream(raw_ostream &os) const {
void BlockDataRegion::dumpToStream(raw_ostream &os) const {
os << "block_data{" << BC;
os << "; ";
- for (BlockDataRegion::referenced_vars_iterator
- I = referenced_vars_begin(),
- E = referenced_vars_end(); I != E; ++I)
- os << "(" << I.getCapturedRegion() << "<-" <<
- I.getOriginalRegion() << ") ";
+ for (auto Var : referenced_vars())
+ os << "(" << Var.getCapturedRegion() << "<-" << Var.getOriginalRegion()
+ << ") ";
os << '}';
}
@@ -479,7 +504,17 @@ void CompoundLiteralRegion::dumpToStream(raw_ostream &os) const {
}
void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const {
- os << "temp_object{" << getValueType().getAsString() << ", "
+ os << "temp_object{" << getValueType() << ", "
+ << "S" << Ex->getID(getContext()) << '}';
+}
+
+void CXXLifetimeExtendedObjectRegion::dumpToStream(raw_ostream &os) const {
+ os << "lifetime_extended_object{" << getValueType() << ", ";
+ if (const IdentifierInfo *ID = ExD->getIdentifier())
+ os << ID->getName();
+ else
+ os << "D" << ExD->getID();
+ os << ", "
<< "S" << Ex->getID(getContext()) << '}';
}
@@ -496,8 +531,8 @@ void CXXThisRegion::dumpToStream(raw_ostream &os) const {
}
void ElementRegion::dumpToStream(raw_ostream &os) const {
- os << "Element{" << superRegion << ','
- << Index << ',' << getElementType().getAsString() << '}';
+ os << "Element{" << superRegion << ',' << Index << ',' << getElementType()
+ << '}';
}
void FieldRegion::dumpToStream(raw_ostream &os) const {
@@ -709,21 +744,17 @@ std::string MemRegion::getDescriptiveName(bool UseQuotes) const {
}
SourceRange MemRegion::sourceRange() const {
- const auto *const VR = dyn_cast<VarRegion>(this->getBaseRegion());
- const auto *const FR = dyn_cast<FieldRegion>(this);
-
// Check for more specific regions first.
- // FieldRegion
- if (FR) {
+ if (auto *FR = dyn_cast<FieldRegion>(this)) {
return FR->getDecl()->getSourceRange();
}
- // VarRegion
- else if (VR) {
+
+ if (auto *VR = dyn_cast<VarRegion>(this->getBaseRegion())) {
return VR->getDecl()->getSourceRange();
}
+
// Return invalid source range (can be checked by client).
- else
- return {};
+ return {};
}
//===----------------------------------------------------------------------===//
@@ -747,6 +778,7 @@ DefinedOrUnknownSVal MemRegionManager::getStaticSize(const MemRegion *MR,
case MemRegion::CXXBaseObjectRegionKind:
case MemRegion::CXXDerivedObjectRegionKind:
case MemRegion::CXXTempObjectRegionKind:
+ case MemRegion::CXXLifetimeExtendedObjectRegionKind:
case MemRegion::CXXThisRegionKind:
case MemRegion::ObjCIvarRegionKind:
case MemRegion::NonParamVarRegionKind:
@@ -768,14 +800,52 @@ DefinedOrUnknownSVal MemRegionManager::getStaticSize(const MemRegion *MR,
return UnknownVal();
QualType Ty = cast<TypedValueRegion>(SR)->getDesugaredValueType(Ctx);
- DefinedOrUnknownSVal Size = getElementExtent(Ty, 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();
- }
+ const DefinedOrUnknownSVal Size = getElementExtent(Ty, SVB);
+
+ // We currently don't model flexible array members (FAMs), which are:
+ // - int array[]; of IncompleteArrayType
+ // - int array[0]; of ConstantArrayType with size 0
+ // - int array[1]; of ConstantArrayType with size 1
+ // https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
+ const auto isFlexibleArrayMemberCandidate =
+ [this](const ArrayType *AT) -> bool {
+ if (!AT)
+ return false;
+
+ auto IsIncompleteArray = [](const ArrayType *AT) {
+ return isa<IncompleteArrayType>(AT);
+ };
+ auto IsArrayOfZero = [](const ArrayType *AT) {
+ const auto *CAT = dyn_cast<ConstantArrayType>(AT);
+ return CAT && CAT->getSize() == 0;
+ };
+ auto IsArrayOfOne = [](const ArrayType *AT) {
+ const auto *CAT = dyn_cast<ConstantArrayType>(AT);
+ return CAT && CAT->getSize() == 1;
+ };
+
+ using FAMKind = LangOptions::StrictFlexArraysLevelKind;
+ const FAMKind StrictFlexArraysLevel =
+ Ctx.getLangOpts().getStrictFlexArraysLevel();
+
+ // "Default": Any trailing array member is a FAM.
+ // Since we cannot tell at this point if this array is a trailing member
+ // or not, let's just do the same as for "OneZeroOrIncomplete".
+ if (StrictFlexArraysLevel == FAMKind::Default)
+ return IsArrayOfOne(AT) || IsArrayOfZero(AT) || IsIncompleteArray(AT);
+
+ if (StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete)
+ return IsArrayOfOne(AT) || IsArrayOfZero(AT) || IsIncompleteArray(AT);
+
+ if (StrictFlexArraysLevel == FAMKind::ZeroOrIncomplete)
+ return IsArrayOfZero(AT) || IsIncompleteArray(AT);
+
+ assert(StrictFlexArraysLevel == FAMKind::IncompleteOnly);
+ return IsIncompleteArray(AT);
+ };
+
+ if (isFlexibleArrayMemberCandidate(Ctx.getAsArrayType(Ty)))
+ return UnknownVal();
return Size;
}
@@ -794,8 +864,7 @@ DefinedOrUnknownSVal MemRegionManager::getStaticSize(const MemRegion *MR,
template <typename REG>
const REG *MemRegionManager::LazyAllocate(REG*& region) {
if (!region) {
- region = A.Allocate<REG>();
- new (region) REG(*this);
+ region = new (A) REG(*this);
}
return region;
@@ -804,8 +873,7 @@ const REG *MemRegionManager::LazyAllocate(REG*& region) {
template <typename REG, typename ARG>
const REG *MemRegionManager::LazyAllocate(REG*& region, ARG a) {
if (!region) {
- region = A.Allocate<REG>();
- new (region) REG(this, a);
+ region = new (A) REG(this, a);
}
return region;
@@ -819,8 +887,7 @@ MemRegionManager::getStackLocalsRegion(const StackFrameContext *STC) {
if (R)
return R;
- R = A.Allocate<StackLocalsSpaceRegion>();
- new (R) StackLocalsSpaceRegion(*this, STC);
+ R = new (A) StackLocalsSpaceRegion(*this, STC);
return R;
}
@@ -832,8 +899,7 @@ MemRegionManager::getStackArgumentsRegion(const StackFrameContext *STC) {
if (R)
return R;
- R = A.Allocate<StackArgumentsSpaceRegion>();
- new (R) StackArgumentsSpaceRegion(*this, STC);
+ R = new (A) StackArgumentsSpaceRegion(*this, STC);
return R;
}
@@ -854,8 +920,7 @@ const GlobalsSpaceRegion
if (R)
return R;
- R = A.Allocate<StaticGlobalSpaceRegion>();
- new (R) StaticGlobalSpaceRegion(*this, CR);
+ R = new (A) StaticGlobalSpaceRegion(*this, CR);
return R;
}
@@ -901,13 +966,11 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
if (const auto *BC = dyn_cast<BlockInvocationContext>(LC)) {
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 TypedValueRegion *OrigR = I.getOriginalRegion();
+ for (auto Var : BR->referenced_vars()) {
+ const TypedValueRegion *OrigR = Var.getOriginalRegion();
if (const auto *VR = dyn_cast<VarRegion>(OrigR)) {
if (VR->getDecl() == VD)
- return cast<VarRegion>(I.getCapturedRegion());
+ return cast<VarRegion>(Var.getCapturedRegion());
}
}
}
@@ -945,26 +1008,14 @@ const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
const MemRegion *sReg = nullptr;
if (D->hasGlobalStorage() && !D->isStaticLocal()) {
-
- // First handle the globals defined in system headers.
- 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)
- sReg = getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);
- else
- sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind);
-
- // Treat other globals as GlobalInternal unless they are constants.
+ QualType Ty = D->getType();
+ assert(!Ty.isNull());
+ if (Ty.isConstQualified()) {
+ sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind);
+ } else if (Ctx.getSourceManager().isInSystemHeader(D->getLocation())) {
+ sReg = getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);
} else {
- QualType GQT = D->getType();
- const Type *GT = GQT.getTypePtrOrNull();
- // TODO: We could walk the complex types here and see if everything is
- // constified.
- if (GT && GQT.isConstQualified() && GT->isArithmeticType())
- sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind);
- else
- sReg = getGlobalsRegion();
+ sReg = getGlobalsRegion(MemRegion::GlobalInternalSpaceRegionKind);
}
// Finally handle static locals.
@@ -986,14 +1037,15 @@ const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
sReg = getUnknownRegion();
} else {
if (D->hasLocalStorage()) {
- sReg = isa<ParmVarDecl>(D) || isa<ImplicitParamDecl>(D)
- ? static_cast<const MemRegion*>(getStackArgumentsRegion(STC))
- : static_cast<const MemRegion*>(getStackLocalsRegion(STC));
+ sReg =
+ isa<ParmVarDecl, ImplicitParamDecl>(D)
+ ? static_cast<const MemRegion *>(getStackArgumentsRegion(STC))
+ : static_cast<const MemRegion *>(getStackLocalsRegion(STC));
}
else {
assert(D->isStaticLocal());
const Decl *STCD = STC->getDecl();
- if (isa<FunctionDecl>(STCD) || isa<ObjCMethodDecl>(STCD))
+ if (isa<FunctionDecl, ObjCMethodDecl>(STCD))
sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind,
getFunctionCodeRegion(cast<NamedDecl>(STCD)));
else if (const auto *BD = dyn_cast<BlockDecl>(STCD)) {
@@ -1006,8 +1058,10 @@ const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
T = TSI->getType();
if (T.isNull())
T = getContext().VoidTy;
- if (!T->getAs<FunctionType>())
- T = getContext().getFunctionNoProtoType(T);
+ if (!T->getAs<FunctionType>()) {
+ FunctionProtoType::ExtProtoInfo Ext;
+ T = getContext().getFunctionType(T, std::nullopt, Ext);
+ }
T = getContext().getBlockPointerType(T);
const BlockCodeRegion *BTR =
@@ -1023,13 +1077,16 @@ const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
}
}
- return getSubRegion<NonParamVarRegion>(D, sReg);
+ return getNonParamVarRegion(D, sReg);
}
const NonParamVarRegion *
MemRegionManager::getNonParamVarRegion(const VarDecl *D,
const MemRegion *superR) {
+ // Prefer the definition over the canonical decl as the canonical form.
D = D->getCanonicalDecl();
+ if (const VarDecl *Def = D->getDefinition())
+ D = Def;
return getSubRegion<NonParamVarRegion>(D, superR);
}
@@ -1053,14 +1110,18 @@ MemRegionManager::getBlockDataRegion(const BlockCodeRegion *BC,
sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind);
}
else {
- if (LC) {
+ bool IsArcManagedBlock = Ctx.getLangOpts().ObjCAutoRefCount;
+
+ // ARC managed blocks can be initialized on stack or directly in heap
+ // depending on the implementations. So we initialize them with
+ // UnknownRegion.
+ if (!IsArcManagedBlock && LC) {
// FIXME: Once we implement scope handling, we want the parent region
// to be the scope.
const StackFrameContext *STC = LC->getStackFrame();
assert(STC);
sReg = getStackLocalsRegion(STC);
- }
- else {
+ } else {
// We allow 'LC' to be NULL for cases where want BlockDataRegions
// without context-sensitivity.
sReg = getUnknownRegion();
@@ -1070,12 +1131,6 @@ MemRegionManager::getBlockDataRegion(const BlockCodeRegion *BC,
return getSubRegion<BlockDataRegion>(BC, LC, blockCount, sReg);
}
-const CXXTempObjectRegion *
-MemRegionManager::getCXXStaticTempObjectRegion(const Expr *Ex) {
- return getSubRegion<CXXTempObjectRegion>(
- Ex, getGlobalsRegion(MemRegion::GlobalInternalSpaceRegionKind, nullptr));
-}
-
const CompoundLiteralRegion*
MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr *CL,
const LocationContext *LC) {
@@ -1106,8 +1161,7 @@ MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx,
auto *R = cast_or_null<ElementRegion>(data);
if (!R) {
- R = A.Allocate<ElementRegion>();
- new (R) ElementRegion(T, Idx, superRegion);
+ R = new (A) ElementRegion(T, Idx, superRegion);
Regions.InsertNode(R, InsertPos);
}
@@ -1126,9 +1180,12 @@ MemRegionManager::getBlockCodeRegion(const BlockDecl *BD, CanQualType locTy,
return getSubRegion<BlockCodeRegion>(BD, locTy, AC, getCodeRegion());
}
-/// getSymbolicRegion - Retrieve or create a "symbolic" memory region.
-const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) {
- return getSubRegion<SymbolicRegion>(sym, getUnknownRegion());
+const SymbolicRegion *
+MemRegionManager::getSymbolicRegion(SymbolRef sym,
+ const MemSpaceRegion *MemSpace) {
+ if (MemSpace == nullptr)
+ MemSpace = getUnknownRegion();
+ return getSubRegion<SymbolicRegion>(sym, MemSpace);
}
const SymbolicRegion *MemRegionManager::getSymbolicHeapRegion(SymbolRef Sym) {
@@ -1155,6 +1212,23 @@ MemRegionManager::getCXXTempObjectRegion(Expr const *E,
return getSubRegion<CXXTempObjectRegion>(E, getStackLocalsRegion(SFC));
}
+const CXXLifetimeExtendedObjectRegion *
+MemRegionManager::getCXXLifetimeExtendedObjectRegion(
+ const Expr *Ex, const ValueDecl *VD, const LocationContext *LC) {
+ const StackFrameContext *SFC = LC->getStackFrame();
+ assert(SFC);
+ return getSubRegion<CXXLifetimeExtendedObjectRegion>(
+ Ex, VD, getStackLocalsRegion(SFC));
+}
+
+const CXXLifetimeExtendedObjectRegion *
+MemRegionManager::getCXXStaticLifetimeExtendedObjectRegion(
+ const Expr *Ex, const ValueDecl *VD) {
+ return getSubRegion<CXXLifetimeExtendedObjectRegion>(
+ Ex, VD,
+ getGlobalsRegion(MemRegion::GlobalInternalSpaceRegionKind, nullptr));
+}
+
/// Checks whether \p BaseClass is a valid virtual or direct non-virtual base
/// class of the type of \p Super.
static bool isValidBaseClass(const CXXRecordDecl *BaseClass,
@@ -1241,7 +1315,7 @@ const MemSpaceRegion *MemRegion::getMemorySpace() const {
SR = dyn_cast<SubRegion>(R);
}
- return dyn_cast<MemSpaceRegion>(R);
+ return cast<MemSpaceRegion>(R);
}
bool MemRegion::hasStackStorage() const {
@@ -1256,14 +1330,8 @@ bool MemRegion::hasStackParametersStorage() const {
return isa<StackArgumentsSpaceRegion>(getMemorySpace());
}
-bool MemRegion::hasGlobalsOrParametersStorage() const {
- const MemSpaceRegion *MS = getMemorySpace();
- return isa<StackArgumentsSpaceRegion>(MS) ||
- isa<GlobalsSpaceRegion>(MS);
-}
-
-// getBaseRegion strips away all elements and fields, and get the base region
-// of them.
+// Strips away all elements and fields.
+// Returns the base region of them.
const MemRegion *MemRegion::getBaseRegion() const {
const MemRegion *R = this;
while (true) {
@@ -1283,8 +1351,7 @@ const MemRegion *MemRegion::getBaseRegion() const {
return R;
}
-// getgetMostDerivedObjectRegion gets the region of the root class of a C++
-// class hierarchy.
+// Returns the region of the root class of a C++ class hierarchy.
const MemRegion *MemRegion::getMostDerivedObjectRegion() const {
const MemRegion *R = this;
while (const auto *BR = dyn_cast<CXXBaseObjectRegion>(R))
@@ -1435,6 +1502,7 @@ static RegionOffset calculateOffset(const MemRegion *R) {
case MemRegion::NonParamVarRegionKind:
case MemRegion::ParamVarRegionKind:
case MemRegion::CXXTempObjectRegionKind:
+ case MemRegion::CXXLifetimeExtendedObjectRegionKind:
// Usual base regions.
goto Finish;
@@ -1458,7 +1526,7 @@ static RegionOffset calculateOffset(const MemRegion *R) {
// If our base region is symbolic, we don't know what type it really is.
// Pretend the type of the symbol is the true dynamic type.
// (This will at least be self-consistent for the life of the symbol.)
- Ty = SR->getSymbol()->getType()->getPointeeType();
+ Ty = SR->getPointeeStaticType();
RootIsSymbolic = true;
}
@@ -1515,7 +1583,7 @@ static RegionOffset calculateOffset(const MemRegion *R) {
}
SVal Index = ER->getIndex();
- if (Optional<nonloc::ConcreteInt> CI =
+ if (std::optional<nonloc::ConcreteInt> CI =
Index.getAs<nonloc::ConcreteInt>()) {
// Don't bother calculating precise offsets if we already have a
// symbolic offset somewhere in the chain.
@@ -1625,10 +1693,8 @@ void BlockDataRegion::LazyInitializeReferencedVars() {
using VarVec = BumpVector<const MemRegion *>;
- auto *BV = A.Allocate<VarVec>();
- new (BV) VarVec(BC, NumBlockVars);
- auto *BVOriginal = A.Allocate<VarVec>();
- new (BVOriginal) VarVec(BC, NumBlockVars);
+ auto *BV = new (A) VarVec(BC, NumBlockVars);
+ auto *BVOriginal = new (A) VarVec(BC, NumBlockVars);
for (const auto *VD : ReferencedBlockVars) {
const VarRegion *VR = nullptr;
@@ -1676,10 +1742,13 @@ BlockDataRegion::referenced_vars_end() const {
VecOriginal->end());
}
+llvm::iterator_range<BlockDataRegion::referenced_vars_iterator>
+BlockDataRegion::referenced_vars() const {
+ return llvm::make_range(referenced_vars_begin(), referenced_vars_end());
+}
+
const VarRegion *BlockDataRegion::getOriginalRegion(const VarRegion *R) const {
- for (referenced_vars_iterator I = referenced_vars_begin(),
- E = referenced_vars_end();
- I != E; ++I) {
+ for (const auto &I : referenced_vars()) {
if (I.getCapturedRegion() == R)
return I.getOriginalRegion();
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
index 92104d628711..be19a1c118ea 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -28,6 +28,7 @@
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Casting.h"
#include <memory>
+#include <optional>
using namespace clang;
using namespace ento;
@@ -165,7 +166,7 @@ static void printCoverage(const PathDiagnostic *D,
FIDMap &FM,
llvm::raw_fd_ostream &o);
-static Optional<StringRef> getExpandedMacro(
+static std::optional<StringRef> getExpandedMacro(
SourceLocation MacroLoc, const cross_tu::CrossTranslationUnitContext &CTU,
const MacroExpansionContext &MacroExpansions, const SourceManager &SM);
@@ -366,10 +367,8 @@ void PlistPrinter::ReportMacroSubPieces(raw_ostream &o,
unsigned indent, unsigned depth) {
MacroPieces.push_back(&P);
- for (PathPieces::const_iterator I = P.subPieces.begin(),
- E = P.subPieces.end();
- I != E; ++I) {
- ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ false);
+ for (const auto &SubPiece : P.subPieces) {
+ ReportPiece(o, *SubPiece, indent, depth, /*includeControlFlow*/ false);
}
assert(P.getFixits().size() == 0 &&
@@ -384,12 +383,12 @@ void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) {
SourceLocation MacroExpansionLoc =
P->getLocation().asLocation().getExpansionLoc();
- const Optional<StringRef> MacroName =
+ const std::optional<StringRef> MacroName =
MacroExpansions.getOriginalText(MacroExpansionLoc);
- const Optional<StringRef> ExpansionText =
+ const std::optional<StringRef> ExpansionText =
getExpandedMacro(MacroExpansionLoc, CTU, MacroExpansions, SM);
- if (!MacroName.hasValue() || !ExpansionText.hasValue())
+ if (!MacroName || !ExpansionText)
continue;
Indent(o, indent) << "<dict>\n";
@@ -407,11 +406,11 @@ void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) {
// Output the macro name.
Indent(o, indent) << "<key>name</key>";
- EmitString(o, MacroName.getValue()) << '\n';
+ EmitString(o, *MacroName) << '\n';
// Output what it expands into.
Indent(o, indent) << "<key>expansion</key>";
- EmitString(o, ExpansionText.getValue()) << '\n';
+ EmitString(o, *ExpansionText) << '\n';
// Finish up.
--indent;
@@ -499,12 +498,12 @@ static void printCoverage(const PathDiagnostic *D,
// Mapping from file IDs to executed lines.
const FilesToLineNumsMap &ExecutedLines = D->getExecutedLines();
- for (auto I = ExecutedLines.begin(), E = ExecutedLines.end(); I != E; ++I) {
- unsigned FileKey = AddFID(FM, Fids, I->first);
+ for (const auto &[FID, Lines] : ExecutedLines) {
+ unsigned FileKey = AddFID(FM, Fids, FID);
Indent(o, IndentLevel) << "<key>" << FileKey << "</key>\n";
Indent(o, IndentLevel) << "<array>\n";
IndentLevel++;
- for (unsigned LineNo : I->second) {
+ for (unsigned LineNo : Lines) {
Indent(o, IndentLevel);
EmitInteger(o, LineNo) << "\n";
}
@@ -596,8 +595,8 @@ void PlistDiagnostics::printBugPath(llvm::raw_ostream &o, const FIDMap &FM,
o << " <array>\n";
- for (PathPieces::const_iterator E = Path.end(); I != E; ++I)
- Printer.ReportDiag(o, **I);
+ for (const auto &Piece : llvm::make_range(I, Path.end()))
+ Printer.ReportDiag(o, *Piece);
o << " </array>\n";
@@ -805,7 +804,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
o << " <key>files</key>\n"
" <array>\n";
for (FileID FID : Fids)
- EmitString(o << " ", SM.getFileEntryForID(FID)->getName()) << '\n';
+ EmitString(o << " ", SM.getFileEntryRefForID(FID)->getName()) << '\n';
o << " </array>\n";
if (llvm::AreStatisticsEnabled() && DiagOpts.ShouldSerializeStats) {
@@ -825,7 +824,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
// Definitions of helper functions and methods for expanding macros.
//===----------------------------------------------------------------------===//
-static Optional<StringRef>
+static std::optional<StringRef>
getExpandedMacro(SourceLocation MacroExpansionLoc,
const cross_tu::CrossTranslationUnitContext &CTU,
const MacroExpansionContext &MacroExpansions,
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
index 1ccb0de92fba..f12f1a5ac970 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -19,6 +19,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -54,12 +55,8 @@ ProgramState::ProgramState(ProgramStateManager *mgr, const Environment& env,
}
ProgramState::ProgramState(const ProgramState &RHS)
- : llvm::FoldingSetNode(),
- stateMgr(RHS.stateMgr),
- Env(RHS.Env),
- store(RHS.store),
- GDM(RHS.GDM),
- refCount(0) {
+ : stateMgr(RHS.stateMgr), Env(RHS.Env), store(RHS.store), GDM(RHS.GDM),
+ PosteriorlyOverconstrained(RHS.PosteriorlyOverconstrained), refCount(0) {
stateMgr->getStoreManager().incrementReferenceCount(store);
}
@@ -159,9 +156,8 @@ ProgramState::invalidateRegions(RegionList Regions,
const CallEvent *Call,
RegionAndSymbolInvalidationTraits *ITraits) const {
SmallVector<SVal, 8> Values;
- for (RegionList::const_iterator I = Regions.begin(),
- End = Regions.end(); I != End; ++I)
- Values.push_back(loc::MemRegionVal(*I));
+ for (const MemRegion *Reg : Regions)
+ Values.push_back(loc::MemRegionVal(Reg));
return invalidateRegionsImpl(Values, E, Count, LCtx, CausedByPointerEscape,
IS, ITraits, Call);
@@ -220,8 +216,6 @@ ProgramState::invalidateRegionsImpl(ValueList Values,
}
ProgramStateRef ProgramState::killBinding(Loc LV) const {
- assert(!LV.getAs<loc::MemRegionVal>() && "Use invalidateRegion instead.");
-
Store OldStore = getStore();
const StoreRef &newStore =
getStateManager().StoreMgr->killBinding(OldStore, LV);
@@ -318,12 +312,12 @@ ProgramStateRef ProgramState::BindExpr(const Stmt *S,
return getStateManager().getPersistentState(NewSt);
}
-ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx,
- DefinedOrUnknownSVal UpperBound,
- bool Assumption,
- QualType indexTy) const {
+[[nodiscard]] std::pair<ProgramStateRef, ProgramStateRef>
+ProgramState::assumeInBoundDual(DefinedOrUnknownSVal Idx,
+ DefinedOrUnknownSVal UpperBound,
+ QualType indexTy) const {
if (Idx.isUnknown() || UpperBound.isUnknown())
- return this;
+ return {this, this};
// Build an expression for 0 <= Idx < UpperBound.
// This is the same as Idx + MIN < UpperBound + MIN, if overflow is allowed.
@@ -342,7 +336,7 @@ ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx,
SVal newIdx = svalBuilder.evalBinOpNN(this, BO_Add,
Idx.castAs<NonLoc>(), Min, indexTy);
if (newIdx.isUnknownOrUndef())
- return this;
+ return {this, this};
// Adjust the upper bound.
SVal newBound =
@@ -350,17 +344,26 @@ ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx,
Min, indexTy);
if (newBound.isUnknownOrUndef())
- return this;
+ return {this, this};
// Build the actual comparison.
SVal inBound = svalBuilder.evalBinOpNN(this, BO_LT, newIdx.castAs<NonLoc>(),
newBound.castAs<NonLoc>(), Ctx.IntTy);
if (inBound.isUnknownOrUndef())
- return this;
+ return {this, this};
// Finally, let the constraint manager take care of it.
ConstraintManager &CM = SM.getConstraintManager();
- return CM.assume(this, inBound.castAs<DefinedSVal>(), Assumption);
+ return CM.assumeDual(this, inBound.castAs<DefinedSVal>());
+}
+
+ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx,
+ DefinedOrUnknownSVal UpperBound,
+ bool Assumption,
+ QualType indexTy) const {
+ std::pair<ProgramStateRef, ProgramStateRef> R =
+ assumeInBoundDual(Idx, UpperBound, indexTy);
+ return Assumption ? R.first : R.second;
}
ConditionTruthVal ProgramState::isNonNull(SVal V) const {
@@ -420,7 +423,7 @@ ProgramStateRef ProgramStateManager::getPersistentState(ProgramState &State) {
freeStates.pop_back();
}
else {
- newState = (ProgramState*) Alloc.Allocate<ProgramState>();
+ newState = Alloc.Allocate<ProgramState>();
}
new (newState) ProgramState(State);
StateSet.InsertNode(newState, InsertPos);
@@ -433,6 +436,12 @@ ProgramStateRef ProgramState::makeWithStore(const StoreRef &store) const {
return getStateManager().getPersistentState(NewSt);
}
+ProgramStateRef ProgramState::cloneAsPosteriorlyOverconstrained() const {
+ ProgramState NewSt(*this);
+ NewSt.PosteriorlyOverconstrained = true;
+ return getStateManager().getPersistentState(NewSt);
+}
+
void ProgramState::setStore(const StoreRef &newStore) {
Store newStoreStore = newStore.getStore();
if (newStoreStore)
@@ -546,22 +555,20 @@ bool ScanReachableSymbols::scan(nonloc::LazyCompoundVal val) {
}
bool ScanReachableSymbols::scan(nonloc::CompoundVal val) {
- for (nonloc::CompoundVal::iterator I=val.begin(), E=val.end(); I!=E; ++I)
- if (!scan(*I))
+ for (SVal V : val)
+ if (!scan(V))
return false;
return true;
}
bool ScanReachableSymbols::scan(const SymExpr *sym) {
- for (SymExpr::symbol_iterator SI = sym->symbol_begin(),
- SE = sym->symbol_end();
- SI != SE; ++SI) {
- bool wasVisited = !visited.insert(*SI).second;
+ for (SymbolRef SubSym : sym->symbols()) {
+ bool wasVisited = !visited.insert(SubSym).second;
if (wasVisited)
continue;
- if (!visitor.VisitSymbol(*SI))
+ if (!visitor.VisitSymbol(SubSym))
return false;
}
@@ -569,20 +576,20 @@ bool ScanReachableSymbols::scan(const SymExpr *sym) {
}
bool ScanReachableSymbols::scan(SVal val) {
- if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>())
+ if (std::optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>())
return scan(X->getRegion());
- if (Optional<nonloc::LazyCompoundVal> X =
+ if (std::optional<nonloc::LazyCompoundVal> X =
val.getAs<nonloc::LazyCompoundVal>())
return scan(*X);
- if (Optional<nonloc::LocAsInteger> X = val.getAs<nonloc::LocAsInteger>())
+ if (std::optional<nonloc::LocAsInteger> X = val.getAs<nonloc::LocAsInteger>())
return scan(X->getLoc());
if (SymbolRef Sym = val.getAsSymbol())
return scan(Sym);
- if (Optional<nonloc::CompoundVal> X = val.getAs<nonloc::CompoundVal>())
+ if (std::optional<nonloc::CompoundVal> X = val.getAs<nonloc::CompoundVal>())
return scan(*X);
return true;
@@ -620,10 +627,8 @@ bool ScanReachableSymbols::scan(const MemRegion *R) {
// Regions captured by a block are also implicitly reachable.
if (const BlockDataRegion *BDR = dyn_cast<BlockDataRegion>(R)) {
- BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(),
- E = BDR->referenced_vars_end();
- for ( ; I != E; ++I) {
- if (!scan(I.getCapturedRegion()))
+ for (auto Var : BDR->referenced_vars()) {
+ if (!scan(Var.getCapturedRegion()))
return false;
}
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index 69554576bdb2..25d066c4652f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -20,12 +20,13 @@
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableSet.h"
#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <iterator>
+#include <optional>
using namespace clang;
using namespace ento;
@@ -110,6 +111,14 @@ public:
RangeSet::ContainerType RangeSet::Factory::EmptySet{};
+RangeSet RangeSet::Factory::add(RangeSet LHS, RangeSet RHS) {
+ ContainerType Result;
+ Result.reserve(LHS.size() + RHS.size());
+ std::merge(LHS.begin(), LHS.end(), RHS.begin(), RHS.end(),
+ std::back_inserter(Result));
+ return makePersistent(std::move(Result));
+}
+
RangeSet RangeSet::Factory::add(RangeSet Original, Range Element) {
ContainerType Result;
Result.reserve(Original.size() + 1);
@@ -126,6 +135,186 @@ RangeSet RangeSet::Factory::add(RangeSet Original, const llvm::APSInt &Point) {
return add(Original, Range(Point));
}
+RangeSet RangeSet::Factory::unite(RangeSet LHS, RangeSet RHS) {
+ ContainerType Result = unite(*LHS.Impl, *RHS.Impl);
+ return makePersistent(std::move(Result));
+}
+
+RangeSet RangeSet::Factory::unite(RangeSet Original, Range R) {
+ ContainerType Result;
+ Result.push_back(R);
+ Result = unite(*Original.Impl, Result);
+ return makePersistent(std::move(Result));
+}
+
+RangeSet RangeSet::Factory::unite(RangeSet Original, llvm::APSInt Point) {
+ return unite(Original, Range(ValueFactory.getValue(Point)));
+}
+
+RangeSet RangeSet::Factory::unite(RangeSet Original, llvm::APSInt From,
+ llvm::APSInt To) {
+ return unite(Original,
+ Range(ValueFactory.getValue(From), ValueFactory.getValue(To)));
+}
+
+template <typename T>
+void swapIterators(T &First, T &FirstEnd, T &Second, T &SecondEnd) {
+ std::swap(First, Second);
+ std::swap(FirstEnd, SecondEnd);
+}
+
+RangeSet::ContainerType RangeSet::Factory::unite(const ContainerType &LHS,
+ const ContainerType &RHS) {
+ if (LHS.empty())
+ return RHS;
+ if (RHS.empty())
+ return LHS;
+
+ using llvm::APSInt;
+ using iterator = ContainerType::const_iterator;
+
+ iterator First = LHS.begin();
+ iterator FirstEnd = LHS.end();
+ iterator Second = RHS.begin();
+ iterator SecondEnd = RHS.end();
+ APSIntType Ty = APSIntType(First->From());
+ const APSInt Min = Ty.getMinValue();
+
+ // Handle a corner case first when both range sets start from MIN.
+ // This helps to avoid complicated conditions below. Specifically, this
+ // particular check for `MIN` is not needed in the loop below every time
+ // when we do `Second->From() - One` operation.
+ if (Min == First->From() && Min == Second->From()) {
+ if (First->To() > Second->To()) {
+ // [ First ]--->
+ // [ Second ]----->
+ // MIN^
+ // The Second range is entirely inside the First one.
+
+ // Check if Second is the last in its RangeSet.
+ if (++Second == SecondEnd)
+ // [ First ]--[ First + 1 ]--->
+ // [ Second ]--------------------->
+ // MIN^
+ // The Union is equal to First's RangeSet.
+ return LHS;
+ } else {
+ // case 1: [ First ]----->
+ // case 2: [ First ]--->
+ // [ Second ]--->
+ // MIN^
+ // The First range is entirely inside or equal to the Second one.
+
+ // Check if First is the last in its RangeSet.
+ if (++First == FirstEnd)
+ // [ First ]----------------------->
+ // [ Second ]--[ Second + 1 ]---->
+ // MIN^
+ // The Union is equal to Second's RangeSet.
+ return RHS;
+ }
+ }
+
+ const APSInt One = Ty.getValue(1);
+ ContainerType Result;
+
+ // This is called when there are no ranges left in one of the ranges.
+ // Append the rest of the ranges from another range set to the Result
+ // and return with that.
+ const auto AppendTheRest = [&Result](iterator I, iterator E) {
+ Result.append(I, E);
+ return Result;
+ };
+
+ while (true) {
+ // We want to keep the following invariant at all times:
+ // ---[ First ------>
+ // -----[ Second --->
+ if (First->From() > Second->From())
+ swapIterators(First, FirstEnd, Second, SecondEnd);
+
+ // The Union definitely starts with First->From().
+ // ----------[ First ------>
+ // ------------[ Second --->
+ // ----------[ Union ------>
+ // UnionStart^
+ const llvm::APSInt &UnionStart = First->From();
+
+ // Loop where the invariant holds.
+ while (true) {
+ // Skip all enclosed ranges.
+ // ---[ First ]--->
+ // -----[ Second ]--[ Second + 1 ]--[ Second + N ]----->
+ while (First->To() >= Second->To()) {
+ // Check if Second is the last in its RangeSet.
+ if (++Second == SecondEnd) {
+ // Append the Union.
+ // ---[ Union ]--->
+ // -----[ Second ]----->
+ // --------[ First ]--->
+ // UnionEnd^
+ Result.emplace_back(UnionStart, First->To());
+ // ---[ Union ]----------------->
+ // --------------[ First + 1]--->
+ // Append all remaining ranges from the First's RangeSet.
+ return AppendTheRest(++First, FirstEnd);
+ }
+ }
+
+ // Check if First and Second are disjoint. It means that we find
+ // the end of the Union. Exit the loop and append the Union.
+ // ---[ First ]=------------->
+ // ------------=[ Second ]--->
+ // ----MinusOne^
+ if (First->To() < Second->From() - One)
+ break;
+
+ // First is entirely inside the Union. Go next.
+ // ---[ Union ----------->
+ // ---- [ First ]-------->
+ // -------[ Second ]----->
+ // Check if First is the last in its RangeSet.
+ if (++First == FirstEnd) {
+ // Append the Union.
+ // ---[ Union ]--->
+ // -----[ First ]------->
+ // --------[ Second ]--->
+ // UnionEnd^
+ Result.emplace_back(UnionStart, Second->To());
+ // ---[ Union ]------------------>
+ // --------------[ Second + 1]--->
+ // Append all remaining ranges from the Second's RangeSet.
+ return AppendTheRest(++Second, SecondEnd);
+ }
+
+ // We know that we are at one of the two cases:
+ // case 1: --[ First ]--------->
+ // case 2: ----[ First ]------->
+ // --------[ Second ]---------->
+ // In both cases First starts after Second->From().
+ // Make sure that the loop invariant holds.
+ swapIterators(First, FirstEnd, Second, SecondEnd);
+ }
+
+ // Here First and Second are disjoint.
+ // Append the Union.
+ // ---[ Union ]--------------->
+ // -----------------[ Second ]--->
+ // ------[ First ]--------------->
+ // UnionEnd^
+ Result.emplace_back(UnionStart, First->To());
+
+ // Check if First is the last in its RangeSet.
+ if (++First == FirstEnd)
+ // ---[ Union ]--------------->
+ // --------------[ Second ]--->
+ // Append all remaining ranges from the Second's RangeSet.
+ return AppendTheRest(Second, SecondEnd);
+ }
+
+ llvm_unreachable("Normally, we should not reach here");
+}
+
RangeSet RangeSet::Factory::getRangeSet(Range From) {
ContainerType Result;
Result.push_back(From);
@@ -155,13 +344,6 @@ RangeSet::ContainerType *RangeSet::Factory::construct(ContainerType &&From) {
return new (Buffer) ContainerType(std::move(From));
}
-RangeSet RangeSet::Factory::add(RangeSet LHS, RangeSet RHS) {
- ContainerType Result;
- std::merge(LHS.begin(), LHS.end(), RHS.begin(), RHS.end(),
- std::back_inserter(Result));
- return makePersistent(std::move(Result));
-}
-
const llvm::APSInt &RangeSet::getMinValue() const {
assert(!isEmpty());
return begin()->From();
@@ -172,6 +354,21 @@ const llvm::APSInt &RangeSet::getMaxValue() const {
return std::prev(end())->To();
}
+bool clang::ento::RangeSet::isUnsigned() const {
+ assert(!isEmpty());
+ return begin()->From().isUnsigned();
+}
+
+uint32_t clang::ento::RangeSet::getBitWidth() const {
+ assert(!isEmpty());
+ return begin()->From().getBitWidth();
+}
+
+APSIntType clang::ento::RangeSet::getAPSIntType() const {
+ assert(!isEmpty());
+ return APSIntType(begin()->From());
+}
+
bool RangeSet::containsImpl(llvm::APSInt &Point) const {
if (isEmpty() || !pin(Point))
return false;
@@ -325,11 +522,6 @@ RangeSet RangeSet::Factory::intersect(const RangeSet::ContainerType &LHS,
const_iterator First = LHS.begin(), Second = RHS.begin(),
FirstEnd = LHS.end(), SecondEnd = RHS.end();
- const auto SwapIterators = [&First, &FirstEnd, &Second, &SecondEnd]() {
- std::swap(First, Second);
- std::swap(FirstEnd, SecondEnd);
- };
-
// If we ran out of ranges in one set, but not in the other,
// it means that those elements are definitely not in the
// intersection.
@@ -339,7 +531,7 @@ RangeSet RangeSet::Factory::intersect(const RangeSet::ContainerType &LHS,
// ----[ First ---------------------->
// --------[ Second ----------------->
if (Second->From() < First->From())
- SwapIterators();
+ swapIterators(First, FirstEnd, Second, SecondEnd);
// Loop where the invariant holds:
do {
@@ -373,7 +565,7 @@ RangeSet RangeSet::Factory::intersect(const RangeSet::ContainerType &LHS,
if (Second->To() > First->To()) {
// Here we make a decision to keep First as the "longer"
// range.
- SwapIterators();
+ swapIterators(First, FirstEnd, Second, SecondEnd);
}
// At this point, we have the following situation:
@@ -479,6 +671,181 @@ RangeSet RangeSet::Factory::negate(RangeSet What) {
return makePersistent(std::move(Result));
}
+// Convert range set to the given integral type using truncation and promotion.
+// This works similar to APSIntType::apply function but for the range set.
+RangeSet RangeSet::Factory::castTo(RangeSet What, APSIntType Ty) {
+ // Set is empty or NOOP (aka cast to the same type).
+ if (What.isEmpty() || What.getAPSIntType() == Ty)
+ return What;
+
+ const bool IsConversion = What.isUnsigned() != Ty.isUnsigned();
+ const bool IsTruncation = What.getBitWidth() > Ty.getBitWidth();
+ const bool IsPromotion = What.getBitWidth() < Ty.getBitWidth();
+
+ if (IsTruncation)
+ return makePersistent(truncateTo(What, Ty));
+
+ // Here we handle 2 cases:
+ // - IsConversion && !IsPromotion.
+ // In this case we handle changing a sign with same bitwidth: char -> uchar,
+ // uint -> int. Here we convert negatives to positives and positives which
+ // is out of range to negatives. We use convertTo function for that.
+ // - IsConversion && IsPromotion && !What.isUnsigned().
+ // In this case we handle changing a sign from signeds to unsigneds with
+ // higher bitwidth: char -> uint, int-> uint64. The point is that we also
+ // need convert negatives to positives and use convertTo function as well.
+ // For example, we don't need such a convertion when converting unsigned to
+ // signed with higher bitwidth, because all the values of unsigned is valid
+ // for the such signed.
+ if (IsConversion && (!IsPromotion || !What.isUnsigned()))
+ return makePersistent(convertTo(What, Ty));
+
+ assert(IsPromotion && "Only promotion operation from unsigneds left.");
+ return makePersistent(promoteTo(What, Ty));
+}
+
+RangeSet RangeSet::Factory::castTo(RangeSet What, QualType T) {
+ assert(T->isIntegralOrEnumerationType() && "T shall be an integral type.");
+ return castTo(What, ValueFactory.getAPSIntType(T));
+}
+
+RangeSet::ContainerType RangeSet::Factory::truncateTo(RangeSet What,
+ APSIntType Ty) {
+ using llvm::APInt;
+ using llvm::APSInt;
+ ContainerType Result;
+ ContainerType Dummy;
+ // CastRangeSize is an amount of all possible values of cast type.
+ // Example: `char` has 256 values; `short` has 65536 values.
+ // But in fact we use `amount of values` - 1, because
+ // we can't keep `amount of values of UINT64` inside uint64_t.
+ // E.g. 256 is an amount of all possible values of `char` and we can't keep
+ // it inside `char`.
+ // And it's OK, it's enough to do correct calculations.
+ uint64_t CastRangeSize = APInt::getMaxValue(Ty.getBitWidth()).getZExtValue();
+ for (const Range &R : What) {
+ // Get bounds of the given range.
+ APSInt FromInt = R.From();
+ APSInt ToInt = R.To();
+ // CurrentRangeSize is an amount of all possible values of the current
+ // range minus one.
+ uint64_t CurrentRangeSize = (ToInt - FromInt).getZExtValue();
+ // This is an optimization for a specific case when this Range covers
+ // the whole range of the target type.
+ Dummy.clear();
+ if (CurrentRangeSize >= CastRangeSize) {
+ Dummy.emplace_back(ValueFactory.getMinValue(Ty),
+ ValueFactory.getMaxValue(Ty));
+ Result = std::move(Dummy);
+ break;
+ }
+ // Cast the bounds.
+ Ty.apply(FromInt);
+ Ty.apply(ToInt);
+ const APSInt &PersistentFrom = ValueFactory.getValue(FromInt);
+ const APSInt &PersistentTo = ValueFactory.getValue(ToInt);
+ if (FromInt > ToInt) {
+ Dummy.emplace_back(ValueFactory.getMinValue(Ty), PersistentTo);
+ Dummy.emplace_back(PersistentFrom, ValueFactory.getMaxValue(Ty));
+ } else
+ Dummy.emplace_back(PersistentFrom, PersistentTo);
+ // Every range retrieved after truncation potentialy has garbage values.
+ // So, we have to unite every next range with the previouses.
+ Result = unite(Result, Dummy);
+ }
+
+ return Result;
+}
+
+// Divide the convertion into two phases (presented as loops here).
+// First phase(loop) works when casted values go in ascending order.
+// E.g. char{1,3,5,127} -> uint{1,3,5,127}
+// Interrupt the first phase and go to second one when casted values start
+// go in descending order. That means that we crossed over the middle of
+// the type value set (aka 0 for signeds and MAX/2+1 for unsigneds).
+// For instance:
+// 1: uchar{1,3,5,128,255} -> char{1,3,5,-128,-1}
+// Here we put {1,3,5} to one array and {-128, -1} to another
+// 2: char{-128,-127,-1,0,1,2} -> uchar{128,129,255,0,1,3}
+// Here we put {128,129,255} to one array and {0,1,3} to another.
+// After that we unite both arrays.
+// NOTE: We don't just concatenate the arrays, because they may have
+// adjacent ranges, e.g.:
+// 1: char(-128, 127) -> uchar -> arr1(128, 255), arr2(0, 127) ->
+// unite -> uchar(0, 255)
+// 2: uchar(0, 1)U(254, 255) -> char -> arr1(0, 1), arr2(-2, -1) ->
+// unite -> uchar(-2, 1)
+RangeSet::ContainerType RangeSet::Factory::convertTo(RangeSet What,
+ APSIntType Ty) {
+ using llvm::APInt;
+ using llvm::APSInt;
+ using Bounds = std::pair<const APSInt &, const APSInt &>;
+ ContainerType AscendArray;
+ ContainerType DescendArray;
+ auto CastRange = [Ty, &VF = ValueFactory](const Range &R) -> Bounds {
+ // Get bounds of the given range.
+ APSInt FromInt = R.From();
+ APSInt ToInt = R.To();
+ // Cast the bounds.
+ Ty.apply(FromInt);
+ Ty.apply(ToInt);
+ return {VF.getValue(FromInt), VF.getValue(ToInt)};
+ };
+ // Phase 1. Fill the first array.
+ APSInt LastConvertedInt = Ty.getMinValue();
+ const auto *It = What.begin();
+ const auto *E = What.end();
+ while (It != E) {
+ Bounds NewBounds = CastRange(*(It++));
+ // If values stop going acsending order, go to the second phase(loop).
+ if (NewBounds.first < LastConvertedInt) {
+ DescendArray.emplace_back(NewBounds.first, NewBounds.second);
+ break;
+ }
+ // If the range contains a midpoint, then split the range.
+ // E.g. char(-5, 5) -> uchar(251, 5)
+ // Here we shall add a range (251, 255) to the first array and (0, 5) to the
+ // second one.
+ if (NewBounds.first > NewBounds.second) {
+ DescendArray.emplace_back(ValueFactory.getMinValue(Ty), NewBounds.second);
+ AscendArray.emplace_back(NewBounds.first, ValueFactory.getMaxValue(Ty));
+ } else
+ // Values are going acsending order.
+ AscendArray.emplace_back(NewBounds.first, NewBounds.second);
+ LastConvertedInt = NewBounds.first;
+ }
+ // Phase 2. Fill the second array.
+ while (It != E) {
+ Bounds NewBounds = CastRange(*(It++));
+ DescendArray.emplace_back(NewBounds.first, NewBounds.second);
+ }
+ // Unite both arrays.
+ return unite(AscendArray, DescendArray);
+}
+
+/// Promotion from unsigneds to signeds/unsigneds left.
+RangeSet::ContainerType RangeSet::Factory::promoteTo(RangeSet What,
+ APSIntType Ty) {
+ ContainerType Result;
+ // We definitely know the size of the result set.
+ Result.reserve(What.size());
+
+ // Each unsigned value fits every larger type without any changes,
+ // whether the larger type is signed or unsigned. So just promote and push
+ // back each range one by one.
+ for (const Range &R : What) {
+ // Get bounds of the given range.
+ llvm::APSInt FromInt = R.From();
+ llvm::APSInt ToInt = R.To();
+ // Cast the bounds.
+ Ty.apply(FromInt);
+ Ty.apply(ToInt);
+ Result.emplace_back(ValueFactory.getValue(FromInt),
+ ValueFactory.getValue(ToInt));
+ }
+ return Result;
+}
+
RangeSet RangeSet::Factory::deletePoint(RangeSet From,
const llvm::APSInt &Point) {
if (!From.contains(Point))
@@ -494,15 +861,17 @@ RangeSet RangeSet::Factory::deletePoint(RangeSet From,
return intersect(From, Upper, Lower);
}
-void Range::dump(raw_ostream &OS) const {
+LLVM_DUMP_METHOD void Range::dump(raw_ostream &OS) const {
OS << '[' << toString(From(), 10) << ", " << toString(To(), 10) << ']';
}
+LLVM_DUMP_METHOD void Range::dump() const { dump(llvm::errs()); }
-void RangeSet::dump(raw_ostream &OS) const {
+LLVM_DUMP_METHOD void RangeSet::dump(raw_ostream &OS) const {
OS << "{ ";
llvm::interleaveComma(*this, OS, [&OS](const Range &R) { R.dump(OS); });
OS << " }";
}
+LLVM_DUMP_METHOD void RangeSet::dump() const { dump(llvm::errs()); }
REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(SymbolSet, SymbolRef)
@@ -545,20 +914,20 @@ namespace {
class EquivalenceClass : public llvm::FoldingSetNode {
public:
/// Find equivalence class for the given symbol in the given state.
- LLVM_NODISCARD static inline EquivalenceClass find(ProgramStateRef State,
- SymbolRef Sym);
+ [[nodiscard]] static inline EquivalenceClass find(ProgramStateRef State,
+ SymbolRef Sym);
/// Merge classes for the given symbols and return a new state.
- LLVM_NODISCARD static inline ProgramStateRef merge(RangeSet::Factory &F,
- ProgramStateRef State,
- SymbolRef First,
- SymbolRef Second);
+ [[nodiscard]] static inline ProgramStateRef merge(RangeSet::Factory &F,
+ ProgramStateRef State,
+ SymbolRef First,
+ SymbolRef Second);
// Merge this class with the given class and return a new state.
- LLVM_NODISCARD inline ProgramStateRef
+ [[nodiscard]] inline ProgramStateRef
merge(RangeSet::Factory &F, ProgramStateRef State, EquivalenceClass Other);
/// Return a set of class members for the given state.
- LLVM_NODISCARD inline SymbolSet getClassMembers(ProgramStateRef State) const;
+ [[nodiscard]] inline SymbolSet getClassMembers(ProgramStateRef State) const;
/// Return true if the current class is trivial in the given state.
/// A class is trivial if and only if there is not any member relations stored
@@ -571,39 +940,42 @@ public:
/// members and then during the removal of dead symbols we remove one of its
/// members. In this case, the class is still non-trivial (it still has the
/// mappings in ClassMembers), even though it has only one member.
- LLVM_NODISCARD inline bool isTrivial(ProgramStateRef State) const;
+ [[nodiscard]] inline bool isTrivial(ProgramStateRef State) const;
/// Return true if the current class is trivial and its only member is dead.
- LLVM_NODISCARD inline bool isTriviallyDead(ProgramStateRef State,
- SymbolReaper &Reaper) const;
+ [[nodiscard]] inline bool isTriviallyDead(ProgramStateRef State,
+ SymbolReaper &Reaper) const;
- LLVM_NODISCARD static inline ProgramStateRef
+ [[nodiscard]] static inline ProgramStateRef
markDisequal(RangeSet::Factory &F, ProgramStateRef State, SymbolRef First,
SymbolRef Second);
- LLVM_NODISCARD static inline ProgramStateRef
+ [[nodiscard]] static inline ProgramStateRef
markDisequal(RangeSet::Factory &F, ProgramStateRef State,
EquivalenceClass First, EquivalenceClass Second);
- LLVM_NODISCARD inline ProgramStateRef
+ [[nodiscard]] inline ProgramStateRef
markDisequal(RangeSet::Factory &F, ProgramStateRef State,
EquivalenceClass Other) const;
- LLVM_NODISCARD static inline ClassSet
- getDisequalClasses(ProgramStateRef State, SymbolRef Sym);
- LLVM_NODISCARD inline ClassSet
- getDisequalClasses(ProgramStateRef State) const;
- LLVM_NODISCARD inline ClassSet
+ [[nodiscard]] static inline ClassSet getDisequalClasses(ProgramStateRef State,
+ SymbolRef Sym);
+ [[nodiscard]] inline ClassSet getDisequalClasses(ProgramStateRef State) const;
+ [[nodiscard]] inline ClassSet
getDisequalClasses(DisequalityMapTy Map, ClassSet::Factory &Factory) const;
- LLVM_NODISCARD static inline Optional<bool> areEqual(ProgramStateRef State,
- EquivalenceClass First,
- EquivalenceClass Second);
- LLVM_NODISCARD static inline Optional<bool>
+ [[nodiscard]] static inline std::optional<bool>
+ areEqual(ProgramStateRef State, EquivalenceClass First,
+ EquivalenceClass Second);
+ [[nodiscard]] static inline std::optional<bool>
areEqual(ProgramStateRef State, SymbolRef First, SymbolRef Second);
+ /// Remove one member from the class.
+ [[nodiscard]] ProgramStateRef removeMember(ProgramStateRef State,
+ const SymbolRef Old);
+
/// Iterate over all symbols and try to simplify them.
- LLVM_NODISCARD static inline ProgramStateRef simplify(SValBuilder &SVB,
- RangeSet::Factory &F,
- ProgramStateRef State,
- EquivalenceClass Class);
+ [[nodiscard]] static inline ProgramStateRef simplify(SValBuilder &SVB,
+ RangeSet::Factory &F,
+ ProgramStateRef State,
+ EquivalenceClass Class);
void dumpToStream(ProgramStateRef State, raw_ostream &os) const;
LLVM_DUMP_METHOD void dump(ProgramStateRef State) const {
@@ -611,10 +983,10 @@ public:
}
/// Check equivalence data for consistency.
- LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED static bool
+ [[nodiscard]] LLVM_ATTRIBUTE_UNUSED static bool
isClassDataConsistent(ProgramStateRef State);
- LLVM_NODISCARD QualType getType() const {
+ [[nodiscard]] QualType getType() const {
return getRepresentativeSymbol()->getType();
}
@@ -655,6 +1027,7 @@ private:
inline ProgramStateRef mergeImpl(RangeSet::Factory &F, ProgramStateRef State,
SymbolSet Members, EquivalenceClass Other,
SymbolSet OtherMembers);
+
static inline bool
addToDisequalityInfo(DisequalityMapTy &Info, ConstraintRangeTy &Constraints,
RangeSet::Factory &F, ProgramStateRef State,
@@ -668,7 +1041,7 @@ private:
// Constraint functions
//===----------------------------------------------------------------------===//
-LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED bool
+[[nodiscard]] LLVM_ATTRIBUTE_UNUSED bool
areFeasible(ConstraintRangeTy Constraints) {
return llvm::none_of(
Constraints,
@@ -677,24 +1050,24 @@ areFeasible(ConstraintRangeTy Constraints) {
});
}
-LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State,
- EquivalenceClass Class) {
+[[nodiscard]] inline const RangeSet *getConstraint(ProgramStateRef State,
+ EquivalenceClass Class) {
return State->get<ConstraintRange>(Class);
}
-LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State,
- SymbolRef Sym) {
+[[nodiscard]] inline const RangeSet *getConstraint(ProgramStateRef State,
+ SymbolRef Sym) {
return getConstraint(State, EquivalenceClass::find(State, Sym));
}
-LLVM_NODISCARD ProgramStateRef setConstraint(ProgramStateRef State,
- EquivalenceClass Class,
- RangeSet Constraint) {
+[[nodiscard]] ProgramStateRef setConstraint(ProgramStateRef State,
+ EquivalenceClass Class,
+ RangeSet Constraint) {
return State->set<ConstraintRange>(Class, Constraint);
}
-LLVM_NODISCARD ProgramStateRef setConstraints(ProgramStateRef State,
- ConstraintRangeTy Constraints) {
+[[nodiscard]] ProgramStateRef setConstraints(ProgramStateRef State,
+ ConstraintRangeTy Constraints) {
return State->set<ConstraintRange>(Constraints);
}
@@ -710,8 +1083,8 @@ LLVM_NODISCARD ProgramStateRef setConstraints(ProgramStateRef State,
///
/// \returns true if assuming this Sym to be true means equality of operands
/// false if it means disequality of operands
-/// None otherwise
-Optional<bool> meansEquality(const SymSymExpr *Sym) {
+/// std::nullopt otherwise
+std::optional<bool> meansEquality(const SymSymExpr *Sym) {
switch (Sym->getOpcode()) {
case BO_Sub:
// This case is: A - B != 0 -> disequality check.
@@ -723,7 +1096,7 @@ Optional<bool> meansEquality(const SymSymExpr *Sym) {
// This case is: A != B != 0 -> diseqiality check.
return false;
default:
- return llvm::None;
+ return std::nullopt;
}
}
@@ -732,8 +1105,8 @@ Optional<bool> meansEquality(const SymSymExpr *Sym) {
//===----------------------------------------------------------------------===//
template <class SecondTy, class... RestTy>
-LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
- SecondTy Second, RestTy... Tail);
+[[nodiscard]] inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
+ SecondTy Second, RestTy... Tail);
template <class... RangeTy> struct IntersectionTraits;
@@ -745,7 +1118,7 @@ template <class... TailTy> struct IntersectionTraits<RangeSet, TailTy...> {
template <> struct IntersectionTraits<> {
// We ran out of types, and we didn't find any RangeSet, so the result should
// be optional.
- using Type = Optional<RangeSet>;
+ using Type = std::optional<RangeSet>;
};
template <class OptionalOrPointer, class... TailTy>
@@ -755,32 +1128,33 @@ struct IntersectionTraits<OptionalOrPointer, TailTy...> {
};
template <class EndTy>
-LLVM_NODISCARD inline EndTy intersect(RangeSet::Factory &F, EndTy End) {
- // If the list contains only RangeSet or Optional<RangeSet>, simply return
- // that range set.
+[[nodiscard]] inline EndTy intersect(RangeSet::Factory &F, EndTy End) {
+ // If the list contains only RangeSet or std::optional<RangeSet>, simply
+ // return that range set.
return End;
}
-LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED inline Optional<RangeSet>
+[[nodiscard]] LLVM_ATTRIBUTE_UNUSED inline std::optional<RangeSet>
intersect(RangeSet::Factory &F, const RangeSet *End) {
- // This is an extraneous conversion from a raw pointer into Optional<RangeSet>
+ // This is an extraneous conversion from a raw pointer into
+ // std::optional<RangeSet>
if (End) {
return *End;
}
- return llvm::None;
+ return std::nullopt;
}
template <class... RestTy>
-LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
- RangeSet Second, RestTy... Tail) {
+[[nodiscard]] inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
+ RangeSet Second, RestTy... Tail) {
// Here we call either the <RangeSet,RangeSet,...> or <RangeSet,...> version
// of the function and can be sure that the result is RangeSet.
return intersect(F, F.intersect(Head, Second), Tail...);
}
template <class SecondTy, class... RestTy>
-LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
- SecondTy Second, RestTy... Tail) {
+[[nodiscard]] inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
+ SecondTy Second, RestTy... Tail) {
if (Second) {
// Here we call the <RangeSet,RangeSet,...> version of the function...
return intersect(F, Head, *Second, Tail...);
@@ -792,11 +1166,12 @@ LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
/// Main generic intersect function.
/// It intersects all of the given range sets. If some of the given arguments
-/// don't hold a range set (nullptr or llvm::None), the function will skip them.
+/// don't hold a range set (nullptr or std::nullopt), the function will skip
+/// them.
///
/// Available representations for the arguments are:
/// * RangeSet
-/// * Optional<RangeSet>
+/// * std::optional<RangeSet>
/// * RangeSet *
/// Pointer to a RangeSet is automatically assumed to be nullable and will get
/// checked as well as the optional version. If this behaviour is undesired,
@@ -804,13 +1179,14 @@ LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
///
/// Return type depends on the arguments' types. If we can be sure in compile
/// time that there will be a range set as a result, the returning type is
-/// simply RangeSet, in other cases we have to back off to Optional<RangeSet>.
+/// simply RangeSet, in other cases we have to back off to
+/// std::optional<RangeSet>.
///
/// Please, prefer optional range sets to raw pointers. If the last argument is
-/// a raw pointer and all previous arguments are None, it will cost one
-/// additional check to convert RangeSet * into Optional<RangeSet>.
+/// a raw pointer and all previous arguments are std::nullopt, it will cost one
+/// additional check to convert RangeSet * into std::optional<RangeSet>.
template <class HeadTy, class SecondTy, class... RestTy>
-LLVM_NODISCARD inline
+[[nodiscard]] inline
typename IntersectionTraits<HeadTy, SecondTy, RestTy...>::Type
intersect(RangeSet::Factory &F, HeadTy Head, SecondTy Second,
RestTy... Tail) {
@@ -840,13 +1216,21 @@ public:
}
RangeSet VisitSymExpr(SymbolRef Sym) {
- // If we got to this function, the actual type of the symbolic
+ if (std::optional<RangeSet> RS = getRangeForNegatedSym(Sym))
+ return *RS;
+ // If we've reached this line, 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 VisitUnarySymExpr(const UnarySymExpr *USE) {
+ if (std::optional<RangeSet> RS = getRangeForNegatedUnarySym(USE))
+ return *RS;
+ return infer(USE->getType());
+ }
+
RangeSet VisitSymIntExpr(const SymIntExpr *Sym) {
return VisitBinaryOperator(Sym);
}
@@ -855,14 +1239,25 @@ public:
return VisitBinaryOperator(Sym);
}
- RangeSet VisitSymSymExpr(const SymSymExpr *Sym) {
+ RangeSet VisitSymSymExpr(const SymSymExpr *SSE) {
return intersect(
RangeFactory,
+ // If Sym is a difference of symbols A - B, then maybe we have range
+ // set stored for B - A.
+ //
+ // 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.
+ getRangeForNegatedSymSym(SSE),
+ // If Sym is a comparison expression (except <=>),
+ // find any other comparisons with the same operands.
+ // See function description.
+ getRangeForComparisonSymbol(SSE),
// If Sym is (dis)equality, we might have some information
// on that in our equality classes data structure.
- getRangeForEqualities(Sym),
+ getRangeForEqualities(SSE),
// And we should always check what we can get from the operands.
- VisitBinaryOperator(Sym));
+ VisitBinaryOperator(SSE));
}
private:
@@ -891,25 +1286,13 @@ private:
}
RangeSet infer(SymbolRef Sym) {
- return intersect(
- RangeFactory,
- // Of course, we should take the constraint directly associated with
- // this symbol into consideration.
- getConstraint(State, Sym),
- // If Sym is a difference of symbols A - B, then maybe we have range
- // set stored for B - A.
- //
- // 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.
- getRangeForNegatedSub(Sym),
- // If Sym is a comparison expression (except <=>),
- // find any other comparisons with the same operands.
- // See function description.
- getRangeForComparisonSymbol(Sym),
- // Apart from the Sym itself, we can infer quite a lot if we look
- // into subexpressions of Sym.
- Visit(Sym));
+ return intersect(RangeFactory,
+ // Of course, we should take the constraint directly
+ // associated with this symbol into consideration.
+ getConstraint(State, Sym),
+ // Apart from the Sym itself, we can infer quite a lot if
+ // we look into subexpressions of Sym.
+ Visit(Sym));
}
RangeSet infer(EquivalenceClass Class) {
@@ -953,18 +1336,7 @@ private:
}
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);
- }
- }
+ RangeSet RHS, QualType T);
//===----------------------------------------------------------------------===//
// Ranges and operators
@@ -982,11 +1354,11 @@ private:
/// 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) {
+ /// It will return std::nullopt only when the trivial conversion is possible.
+ std::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 std::nullopt;
}
return Range(ValueFactory.Convert(To, Origin.From()),
ValueFactory.Convert(To, Origin.To()));
@@ -994,11 +1366,7 @@ private:
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();
- }
+ assert(!LHS.isEmpty() && !RHS.isEmpty());
Range CoarseLHS = fillGaps(LHS);
Range CoarseRHS = fillGaps(RHS);
@@ -1070,31 +1438,51 @@ private:
return RangeFactory.deletePoint(Domain, 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.
- Optional<RangeSet> getRangeForNegatedSub(SymbolRef Sym) {
- if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) {
- if (SSE->getOpcode() == BO_Sub) {
- QualType T = Sym->getType();
+ template <typename ProduceNegatedSymFunc>
+ std::optional<RangeSet> getRangeForNegatedExpr(ProduceNegatedSymFunc F,
+ QualType T) {
+ // Do not negate if the type cannot be meaningfully negated.
+ if (!T->isUnsignedIntegerOrEnumerationType() &&
+ !T->isSignedIntegerOrEnumerationType())
+ return std::nullopt;
+
+ if (SymbolRef NegatedSym = F())
+ if (const RangeSet *NegatedRange = getConstraint(State, NegatedSym))
+ return RangeFactory.negate(*NegatedRange);
- // Do not negate unsigned ranges
- if (!T->isUnsignedIntegerOrEnumerationType() &&
- !T->isSignedIntegerOrEnumerationType())
- return llvm::None;
+ return std::nullopt;
+ }
- SymbolManager &SymMgr = State->getSymbolManager();
- SymbolRef NegatedSym =
- SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), T);
+ std::optional<RangeSet> getRangeForNegatedUnarySym(const UnarySymExpr *USE) {
+ // Just get the operand when we negate a symbol that is already negated.
+ // -(-a) == a
+ return getRangeForNegatedExpr(
+ [USE]() -> SymbolRef {
+ if (USE->getOpcode() == UO_Minus)
+ return USE->getOperand();
+ return nullptr;
+ },
+ USE->getType());
+ }
- if (const RangeSet *NegatedRange = getConstraint(State, NegatedSym)) {
- return RangeFactory.negate(*NegatedRange);
- }
- }
- }
- return llvm::None;
+ std::optional<RangeSet> getRangeForNegatedSymSym(const SymSymExpr *SSE) {
+ return getRangeForNegatedExpr(
+ [SSE, State = this->State]() -> SymbolRef {
+ if (SSE->getOpcode() == BO_Sub)
+ return State->getSymbolManager().getSymSymExpr(
+ SSE->getRHS(), BO_Sub, SSE->getLHS(), SSE->getType());
+ return nullptr;
+ },
+ SSE->getType());
+ }
+
+ std::optional<RangeSet> getRangeForNegatedSym(SymbolRef Sym) {
+ return getRangeForNegatedExpr(
+ [Sym, State = this->State]() {
+ return State->getSymbolManager().getUnarySymExpr(Sym, UO_Minus,
+ Sym->getType());
+ },
+ Sym->getType());
}
// Returns ranges only for binary comparison operators (except <=>)
@@ -1107,16 +1495,12 @@ private:
// It covers all possible combinations (see CmpOpTable description).
// Note that `x` and `y` can also stand for subexpressions,
// not only for actual symbols.
- Optional<RangeSet> getRangeForComparisonSymbol(SymbolRef Sym) {
- const auto *SSE = dyn_cast<SymSymExpr>(Sym);
- if (!SSE)
- return llvm::None;
-
- BinaryOperatorKind CurrentOP = SSE->getOpcode();
+ std::optional<RangeSet> getRangeForComparisonSymbol(const SymSymExpr *SSE) {
+ const BinaryOperatorKind CurrentOP = SSE->getOpcode();
// We currently do not support <=> (C++20).
if (!BinaryOperator::isComparisonOp(CurrentOP) || (CurrentOP == BO_Cmp))
- return llvm::None;
+ return std::nullopt;
static const OperatorRelationsTable CmpOpTable{};
@@ -1126,7 +1510,12 @@ private:
SymbolManager &SymMgr = State->getSymbolManager();
- int UnknownStates = 0;
+ // We use this variable to store the last queried operator (`QueriedOP`)
+ // for which the `getCmpOpState` returned with `Unknown`. If there are two
+ // different OPs that returned `Unknown` then we have to query the special
+ // `UnknownX2` column. We assume that `getCmpOpState(CurrentOP, CurrentOP)`
+ // never returns `Unknown`, so `CurrentOP` is a good initial value.
+ BinaryOperatorKind LastQueriedOpToUnknown = CurrentOP;
// Loop goes through all of the columns exept the last one ('UnknownX2').
// We treat `UnknownX2` column separately at the end of the loop body.
@@ -1163,31 +1552,34 @@ private:
CmpOpTable.getCmpOpState(CurrentOP, QueriedOP);
if (BranchState == OperatorRelationsTable::Unknown) {
- if (++UnknownStates == 2)
- // If we met both Unknown states.
+ if (LastQueriedOpToUnknown != CurrentOP &&
+ LastQueriedOpToUnknown != QueriedOP) {
+ // If we got the Unknown state for both different operators.
// 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
+ } else {
+ LastQueriedOpToUnknown = QueriedOP;
continue;
+ }
}
return (BranchState == OperatorRelationsTable::True) ? getTrueRange(T)
: getFalseRange(T);
}
- return llvm::None;
+ return std::nullopt;
}
- Optional<RangeSet> getRangeForEqualities(const SymSymExpr *Sym) {
- Optional<bool> Equality = meansEquality(Sym);
+ std::optional<RangeSet> getRangeForEqualities(const SymSymExpr *Sym) {
+ std::optional<bool> Equality = meansEquality(Sym);
if (!Equality)
- return llvm::None;
+ return std::nullopt;
- if (Optional<bool> AreEqual =
+ if (std::optional<bool> AreEqual =
EquivalenceClass::areEqual(State, Sym->getLHS(), Sym->getRHS())) {
// Here we cover two cases at once:
// * if Sym is equality and its operands are known to be equal -> true
@@ -1199,7 +1591,7 @@ private:
return getFalseRange(Sym->getType());
}
- return llvm::None;
+ return std::nullopt;
}
RangeSet getTrueRange(QualType T) {
@@ -1222,6 +1614,57 @@ private:
//===----------------------------------------------------------------------===//
template <>
+RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_NE>(RangeSet LHS,
+ RangeSet RHS,
+ QualType T) {
+ assert(!LHS.isEmpty() && !RHS.isEmpty());
+
+ if (LHS.getAPSIntType() == RHS.getAPSIntType()) {
+ if (intersect(RangeFactory, LHS, RHS).isEmpty())
+ return getTrueRange(T);
+
+ } else {
+ // We can only lose information if we are casting smaller signed type to
+ // bigger unsigned type. For e.g.,
+ // LHS (unsigned short): [2, USHRT_MAX]
+ // RHS (signed short): [SHRT_MIN, 0]
+ //
+ // Casting RHS to LHS type will leave us with overlapping values
+ // CastedRHS : [0, 0] U [SHRT_MAX + 1, USHRT_MAX]
+ //
+ // We can avoid this by checking if signed type's maximum value is lesser
+ // than unsigned type's minimum value.
+
+ // If both have different signs then only we can get more information.
+ if (LHS.isUnsigned() != RHS.isUnsigned()) {
+ if (LHS.isUnsigned() && (LHS.getBitWidth() >= RHS.getBitWidth())) {
+ if (RHS.getMaxValue().isNegative() ||
+ LHS.getAPSIntType().convert(RHS.getMaxValue()) < LHS.getMinValue())
+ return getTrueRange(T);
+
+ } else if (RHS.isUnsigned() && (LHS.getBitWidth() <= RHS.getBitWidth())) {
+ if (LHS.getMaxValue().isNegative() ||
+ RHS.getAPSIntType().convert(LHS.getMaxValue()) < RHS.getMinValue())
+ return getTrueRange(T);
+ }
+ }
+
+ // Both RangeSets should be casted to bigger unsigned type.
+ APSIntType CastingType(std::max(LHS.getBitWidth(), RHS.getBitWidth()),
+ LHS.isUnsigned() || RHS.isUnsigned());
+
+ RangeSet CastedLHS = RangeFactory.castTo(LHS, CastingType);
+ RangeSet CastedRHS = RangeFactory.castTo(RHS, CastingType);
+
+ if (intersect(RangeFactory, CastedLHS, CastedRHS).isEmpty())
+ return getTrueRange(T);
+ }
+
+ // In all other cases, the resulting range cannot be deduced.
+ return infer(T);
+}
+
+template <>
RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Or>(Range LHS, Range RHS,
QualType T) {
APSIntType ResultType = ValueFactory.getAPSIntType(T);
@@ -1381,6 +1824,144 @@ RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Rem>(Range LHS,
return {RangeFactory, ValueFactory.getValue(Min), ValueFactory.getValue(Max)};
}
+RangeSet SymbolicRangeInferrer::VisitBinaryOperator(RangeSet LHS,
+ BinaryOperator::Opcode Op,
+ 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();
+ }
+
+ switch (Op) {
+ case BO_NE:
+ return VisitBinaryOperator<BO_NE>(LHS, RHS, T);
+ 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);
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Constraint manager implementation details
+//===----------------------------------------------------------------------===//
+
+class RangeConstraintManager : public RangedConstraintManager {
+public:
+ RangeConstraintManager(ExprEngine *EE, SValBuilder &SVB)
+ : RangedConstraintManager(EE, SVB), F(getBasicVals()) {}
+
+ //===------------------------------------------------------------------===//
+ // Implementation for interface from ConstraintManager.
+ //===------------------------------------------------------------------===//
+
+ bool haveEqualConstraints(ProgramStateRef S1,
+ ProgramStateRef S2) const override {
+ // NOTE: ClassMembers are as simple as back pointers for ClassMap,
+ // so comparing constraint ranges and class maps should be
+ // sufficient.
+ return S1->get<ConstraintRange>() == S2->get<ConstraintRange>() &&
+ S1->get<ClassMap>() == S2->get<ClassMap>();
+ }
+
+ bool canReasonAbout(SVal X) const override;
+
+ ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override;
+
+ const llvm::APSInt *getSymVal(ProgramStateRef State,
+ SymbolRef Sym) const override;
+
+ const llvm::APSInt *getSymMinVal(ProgramStateRef State,
+ SymbolRef Sym) const override;
+
+ const llvm::APSInt *getSymMaxVal(ProgramStateRef State,
+ SymbolRef Sym) const override;
+
+ ProgramStateRef removeDeadBindings(ProgramStateRef State,
+ SymbolReaper &SymReaper) override;
+
+ void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n",
+ unsigned int Space = 0, bool IsDot = false) const override;
+ void printValue(raw_ostream &Out, ProgramStateRef State,
+ SymbolRef Sym) override;
+ void printConstraints(raw_ostream &Out, ProgramStateRef State,
+ const char *NL = "\n", unsigned int Space = 0,
+ bool IsDot = false) const;
+ void printEquivalenceClasses(raw_ostream &Out, ProgramStateRef State,
+ const char *NL = "\n", unsigned int Space = 0,
+ bool IsDot = false) const;
+ void printDisequalities(raw_ostream &Out, ProgramStateRef State,
+ const char *NL = "\n", unsigned int Space = 0,
+ bool IsDot = false) const;
+
+ //===------------------------------------------------------------------===//
+ // Implementation for interface from RangedConstraintManager.
+ //===------------------------------------------------------------------===//
+
+ ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymLT(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymGT(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymLE(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymGE(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymWithinInclusiveRange(
+ ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
+ const llvm::APSInt &To, const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymOutsideInclusiveRange(
+ ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
+ const llvm::APSInt &To, const llvm::APSInt &Adjustment) override;
+
+private:
+ RangeSet::Factory F;
+
+ RangeSet getRange(ProgramStateRef State, SymbolRef Sym);
+ RangeSet getRange(ProgramStateRef State, EquivalenceClass Class);
+ ProgramStateRef setRange(ProgramStateRef State, SymbolRef Sym,
+ RangeSet Range);
+ ProgramStateRef setRange(ProgramStateRef State, EquivalenceClass Class,
+ RangeSet Range);
+
+ RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment);
+ RangeSet getSymGTRange(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment);
+ RangeSet getSymLERange(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment);
+ RangeSet getSymLERange(llvm::function_ref<RangeSet()> RS,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment);
+ RangeSet getSymGERange(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment);
+};
+
//===----------------------------------------------------------------------===//
// Constraint assignment logic
//===----------------------------------------------------------------------===//
@@ -1482,7 +2063,7 @@ public:
class ConstraintAssignor : public ConstraintAssignorBase<ConstraintAssignor> {
public:
template <class ClassOrSymbol>
- LLVM_NODISCARD static ProgramStateRef
+ [[nodiscard]] static ProgramStateRef
assign(ProgramStateRef State, SValBuilder &Builder, RangeSet::Factory &F,
ClassOrSymbol CoS, RangeSet NewConstraint) {
if (!State || NewConstraint.isEmpty())
@@ -1492,7 +2073,28 @@ public:
return Assignor.assign(CoS, NewConstraint);
}
+ /// Handle expressions like: a % b != 0.
+ template <typename SymT>
+ bool handleRemainderOp(const SymT *Sym, RangeSet Constraint) {
+ if (Sym->getOpcode() != BO_Rem)
+ return true;
+ // a % b != 0 implies that a != 0.
+ if (!Constraint.containsZero()) {
+ SVal SymSVal = Builder.makeSymbolVal(Sym->getLHS());
+ if (auto NonLocSymSVal = SymSVal.getAs<nonloc::SymbolVal>()) {
+ State = State->assume(*NonLocSymSVal, true);
+ if (!State)
+ return false;
+ }
+ }
+ return true;
+ }
+
inline bool assignSymExprToConst(const SymExpr *Sym, Const Constraint);
+ inline bool assignSymIntExprToRangeSet(const SymIntExpr *Sym,
+ RangeSet Constraint) {
+ return handleRemainderOp(Sym, Constraint);
+ }
inline bool assignSymSymExprToRangeSet(const SymSymExpr *Sym,
RangeSet Constraint);
@@ -1503,7 +2105,7 @@ private:
using Base = ConstraintAssignorBase<ConstraintAssignor>;
/// Base method for handling new constraints for symbols.
- LLVM_NODISCARD ProgramStateRef assign(SymbolRef Sym, RangeSet NewConstraint) {
+ [[nodiscard]] ProgramStateRef assign(SymbolRef Sym, RangeSet NewConstraint) {
// All constraints are actually associated with equivalence classes, and
// that's what we are going to do first.
State = assign(EquivalenceClass::find(State, Sym), NewConstraint);
@@ -1517,8 +2119,8 @@ private:
}
/// Base method for handling new constraints for classes.
- LLVM_NODISCARD ProgramStateRef assign(EquivalenceClass Class,
- RangeSet NewConstraint) {
+ [[nodiscard]] ProgramStateRef assign(EquivalenceClass Class,
+ RangeSet NewConstraint) {
// There is a chance that we might need to update constraints for the
// classes that are known to be disequal to Class.
//
@@ -1564,18 +2166,16 @@ private:
return EquivalenceClass::merge(RangeFactory, State, LHS, RHS);
}
- LLVM_NODISCARD Optional<bool> interpreteAsBool(RangeSet Constraint) {
+ [[nodiscard]] std::optional<bool> interpreteAsBool(RangeSet Constraint) {
assert(!Constraint.isEmpty() && "Empty ranges shouldn't get here");
if (Constraint.getConcreteValue())
- return !Constraint.getConcreteValue()->isNullValue();
+ return !Constraint.getConcreteValue()->isZero();
- APSIntType T{Constraint.getMinValue()};
- Const Zero = T.getZeroValue();
- if (!Constraint.contains(Zero))
+ if (!Constraint.containsZero())
return true;
- return llvm::None;
+ return std::nullopt;
}
ProgramStateRef State;
@@ -1583,113 +2183,6 @@ private:
RangeSet::Factory &RangeFactory;
};
-//===----------------------------------------------------------------------===//
-// Constraint manager implementation details
-//===----------------------------------------------------------------------===//
-
-class RangeConstraintManager : public RangedConstraintManager {
-public:
- RangeConstraintManager(ExprEngine *EE, SValBuilder &SVB)
- : RangedConstraintManager(EE, SVB), F(getBasicVals()) {}
-
- //===------------------------------------------------------------------===//
- // Implementation for interface from ConstraintManager.
- //===------------------------------------------------------------------===//
-
- bool haveEqualConstraints(ProgramStateRef S1,
- ProgramStateRef S2) const override {
- // NOTE: ClassMembers are as simple as back pointers for ClassMap,
- // so comparing constraint ranges and class maps should be
- // sufficient.
- return S1->get<ConstraintRange>() == S2->get<ConstraintRange>() &&
- S1->get<ClassMap>() == S2->get<ClassMap>();
- }
-
- bool canReasonAbout(SVal X) const override;
-
- ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override;
-
- const llvm::APSInt *getSymVal(ProgramStateRef State,
- SymbolRef Sym) const override;
-
- ProgramStateRef removeDeadBindings(ProgramStateRef State,
- SymbolReaper &SymReaper) override;
-
- void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n",
- unsigned int Space = 0, bool IsDot = false) const override;
- void printConstraints(raw_ostream &Out, ProgramStateRef State,
- const char *NL = "\n", unsigned int Space = 0,
- bool IsDot = false) const;
- void printEquivalenceClasses(raw_ostream &Out, ProgramStateRef State,
- const char *NL = "\n", unsigned int Space = 0,
- bool IsDot = false) const;
- void printDisequalities(raw_ostream &Out, ProgramStateRef State,
- const char *NL = "\n", unsigned int Space = 0,
- bool IsDot = false) const;
-
- //===------------------------------------------------------------------===//
- // Implementation for interface from RangedConstraintManager.
- //===------------------------------------------------------------------===//
-
- ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) override;
-
- ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) override;
-
- ProgramStateRef assumeSymLT(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) override;
-
- ProgramStateRef assumeSymGT(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) override;
-
- ProgramStateRef assumeSymLE(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) override;
-
- ProgramStateRef assumeSymGE(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) override;
-
- ProgramStateRef assumeSymWithinInclusiveRange(
- ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
- const llvm::APSInt &To, const llvm::APSInt &Adjustment) override;
-
- ProgramStateRef assumeSymOutsideInclusiveRange(
- ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
- const llvm::APSInt &To, const llvm::APSInt &Adjustment) override;
-
-private:
- RangeSet::Factory F;
-
- RangeSet getRange(ProgramStateRef State, SymbolRef Sym);
- RangeSet getRange(ProgramStateRef State, EquivalenceClass Class);
- ProgramStateRef setRange(ProgramStateRef State, SymbolRef Sym,
- RangeSet Range);
- ProgramStateRef setRange(ProgramStateRef State, EquivalenceClass Class,
- RangeSet Range);
-
- RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym,
- const llvm::APSInt &Int,
- const llvm::APSInt &Adjustment);
- RangeSet getSymGTRange(ProgramStateRef St, SymbolRef Sym,
- const llvm::APSInt &Int,
- const llvm::APSInt &Adjustment);
- RangeSet getSymLERange(ProgramStateRef St, SymbolRef Sym,
- const llvm::APSInt &Int,
- const llvm::APSInt &Adjustment);
- RangeSet getSymLERange(llvm::function_ref<RangeSet()> RS,
- const llvm::APSInt &Int,
- const llvm::APSInt &Adjustment);
- RangeSet getSymGERange(ProgramStateRef St, SymbolRef Sym,
- const llvm::APSInt &Int,
- const llvm::APSInt &Adjustment);
-};
-
bool ConstraintAssignor::assignSymExprToConst(const SymExpr *Sym,
const llvm::APSInt &Constraint) {
llvm::SmallSet<EquivalenceClass, 4> SimplifiedClasses;
@@ -1716,17 +2209,32 @@ bool ConstraintAssignor::assignSymExprToConst(const SymExpr *Sym,
return false;
}
+ // We may have trivial equivalence classes in the disequality info as
+ // well, and we need to simplify them.
+ DisequalityMapTy DisequalityInfo = State->get<DisequalityMap>();
+ for (std::pair<EquivalenceClass, ClassSet> DisequalityEntry :
+ DisequalityInfo) {
+ EquivalenceClass Class = DisequalityEntry.first;
+ ClassSet DisequalClasses = DisequalityEntry.second;
+ State = EquivalenceClass::simplify(Builder, RangeFactory, State, Class);
+ if (!State)
+ return false;
+ }
+
return true;
}
bool ConstraintAssignor::assignSymSymExprToRangeSet(const SymSymExpr *Sym,
RangeSet Constraint) {
- Optional<bool> ConstraintAsBool = interpreteAsBool(Constraint);
+ if (!handleRemainderOp(Sym, Constraint))
+ return false;
+
+ std::optional<bool> ConstraintAsBool = interpreteAsBool(Constraint);
if (!ConstraintAsBool)
return true;
- if (Optional<bool> Equality = meansEquality(Sym)) {
+ if (std::optional<bool> Equality = meansEquality(Sym)) {
// Here we cover two cases:
// * if Sym is equality and the new constraint is true -> Sym's operands
// should be marked as equal
@@ -1864,7 +2372,7 @@ EquivalenceClass::mergeImpl(RangeSet::Factory &RangeFactory,
//
// Intersection here makes perfect sense because both of these constraints
// must hold for the whole new class.
- if (Optional<RangeSet> NewClassConstraint =
+ if (std::optional<RangeSet> NewClassConstraint =
intersect(RangeFactory, getConstraint(State, *this),
getConstraint(State, Other))) {
// NOTE: Essentially, NewClassConstraint should NEVER be infeasible because
@@ -2062,16 +2570,16 @@ inline bool EquivalenceClass::addToDisequalityInfo(
return true;
}
-inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
- SymbolRef FirstSym,
- SymbolRef SecondSym) {
+inline std::optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
+ SymbolRef FirstSym,
+ SymbolRef SecondSym) {
return EquivalenceClass::areEqual(State, find(State, FirstSym),
find(State, SecondSym));
}
-inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
- EquivalenceClass First,
- EquivalenceClass Second) {
+inline std::optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
+ EquivalenceClass First,
+ EquivalenceClass Second) {
// The same equivalence class => symbols are equal.
if (First == Second)
return true;
@@ -2083,7 +2591,61 @@ inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
return false;
// It is not clear.
- return llvm::None;
+ return std::nullopt;
+}
+
+[[nodiscard]] ProgramStateRef
+EquivalenceClass::removeMember(ProgramStateRef State, const SymbolRef Old) {
+
+ SymbolSet ClsMembers = getClassMembers(State);
+ assert(ClsMembers.contains(Old));
+
+ // Remove `Old`'s Class->Sym relation.
+ SymbolSet::Factory &F = getMembersFactory(State);
+ ClassMembersTy::Factory &EMFactory = State->get_context<ClassMembers>();
+ ClsMembers = F.remove(ClsMembers, Old);
+ // Ensure another precondition of the removeMember function (we can check
+ // this only with isEmpty, thus we have to do the remove first).
+ assert(!ClsMembers.isEmpty() &&
+ "Class should have had at least two members before member removal");
+ // Overwrite the existing members assigned to this class.
+ ClassMembersTy ClassMembersMap = State->get<ClassMembers>();
+ ClassMembersMap = EMFactory.add(ClassMembersMap, *this, ClsMembers);
+ State = State->set<ClassMembers>(ClassMembersMap);
+
+ // Remove `Old`'s Sym->Class relation.
+ ClassMapTy Classes = State->get<ClassMap>();
+ ClassMapTy::Factory &CMF = State->get_context<ClassMap>();
+ Classes = CMF.remove(Classes, Old);
+ State = State->set<ClassMap>(Classes);
+
+ return State;
+}
+
+// Re-evaluate an SVal with top-level `State->assume` logic.
+[[nodiscard]] ProgramStateRef
+reAssume(ProgramStateRef State, const RangeSet *Constraint, SVal TheValue) {
+ if (!Constraint)
+ return State;
+
+ const auto DefinedVal = TheValue.castAs<DefinedSVal>();
+
+ // If the SVal is 0, we can simply interpret that as `false`.
+ if (Constraint->encodesFalseRange())
+ return State->assume(DefinedVal, false);
+
+ // If the constraint does not encode 0 then we can interpret that as `true`
+ // AND as a Range(Set).
+ if (Constraint->encodesTrueRange()) {
+ State = State->assume(DefinedVal, true);
+ if (!State)
+ return nullptr;
+ // Fall through, re-assume based on the range values as well.
+ }
+ // Overestimate the individual Ranges with the RangeSet' lowest and
+ // highest values.
+ return State->assumeInclusiveRange(DefinedVal, Constraint->getMinValue(),
+ Constraint->getMaxValue(), true);
}
// Iterate over all symbols and try to simplify them. Once a symbol is
@@ -2091,19 +2653,78 @@ inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
// class to this class. This way, we simplify not just the symbols but the
// classes as well: we strive to keep the number of the classes to be the
// absolute minimum.
-LLVM_NODISCARD ProgramStateRef
+[[nodiscard]] ProgramStateRef
EquivalenceClass::simplify(SValBuilder &SVB, RangeSet::Factory &F,
ProgramStateRef State, EquivalenceClass Class) {
SymbolSet ClassMembers = Class.getClassMembers(State);
for (const SymbolRef &MemberSym : ClassMembers) {
- SymbolRef SimplifiedMemberSym = ento::simplify(State, MemberSym);
+
+ const SVal SimplifiedMemberVal = simplifyToSVal(State, MemberSym);
+ const SymbolRef SimplifiedMemberSym = SimplifiedMemberVal.getAsSymbol();
+
+ // The symbol is collapsed to a constant, check if the current State is
+ // still feasible.
+ if (const auto CI = SimplifiedMemberVal.getAs<nonloc::ConcreteInt>()) {
+ const llvm::APSInt &SV = CI->getValue();
+ const RangeSet *ClassConstraint = getConstraint(State, Class);
+ // We have found a contradiction.
+ if (ClassConstraint && !ClassConstraint->contains(SV))
+ return nullptr;
+ }
+
if (SimplifiedMemberSym && MemberSym != SimplifiedMemberSym) {
// The simplified symbol should be the member of the original Class,
// however, it might be in another existing class at the moment. We
// have to merge these classes.
+ ProgramStateRef OldState = State;
State = merge(F, State, MemberSym, SimplifiedMemberSym);
if (!State)
return nullptr;
+ // No state change, no merge happened actually.
+ if (OldState == State)
+ continue;
+
+ // Be aware that `SimplifiedMemberSym` might refer to an already dead
+ // symbol. In that case, the eqclass of that might not be the same as the
+ // eqclass of `MemberSym`. This is because the dead symbols are not
+ // preserved in the `ClassMap`, hence
+ // `find(State, SimplifiedMemberSym)` will result in a trivial eqclass
+ // compared to the eqclass of `MemberSym`.
+ // These eqclasses should be the same if `SimplifiedMemberSym` is alive.
+ // --> assert(find(State, MemberSym) == find(State, SimplifiedMemberSym))
+ //
+ // Note that `MemberSym` must be alive here since that is from the
+ // `ClassMembers` where all the symbols are alive.
+
+ // Remove the old and more complex symbol.
+ State = find(State, MemberSym).removeMember(State, MemberSym);
+
+ // Query the class constraint again b/c that may have changed during the
+ // merge above.
+ const RangeSet *ClassConstraint = getConstraint(State, Class);
+
+ // Re-evaluate an SVal with top-level `State->assume`, this ignites
+ // a RECURSIVE algorithm that will reach a FIXPOINT.
+ //
+ // About performance and complexity: Let us assume that in a State we
+ // have N non-trivial equivalence classes and that all constraints and
+ // disequality info is related to non-trivial classes. In the worst case,
+ // we can simplify only one symbol of one class in each iteration. The
+ // number of symbols in one class cannot grow b/c we replace the old
+ // symbol with the simplified one. Also, the number of the equivalence
+ // classes can decrease only, b/c the algorithm does a merge operation
+ // optionally. We need N iterations in this case to reach the fixpoint.
+ // Thus, the steps needed to be done in the worst case is proportional to
+ // N*N.
+ //
+ // This worst case scenario can be extended to that case when we have
+ // trivial classes in the constraints and in the disequality map. This
+ // case can be reduced to the case with a State where there are only
+ // non-trivial classes. This is because a merge operation on two trivial
+ // classes results in one non-trivial class.
+ State = reAssume(State, ClassConstraint, SimplifiedMemberVal);
+ if (!State)
+ return nullptr;
}
}
return State;
@@ -2173,7 +2794,7 @@ bool EquivalenceClass::isClassDataConsistent(ProgramStateRef State) {
//===----------------------------------------------------------------------===//
bool RangeConstraintManager::canReasonAbout(SVal X) const {
- Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>();
+ std::optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>();
if (SymVal && SymVal->isExpression()) {
const SymExpr *SE = SymVal->getSymbol();
@@ -2248,6 +2869,22 @@ const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St,
return T ? T->getConcreteValue() : nullptr;
}
+const llvm::APSInt *RangeConstraintManager::getSymMinVal(ProgramStateRef St,
+ SymbolRef Sym) const {
+ const RangeSet *T = getConstraint(St, Sym);
+ if (!T || T->isEmpty())
+ return nullptr;
+ return &T->getMinValue();
+}
+
+const llvm::APSInt *RangeConstraintManager::getSymMaxVal(ProgramStateRef St,
+ SymbolRef Sym) const {
+ const RangeSet *T = getConstraint(St, Sym);
+ if (!T || T->isEmpty())
+ return nullptr;
+ return &T->getMaxValue();
+}
+
//===----------------------------------------------------------------------===//
// Remove dead symbols from existing constraints
//===----------------------------------------------------------------------===//
@@ -2630,6 +3267,13 @@ void RangeConstraintManager::printJson(raw_ostream &Out, ProgramStateRef State,
printDisequalities(Out, State, NL, Space, IsDot);
}
+void RangeConstraintManager::printValue(raw_ostream &Out, ProgramStateRef State,
+ SymbolRef Sym) {
+ const RangeSet RS = getRange(State, Sym);
+ Out << RS.getBitWidth() << (RS.isUnsigned() ? "u:" : "s:");
+ RS.dump(Out);
+}
+
static std::string toString(const SymbolRef &Sym) {
std::string S;
llvm::raw_string_ostream O(S);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
index d227c025fb20..4bbe933be212 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
@@ -41,49 +41,55 @@ ProgramStateRef RangedConstraintManager::assumeSym(ProgramStateRef State,
return assumeSymRel(State, SIE->getLHS(), op, SIE->getRHS());
}
- } else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) {
+ // Handle adjustment with non-comparison ops.
+ const llvm::APSInt &Zero = getBasicVals().getValue(0, SIE->getType());
+ return assumeSymRel(State, SIE, (Assumption ? BO_NE : BO_EQ), Zero);
+ }
+
+ if (const auto *SSE = dyn_cast<SymSymExpr>(Sym)) {
BinaryOperator::Opcode Op = SSE->getOpcode();
- assert(BinaryOperator::isComparisonOp(Op));
-
- // We convert equality operations for pointers only.
- if (Loc::isLocType(SSE->getLHS()->getType()) &&
- Loc::isLocType(SSE->getRHS()->getType())) {
- // Translate "a != b" to "(b - a) != 0".
- // We invert the order of the operands as a heuristic for how loop
- // conditions are usually written ("begin != end") as compared to length
- // calculations ("end - begin"). The more correct thing to do would be to
- // canonicalize "a - b" and "b - a", which would allow us to treat
- // "a != b" and "b != a" the same.
-
- SymbolManager &SymMgr = getSymbolManager();
- QualType DiffTy = SymMgr.getContext().getPointerDiffType();
- SymbolRef Subtraction =
- SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), DiffTy);
-
- const llvm::APSInt &Zero = getBasicVals().getValue(0, DiffTy);
- Op = BinaryOperator::reverseComparisonOp(Op);
- if (!Assumption)
- Op = BinaryOperator::negateComparisonOp(Op);
- return assumeSymRel(State, Subtraction, Op, Zero);
- }
+ if (BinaryOperator::isComparisonOp(Op)) {
+
+ // We convert equality operations for pointers only.
+ if (Loc::isLocType(SSE->getLHS()->getType()) &&
+ Loc::isLocType(SSE->getRHS()->getType())) {
+ // Translate "a != b" to "(b - a) != 0".
+ // We invert the order of the operands as a heuristic for how loop
+ // conditions are usually written ("begin != end") as compared to length
+ // calculations ("end - begin"). The more correct thing to do would be
+ // to canonicalize "a - b" and "b - a", which would allow us to treat
+ // "a != b" and "b != a" the same.
+
+ SymbolManager &SymMgr = getSymbolManager();
+ QualType DiffTy = SymMgr.getContext().getPointerDiffType();
+ SymbolRef Subtraction =
+ SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), DiffTy);
+
+ const llvm::APSInt &Zero = getBasicVals().getValue(0, DiffTy);
+ Op = BinaryOperator::reverseComparisonOp(Op);
+ if (!Assumption)
+ Op = BinaryOperator::negateComparisonOp(Op);
+ return assumeSymRel(State, Subtraction, Op, Zero);
+ }
- if (BinaryOperator::isEqualityOp(Op)) {
- SymbolManager &SymMgr = getSymbolManager();
+ if (BinaryOperator::isEqualityOp(Op)) {
+ SymbolManager &SymMgr = getSymbolManager();
- QualType ExprType = SSE->getType();
- SymbolRef CanonicalEquality =
- SymMgr.getSymSymExpr(SSE->getLHS(), BO_EQ, SSE->getRHS(), ExprType);
+ QualType ExprType = SSE->getType();
+ SymbolRef CanonicalEquality =
+ SymMgr.getSymSymExpr(SSE->getLHS(), BO_EQ, SSE->getRHS(), ExprType);
- bool WasEqual = SSE->getOpcode() == BO_EQ;
- bool IsExpectedEqual = WasEqual == Assumption;
+ bool WasEqual = SSE->getOpcode() == BO_EQ;
+ bool IsExpectedEqual = WasEqual == Assumption;
- const llvm::APSInt &Zero = getBasicVals().getValue(0, ExprType);
+ const llvm::APSInt &Zero = getBasicVals().getValue(0, ExprType);
- if (IsExpectedEqual) {
- return assumeSymNE(State, CanonicalEquality, Zero, Zero);
- }
+ if (IsExpectedEqual) {
+ return assumeSymNE(State, CanonicalEquality, Zero, Zero);
+ }
- return assumeSymEQ(State, CanonicalEquality, Zero, Zero);
+ return assumeSymEQ(State, CanonicalEquality, Zero, Zero);
+ }
}
}
@@ -226,9 +232,13 @@ void RangedConstraintManager::computeAdjustment(SymbolRef &Sym,
}
}
-SymbolRef simplify(ProgramStateRef State, SymbolRef Sym) {
+SVal simplifyToSVal(ProgramStateRef State, SymbolRef Sym) {
SValBuilder &SVB = State->getStateManager().getSValBuilder();
- SVal SimplifiedVal = SVB.simplifySVal(State, SVB.makeSymbolVal(Sym));
+ return SVB.simplifySVal(State, SVB.makeSymbolVal(Sym));
+}
+
+SymbolRef simplify(ProgramStateRef State, SymbolRef Sym) {
+ SVal SimplifiedVal = simplifyToSVal(State, Sym);
if (SymbolRef SimplifiedSym = SimplifiedVal.getAsSymbol())
return SimplifiedSym;
return Sym;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 4ffa1aacb41f..da9a1a1a4d1f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -28,8 +28,9 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/ADT/ImmutableMap.h"
-#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
#include <utility>
using namespace clang;
@@ -62,8 +63,8 @@ private:
: P(r, k), Data(offset) {
assert(r && "Must have known regions.");
assert(getOffset() == offset && "Failed to store offset");
- assert((r == r->getBaseRegion() || isa<ObjCIvarRegion>(r) ||
- isa <CXXDerivedObjectRegion>(r)) &&
+ assert((r == r->getBaseRegion() ||
+ isa<ObjCIvarRegion, CXXDerivedObjectRegion>(r)) &&
"Not a base");
}
public:
@@ -212,11 +213,11 @@ public:
removeBinding(R, BindingKey::Default);
}
- Optional<SVal> getDirectBinding(const MemRegion *R) const;
+ std::optional<SVal> getDirectBinding(const MemRegion *R) const;
/// getDefaultBinding - Returns an SVal* representing an optional default
/// binding associated with a region and its subregions.
- Optional<SVal> getDefaultBinding(const MemRegion *R) const;
+ std::optional<SVal> getDefaultBinding(const MemRegion *R) const;
/// Return the internal tree as a Store.
Store asStore() const {
@@ -231,7 +232,7 @@ public:
void printJson(raw_ostream &Out, const char *NL = "\n",
unsigned int Space = 0, bool IsDot = false) const {
- for (iterator I = begin(); I != end(); ++I) {
+ for (iterator I = begin(), E = end(); I != E; ++I) {
// TODO: We might need a .printJson for I.getKey() as well.
Indent(Out, Space, IsDot)
<< "{ \"cluster\": \"" << I.getKey() << "\", \"pointer\": \""
@@ -239,18 +240,19 @@ public:
++Space;
const ClusterBindings &CB = I.getData();
- for (ClusterBindings::iterator CI = CB.begin(); CI != CB.end(); ++CI) {
+ for (ClusterBindings::iterator CI = CB.begin(), CE = CB.end(); CI != CE;
+ ++CI) {
Indent(Out, Space, IsDot) << "{ " << CI.getKey() << ", \"value\": ";
CI.getData().printJson(Out, /*AddQuotes=*/true);
Out << " }";
- if (std::next(CI) != CB.end())
+ if (std::next(CI) != CE)
Out << ',';
Out << NL;
}
--Space;
Indent(Out, Space, IsDot) << "]}";
- if (std::next(I) != end())
+ if (std::next(I) != E)
Out << ',';
Out << NL;
}
@@ -262,12 +264,16 @@ public:
typedef const RegionBindingsRef& RegionBindingsConstRef;
-Optional<SVal> RegionBindingsRef::getDirectBinding(const MemRegion *R) const {
- return Optional<SVal>::create(lookup(R, BindingKey::Direct));
+std::optional<SVal>
+RegionBindingsRef::getDirectBinding(const MemRegion *R) const {
+ const SVal *V = lookup(R, BindingKey::Direct);
+ return V ? std::optional<SVal>(*V) : std::nullopt;
}
-Optional<SVal> RegionBindingsRef::getDefaultBinding(const MemRegion *R) const {
- return Optional<SVal>::create(lookup(R, BindingKey::Default));
+std::optional<SVal>
+RegionBindingsRef::getDefaultBinding(const MemRegion *R) const {
+ const SVal *V = lookup(R, BindingKey::Default);
+ return V ? std::optional<SVal>(*V) : std::nullopt;
}
RegionBindingsRef RegionBindingsRef::addBinding(BindingKey K, SVal V) const {
@@ -318,29 +324,6 @@ RegionBindingsRef RegionBindingsRef::removeBinding(const MemRegion *R,
}
//===----------------------------------------------------------------------===//
-// Fine-grained control of RegionStoreManager.
-//===----------------------------------------------------------------------===//
-
-namespace {
-struct minimal_features_tag {};
-struct maximal_features_tag {};
-
-class RegionStoreFeatures {
- bool SupportsFields;
-public:
- RegionStoreFeatures(minimal_features_tag) :
- SupportsFields(false) {}
-
- RegionStoreFeatures(maximal_features_tag) :
- SupportsFields(true) {}
-
- void enableFields(bool t) { SupportsFields = t; }
-
- bool supportsFields() const { return SupportsFields; }
-};
-}
-
-//===----------------------------------------------------------------------===//
// Main RegionStore logic.
//===----------------------------------------------------------------------===//
@@ -349,8 +332,6 @@ class InvalidateRegionsWorker;
class RegionStoreManager : public StoreManager {
public:
- const RegionStoreFeatures Features;
-
RegionBindings::Factory RBFactory;
mutable ClusterBindings::Factory CBFactory;
@@ -370,6 +351,16 @@ private:
/// To disable all small-struct-dependent behavior, set the option to "0".
unsigned SmallStructLimit;
+ /// The largest number of element an array can have and still be
+ /// considered "small".
+ ///
+ /// This is currently used to decide whether or not it is worth "forcing" a
+ /// LazyCompoundVal on bind.
+ ///
+ /// This is controlled by 'region-store-small-struct-limit' option.
+ /// To disable all small-struct-dependent behavior, set the option to "0".
+ unsigned SmallArrayLimit;
+
/// A helper used to populate the work list with the given set of
/// regions.
void populateWorkList(InvalidateRegionsWorker &W,
@@ -377,16 +368,15 @@ private:
InvalidatedRegions *TopLevelRegions);
public:
- RegionStoreManager(ProgramStateManager& mgr, const RegionStoreFeatures &f)
- : StoreManager(mgr), Features(f),
- RBFactory(mgr.getAllocator()), CBFactory(mgr.getAllocator()),
- SmallStructLimit(0) {
+ RegionStoreManager(ProgramStateManager &mgr)
+ : StoreManager(mgr), RBFactory(mgr.getAllocator()),
+ CBFactory(mgr.getAllocator()), SmallStructLimit(0), SmallArrayLimit(0) {
ExprEngine &Eng = StateMgr.getOwningEngine();
AnalyzerOptions &Options = Eng.getAnalysisManager().options;
SmallStructLimit = Options.RegionStoreSmallStructLimit;
+ SmallArrayLimit = Options.RegionStoreSmallArrayLimit;
}
-
/// setImplicitDefaultValue - Set the default binding for the provided
/// MemRegion to the value implicitly defined for compound literals when
/// the value is not specified.
@@ -437,6 +427,15 @@ public:
RegionBindingsRef removeSubRegionBindings(RegionBindingsConstRef B,
const SubRegion *R);
+ std::optional<SVal>
+ getConstantValFromConstArrayInitializer(RegionBindingsConstRef B,
+ const ElementRegion *R);
+ std::optional<SVal>
+ getSValFromInitListExpr(const InitListExpr *ILE,
+ const SmallVector<uint64_t, 2> &ConcreteOffsets,
+ QualType ElemT);
+ SVal getSValFromStringLiteral(const StringLiteral *SL, uint64_t Offset,
+ QualType ElemT);
public: // Part of public interface to class.
@@ -490,12 +489,11 @@ public: // Part of public interface to class.
/// than using a Default binding at the base of the entire region. This is a
/// heuristic attempting to avoid building long chains of LazyCompoundVals.
///
- /// \returns The updated store bindings, or \c None if binding non-lazily
- /// would be too expensive.
- Optional<RegionBindingsRef> tryBindSmallStruct(RegionBindingsConstRef B,
- const TypedValueRegion *R,
- const RecordDecl *RD,
- nonloc::LazyCompoundVal LCV);
+ /// \returns The updated store bindings, or \c std::nullopt if binding
+ /// non-lazily would be too expensive.
+ std::optional<RegionBindingsRef>
+ tryBindSmallStruct(RegionBindingsConstRef B, const TypedValueRegion *R,
+ const RecordDecl *RD, nonloc::LazyCompoundVal LCV);
/// BindStruct - Bind a compound value to a structure.
RegionBindingsRef bindStruct(RegionBindingsConstRef B,
@@ -505,6 +503,10 @@ public: // Part of public interface to class.
RegionBindingsRef bindVector(RegionBindingsConstRef B,
const TypedValueRegion* R, SVal V);
+ std::optional<RegionBindingsRef>
+ tryBindSmallArray(RegionBindingsConstRef B, const TypedValueRegion *R,
+ const ArrayType *AT, nonloc::LazyCompoundVal LCV);
+
RegionBindingsRef bindArray(RegionBindingsConstRef B,
const TypedValueRegion* R,
SVal V);
@@ -550,7 +552,7 @@ public: // Part of public interface to class.
return getBinding(getRegionBindings(S), L, T);
}
- Optional<SVal> getDefaultBinding(Store S, const MemRegion *R) override {
+ std::optional<SVal> getDefaultBinding(Store S, const MemRegion *R) override {
RegionBindingsRef B = getRegionBindings(S);
// Default bindings are always applied over a base region so look up the
// base region's default binding, otherwise the lookup will fail when R
@@ -591,10 +593,10 @@ public: // Part of public interface to class.
///
/// Note that callers may need to specially handle LazyCompoundVals, which
/// are returned as is in case the caller needs to treat them differently.
- Optional<SVal> getBindingForDerivedDefaultValue(RegionBindingsConstRef B,
- const MemRegion *superR,
- const TypedValueRegion *R,
- QualType Ty);
+ std::optional<SVal>
+ getBindingForDerivedDefaultValue(RegionBindingsConstRef B,
+ const MemRegion *superR,
+ const TypedValueRegion *R, QualType Ty);
/// Get the state and region whose binding this region \p R corresponds to.
///
@@ -610,6 +612,10 @@ public: // Part of public interface to class.
/// The precise value of "interesting" is determined for the purposes of
/// RegionStore's internal analysis. It must always contain all regions and
/// symbols, but may omit constants and other kinds of SVal.
+ ///
+ /// In contrast to compound values, LazyCompoundVals are also added
+ /// to the 'interesting values' list in addition to the child interesting
+ /// values.
const SValListTy &getInterestingValues(nonloc::LazyCompoundVal LCV);
//===------------------------------------------------------------------===//
@@ -640,16 +646,13 @@ public: // Part of public interface to class.
void iterBindings(Store store, BindingsHandler& f) override {
RegionBindingsRef B = getRegionBindings(store);
- for (RegionBindingsRef::iterator I = B.begin(), E = B.end(); I != E; ++I) {
- const ClusterBindings &Cluster = I.getData();
- for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
- CI != CE; ++CI) {
- const BindingKey &K = CI.getKey();
- if (!K.isDirect())
+ for (const auto &[Region, Cluster] : B) {
+ for (const auto &[Key, Value] : Cluster) {
+ if (!Key.isDirect())
continue;
- if (const SubRegion *R = dyn_cast<SubRegion>(K.getRegion())) {
+ if (const SubRegion *R = dyn_cast<SubRegion>(Key.getRegion())) {
// FIXME: Possibly incorporate the offset?
- if (!f.HandleBinding(*this, store, R, CI.getData()))
+ if (!f.HandleBinding(*this, store, R, Value))
return;
}
}
@@ -665,18 +668,9 @@ public: // Part of public interface to class.
std::unique_ptr<StoreManager>
ento::CreateRegionStoreManager(ProgramStateManager &StMgr) {
- RegionStoreFeatures F = maximal_features_tag();
- return std::make_unique<RegionStoreManager>(StMgr, F);
-}
-
-std::unique_ptr<StoreManager>
-ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) {
- RegionStoreFeatures F = minimal_features_tag();
- F.enableFields(true);
- return std::make_unique<RegionStoreManager>(StMgr, F);
+ return std::make_unique<RegionStoreManager>(StMgr);
}
-
//===----------------------------------------------------------------------===//
// Region Cluster analysis.
//===----------------------------------------------------------------------===//
@@ -868,7 +862,7 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings,
// Find the length (in bits) of the region being invalidated.
uint64_t Length = UINT64_MAX;
SVal Extent = Top->getMemRegionManager().getStaticSize(Top, SVB);
- if (Optional<nonloc::ConcreteInt> ExtentCI =
+ if (std::optional<nonloc::ConcreteInt> ExtentCI =
Extent.getAs<nonloc::ConcreteInt>()) {
const llvm::APSInt &ExtentInt = ExtentCI->getValue();
assert(ExtentInt.isNonNegative() || ExtentInt.isUnsigned());
@@ -879,9 +873,8 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings,
Length = FR->getDecl()->getBitWidthValue(SVB.getContext());
}
- for (ClusterBindings::iterator I = Cluster.begin(), E = Cluster.end();
- I != E; ++I) {
- BindingKey NextKey = I.getKey();
+ for (const auto &StoreEntry : Cluster) {
+ BindingKey NextKey = StoreEntry.first;
if (NextKey.getRegion() == TopKey.getRegion()) {
// FIXME: This doesn't catch the case where we're really invalidating a
// region with a symbolic offset. Example:
@@ -892,7 +885,7 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings,
NextKey.getOffset() - TopKey.getOffset() < Length) {
// Case 1: The next binding is inside the region we're invalidating.
// Include it.
- Bindings.push_back(*I);
+ Bindings.push_back(StoreEntry);
} else if (NextKey.getOffset() == TopKey.getOffset()) {
// Case 2: The next binding is at the same offset as the region we're
@@ -902,7 +895,7 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings,
// FIXME: This is probably incorrect; consider invalidating an outer
// struct whose first field is bound to a LazyCompoundVal.
if (IncludeAllDefaultBindings || NextKey.isDirect())
- Bindings.push_back(*I);
+ Bindings.push_back(StoreEntry);
}
} else if (NextKey.hasSymbolicOffset()) {
@@ -913,13 +906,13 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings,
// we'll be conservative and include it.
if (IncludeAllDefaultBindings || NextKey.isDirect())
if (isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions))
- Bindings.push_back(*I);
+ Bindings.push_back(StoreEntry);
} else if (const SubRegion *BaseSR = dyn_cast<SubRegion>(Base)) {
// Case 4: The next key is symbolic, but we changed a known
// super-region. In this case the binding is certainly included.
if (BaseSR->isSubRegionOf(Top))
if (isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions))
- Bindings.push_back(*I);
+ Bindings.push_back(StoreEntry);
}
}
}
@@ -961,10 +954,8 @@ RegionStoreManager::removeSubRegionBindings(RegionBindingsConstRef B,
/*IncludeAllDefaultBindings=*/false);
ClusterBindingsRef Result(*Cluster, CBFactory);
- for (SmallVectorImpl<BindingPair>::const_iterator I = Bindings.begin(),
- E = Bindings.end();
- I != E; ++I)
- Result = Result.remove(I->first);
+ for (BindingKey Key : llvm::make_first_range(Bindings))
+ Result = Result.remove(Key);
// If we're invalidating a region with a symbolic offset, we need to make sure
// we don't treat the base region as uninitialized anymore.
@@ -1040,15 +1031,14 @@ void InvalidateRegionsWorker::VisitBinding(SVal V) {
}
// Is it a LazyCompoundVal? All references get invalidated as well.
- if (Optional<nonloc::LazyCompoundVal> LCS =
+ if (std::optional<nonloc::LazyCompoundVal> LCS =
V.getAs<nonloc::LazyCompoundVal>()) {
- const RegionStoreManager::SValListTy &Vals = RM.getInterestingValues(*LCS);
-
- for (RegionStoreManager::SValListTy::const_iterator I = Vals.begin(),
- E = Vals.end();
- I != E; ++I)
- VisitBinding(*I);
+ // `getInterestingValues()` returns SVals contained within LazyCompoundVals,
+ // so there is no need to visit them.
+ for (SVal V : RM.getInterestingValues(*LCS))
+ if (!isa<nonloc::LazyCompoundVal>(V))
+ VisitBinding(V);
return;
}
@@ -1062,8 +1052,8 @@ void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
RegionAndSymbolInvalidationTraits::TK_PreserveContents);
if (C) {
- for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I)
- VisitBinding(I.getData());
+ for (SVal Val : llvm::make_second_range(*C))
+ VisitBinding(Val);
// Invalidate regions contents.
if (!PreserveRegionsContents)
@@ -1099,10 +1089,8 @@ void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
// BlockDataRegion? If so, invalidate captured variables that are passed
// by reference.
if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(baseR)) {
- for (BlockDataRegion::referenced_vars_iterator
- BI = BR->referenced_vars_begin(), BE = BR->referenced_vars_end() ;
- BI != BE; ++BI) {
- const VarRegion *VR = BI.getCapturedRegion();
+ for (auto Var : BR->referenced_vars()) {
+ const VarRegion *VR = Var.getCapturedRegion();
const VarDecl *VD = VR->getDecl();
if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage()) {
AddToWorkList(VR);
@@ -1114,7 +1102,7 @@ void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
// a pointer value, but the thing pointed by that pointer may
// get invalidated.
SVal V = RM.getBinding(B, loc::MemRegionVal(VR));
- if (Optional<Loc> L = V.getAs<Loc>()) {
+ if (std::optional<Loc> L = V.getAs<Loc>()) {
if (const MemRegion *LR = L->getAsRegion())
AddToWorkList(LR);
}
@@ -1135,7 +1123,7 @@ void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
if (Regions)
Regions->push_back(baseR);
- if (isa<AllocaRegion>(baseR) || isa<SymbolicRegion>(baseR)) {
+ if (isa<AllocaRegion, SymbolicRegion>(baseR)) {
// Invalidate the region by setting its default value to
// conjured symbol. The type of the symbol is irrelevant.
DefinedOrUnknownSVal V =
@@ -1174,7 +1162,7 @@ void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
if (doNotInvalidateSuperRegion) {
// We are not doing blank invalidation of the whole array region so we
// have to manually invalidate each elements.
- Optional<uint64_t> NumElements;
+ std::optional<uint64_t> NumElements;
// Compute lower and upper offsets for region within array.
if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(AT))
@@ -1206,11 +1194,9 @@ void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
if (!C)
goto conjure_default;
- for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E;
- ++I) {
- const BindingKey &BK = I.getKey();
- Optional<uint64_t> ROffset =
- BK.hasSymbolicOffset() ? Optional<uint64_t>() : BK.getOffset();
+ for (const auto &[BK, V] : *C) {
+ std::optional<uint64_t> ROffset =
+ BK.hasSymbolicOffset() ? std::optional<uint64_t>() : BK.getOffset();
// Check offset is not symbolic and within array's boundaries.
// Handles arrays of 0 elements and of 0-sized elements as well.
@@ -1219,12 +1205,11 @@ void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
(UpperOverflow &&
(*ROffset >= LowerOffset || *ROffset < UpperOffset)) ||
(LowerOffset == UpperOffset && *ROffset == LowerOffset))) {
- B = B.removeBinding(I.getKey());
+ B = B.removeBinding(BK);
// Bound symbolic regions need to be invalidated for dead symbol
// detection.
- SVal V = I.getData();
const MemRegion *R = V.getAsRegion();
- if (R && isa<SymbolicRegion>(R))
+ if (isa_and_nonnull<SymbolicRegion>(R))
VisitBinding(V);
}
}
@@ -1295,21 +1280,12 @@ RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K,
void RegionStoreManager::populateWorkList(InvalidateRegionsWorker &W,
ArrayRef<SVal> Values,
InvalidatedRegions *TopLevelRegions) {
- for (ArrayRef<SVal>::iterator I = Values.begin(),
- E = Values.end(); I != E; ++I) {
- SVal V = *I;
- if (Optional<nonloc::LazyCompoundVal> LCS =
- V.getAs<nonloc::LazyCompoundVal>()) {
-
- const SValListTy &Vals = getInterestingValues(*LCS);
-
- for (SValListTy::const_iterator I = Vals.begin(),
- E = Vals.end(); I != E; ++I) {
- // Note: the last argument is false here because these are
- // non-top-level regions.
- if (const MemRegion *R = (*I).getAsRegion())
+ for (SVal V : Values) {
+ if (auto LCS = V.getAs<nonloc::LazyCompoundVal>()) {
+ for (SVal S : getInterestingValues(*LCS))
+ if (const MemRegion *R = S.getAsRegion())
W.AddToWorkList(R);
- }
+
continue;
}
@@ -1365,11 +1341,11 @@ RegionStoreManager::invalidateRegions(Store store,
case GFK_All:
B = invalidateGlobalRegion(MemRegion::GlobalInternalSpaceRegionKind,
Ex, Count, LCtx, B, Invalidated);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case GFK_SystemOnly:
B = invalidateGlobalRegion(MemRegion::GlobalSystemSpaceRegionKind,
Ex, Count, LCtx, B, Invalidated);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case GFK_None:
break;
}
@@ -1388,10 +1364,10 @@ RegionStoreManager::invalidateRegions(Store store,
/// the array). This is called by ExprEngine when evaluating casts
/// from arrays to pointers.
SVal RegionStoreManager::ArrayToPointer(Loc Array, QualType T) {
- if (Array.getAs<loc::ConcreteInt>())
+ if (isa<loc::ConcreteInt>(Array))
return Array;
- if (!Array.getAs<loc::MemRegionVal>())
+ if (!isa<loc::MemRegionVal>(Array))
return UnknownVal();
const SubRegion *R =
@@ -1405,8 +1381,8 @@ SVal RegionStoreManager::ArrayToPointer(Loc Array, QualType T) {
//===----------------------------------------------------------------------===//
SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T) {
- assert(!L.getAs<UnknownVal>() && "location unknown");
- assert(!L.getAs<UndefinedVal>() && "location undefined");
+ assert(!isa<UnknownVal>(L) && "location unknown");
+ assert(!isa<UndefinedVal>(L) && "location undefined");
// For access to concrete addresses, return UnknownVal. Checks
// for null dereferences (and similar errors) are done by checkers, not
@@ -1427,19 +1403,20 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T)
return UnknownVal();
}
- if (!isa<TypedValueRegion>(MR)) {
- if (T.isNull()) {
- if (const TypedRegion *TR = dyn_cast<TypedRegion>(MR))
- T = TR->getLocationType()->getPointeeType();
- else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR))
- T = SR->getSymbol()->getType()->getPointeeType();
- }
- assert(!T.isNull() && "Unable to auto-detect binding type!");
- assert(!T->isVoidType() && "Attempting to dereference a void pointer!");
- MR = GetElementZeroRegion(cast<SubRegion>(MR), T);
- } else {
- T = cast<TypedValueRegion>(MR)->getValueType();
+ // Auto-detect the binding type.
+ if (T.isNull()) {
+ if (const auto *TVR = dyn_cast<TypedValueRegion>(MR))
+ T = TVR->getValueType();
+ else if (const auto *TR = dyn_cast<TypedRegion>(MR))
+ T = TR->getLocationType()->getPointeeType();
+ else if (const auto *SR = dyn_cast<SymbolicRegion>(MR))
+ T = SR->getPointeeStaticType();
}
+ assert(!T.isNull() && "Unable to auto-detect binding type!");
+ assert(!T->isVoidType() && "Attempting to dereference a void pointer!");
+
+ if (!isa<TypedValueRegion>(MR))
+ MR = GetElementZeroRegion(cast<SubRegion>(MR), T);
// FIXME: Perhaps this method should just take a 'const MemRegion*' argument
// instead of 'Loc', and have the other Loc cases handled at a higher level.
@@ -1548,16 +1525,17 @@ static QualType getUnderlyingType(const SubRegion *R) {
///
/// Note that unlike RegionStoreManager::findLazyBinding, this will not search
/// for lazy bindings for super-regions of \p R.
-static Optional<nonloc::LazyCompoundVal>
+static std::optional<nonloc::LazyCompoundVal>
getExistingLazyBinding(SValBuilder &SVB, RegionBindingsConstRef B,
const SubRegion *R, bool AllowSubregionBindings) {
- Optional<SVal> V = B.getDefaultBinding(R);
+ std::optional<SVal> V = B.getDefaultBinding(R);
if (!V)
- return None;
+ return std::nullopt;
- Optional<nonloc::LazyCompoundVal> LCV = V->getAs<nonloc::LazyCompoundVal>();
+ std::optional<nonloc::LazyCompoundVal> LCV =
+ V->getAs<nonloc::LazyCompoundVal>();
if (!LCV)
- return None;
+ return std::nullopt;
// If the LCV is for a subregion, the types might not match, and we shouldn't
// reuse the binding.
@@ -1566,7 +1544,7 @@ getExistingLazyBinding(SValBuilder &SVB, RegionBindingsConstRef B,
!RegionTy->isVoidPointerType()) {
QualType SourceRegionTy = LCV->getRegion()->getValueType();
if (!SVB.getContext().hasSameUnqualifiedType(RegionTy, SourceRegionTy))
- return None;
+ return std::nullopt;
}
if (!AllowSubregionBindings) {
@@ -1576,20 +1554,19 @@ getExistingLazyBinding(SValBuilder &SVB, RegionBindingsConstRef B,
collectSubRegionBindings(Bindings, SVB, *B.lookup(R->getBaseRegion()), R,
/*IncludeAllDefaultBindings=*/true);
if (Bindings.size() > 1)
- return None;
+ return std::nullopt;
}
return *LCV;
}
-
std::pair<Store, const SubRegion *>
RegionStoreManager::findLazyBinding(RegionBindingsConstRef B,
const SubRegion *R,
const SubRegion *originalRegion) {
if (originalRegion != R) {
- if (Optional<nonloc::LazyCompoundVal> V =
- getExistingLazyBinding(svalBuilder, B, R, true))
+ if (std::optional<nonloc::LazyCompoundVal> V =
+ getExistingLazyBinding(svalBuilder, B, R, true))
return std::make_pair(V->getStore(), V->getRegion());
}
@@ -1625,10 +1602,313 @@ RegionStoreManager::findLazyBinding(RegionBindingsConstRef B,
return Result;
}
+/// This is a helper function for `getConstantValFromConstArrayInitializer`.
+///
+/// Return an array of extents of the declared array type.
+///
+/// E.g. for `int x[1][2][3];` returns { 1, 2, 3 }.
+static SmallVector<uint64_t, 2>
+getConstantArrayExtents(const ConstantArrayType *CAT) {
+ assert(CAT && "ConstantArrayType should not be null");
+ CAT = cast<ConstantArrayType>(CAT->getCanonicalTypeInternal());
+ SmallVector<uint64_t, 2> Extents;
+ do {
+ Extents.push_back(CAT->getSize().getZExtValue());
+ } while ((CAT = dyn_cast<ConstantArrayType>(CAT->getElementType())));
+ return Extents;
+}
+
+/// This is a helper function for `getConstantValFromConstArrayInitializer`.
+///
+/// Return an array of offsets from nested ElementRegions and a root base
+/// region. The array is never empty and a base region is never null.
+///
+/// E.g. for `Element{Element{Element{VarRegion},1},2},3}` returns { 3, 2, 1 }.
+/// This represents an access through indirection: `arr[1][2][3];`
+///
+/// \param ER The given (possibly nested) ElementRegion.
+///
+/// \note The result array is in the reverse order of indirection expression:
+/// arr[1][2][3] -> { 3, 2, 1 }. This helps to provide complexity O(n), where n
+/// is a number of indirections. It may not affect performance in real-life
+/// code, though.
+static std::pair<SmallVector<SVal, 2>, const MemRegion *>
+getElementRegionOffsetsWithBase(const ElementRegion *ER) {
+ assert(ER && "ConstantArrayType should not be null");
+ const MemRegion *Base;
+ SmallVector<SVal, 2> SValOffsets;
+ do {
+ SValOffsets.push_back(ER->getIndex());
+ Base = ER->getSuperRegion();
+ ER = dyn_cast<ElementRegion>(Base);
+ } while (ER);
+ return {SValOffsets, Base};
+}
+
+/// This is a helper function for `getConstantValFromConstArrayInitializer`.
+///
+/// Convert array of offsets from `SVal` to `uint64_t` in consideration of
+/// respective array extents.
+/// \param SrcOffsets [in] The array of offsets of type `SVal` in reversed
+/// order (expectedly received from `getElementRegionOffsetsWithBase`).
+/// \param ArrayExtents [in] The array of extents.
+/// \param DstOffsets [out] The array of offsets of type `uint64_t`.
+/// \returns:
+/// - `std::nullopt` for successful convertion.
+/// - `UndefinedVal` or `UnknownVal` otherwise. It's expected that this SVal
+/// will be returned as a suitable value of the access operation.
+/// which should be returned as a correct
+///
+/// \example:
+/// const int arr[10][20][30] = {}; // ArrayExtents { 10, 20, 30 }
+/// int x1 = arr[4][5][6]; // SrcOffsets { NonLoc(6), NonLoc(5), NonLoc(4) }
+/// // DstOffsets { 4, 5, 6 }
+/// // returns std::nullopt
+/// int x2 = arr[42][5][-6]; // returns UndefinedVal
+/// int x3 = arr[4][5][x2]; // returns UnknownVal
+static std::optional<SVal>
+convertOffsetsFromSvalToUnsigneds(const SmallVector<SVal, 2> &SrcOffsets,
+ const SmallVector<uint64_t, 2> ArrayExtents,
+ SmallVector<uint64_t, 2> &DstOffsets) {
+ // Check offsets for being out of bounds.
+ // C++20 [expr.add] 7.6.6.4 (excerpt):
+ // If P points to an array element i of an array object x with n
+ // elements, where i < 0 or i > n, the behavior is undefined.
+ // Dereferencing is not allowed on the "one past the last
+ // element", when i == n.
+ // Example:
+ // const int arr[3][2] = {{1, 2}, {3, 4}};
+ // arr[0][0]; // 1
+ // arr[0][1]; // 2
+ // arr[0][2]; // UB
+ // arr[1][0]; // 3
+ // arr[1][1]; // 4
+ // arr[1][-1]; // UB
+ // arr[2][0]; // 0
+ // arr[2][1]; // 0
+ // arr[-2][0]; // UB
+ DstOffsets.resize(SrcOffsets.size());
+ auto ExtentIt = ArrayExtents.begin();
+ auto OffsetIt = DstOffsets.begin();
+ // Reverse `SValOffsets` to make it consistent with `ArrayExtents`.
+ for (SVal V : llvm::reverse(SrcOffsets)) {
+ if (auto CI = V.getAs<nonloc::ConcreteInt>()) {
+ // When offset is out of array's bounds, result is UB.
+ const llvm::APSInt &Offset = CI->getValue();
+ if (Offset.isNegative() || Offset.uge(*(ExtentIt++)))
+ return UndefinedVal();
+ // Store index in a reversive order.
+ *(OffsetIt++) = Offset.getZExtValue();
+ continue;
+ }
+ // Symbolic index presented. Return Unknown value.
+ // FIXME: We also need to take ElementRegions with symbolic indexes into
+ // account.
+ return UnknownVal();
+ }
+ return std::nullopt;
+}
+
+std::optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer(
+ RegionBindingsConstRef B, const ElementRegion *R) {
+ assert(R && "ElementRegion should not be null");
+
+ // Treat an n-dimensional array.
+ SmallVector<SVal, 2> SValOffsets;
+ const MemRegion *Base;
+ std::tie(SValOffsets, Base) = getElementRegionOffsetsWithBase(R);
+ const VarRegion *VR = dyn_cast<VarRegion>(Base);
+ if (!VR)
+ return std::nullopt;
+
+ assert(!SValOffsets.empty() && "getElementRegionOffsets guarantees the "
+ "offsets vector is not empty.");
+
+ // Check if the containing array has an initialized value that we can trust.
+ // We can trust a const value or a value of a global initializer in main().
+ const VarDecl *VD = VR->getDecl();
+ if (!VD->getType().isConstQualified() &&
+ !R->getElementType().isConstQualified() &&
+ (!B.isMainAnalysis() || !VD->hasGlobalStorage()))
+ return std::nullopt;
+
+ // Array's declaration should have `ConstantArrayType` type, because only this
+ // type contains an array extent. It may happen that array type can be of
+ // `IncompleteArrayType` type. To get the declaration of `ConstantArrayType`
+ // type, we should find the declaration in the redeclarations chain that has
+ // the initialization expression.
+ // NOTE: `getAnyInitializer` has an out-parameter, which returns a new `VD`
+ // from which an initializer is obtained. We replace current `VD` with the new
+ // `VD`. If the return value of the function is null than `VD` won't be
+ // replaced.
+ const Expr *Init = VD->getAnyInitializer(VD);
+ // NOTE: If `Init` is non-null, then a new `VD` is non-null for sure. So check
+ // `Init` for null only and don't worry about the replaced `VD`.
+ if (!Init)
+ return std::nullopt;
+
+ // Array's declaration should have ConstantArrayType type, because only this
+ // type contains an array extent.
+ const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(VD->getType());
+ if (!CAT)
+ return std::nullopt;
+
+ // Get array extents.
+ SmallVector<uint64_t, 2> Extents = getConstantArrayExtents(CAT);
+
+ // The number of offsets should equal to the numbers of extents,
+ // otherwise wrong type punning occurred. For instance:
+ // int arr[1][2][3];
+ // auto ptr = (int(*)[42])arr;
+ // auto x = ptr[4][2]; // UB
+ // FIXME: Should return UndefinedVal.
+ if (SValOffsets.size() != Extents.size())
+ return std::nullopt;
+
+ SmallVector<uint64_t, 2> ConcreteOffsets;
+ if (std::optional<SVal> V = convertOffsetsFromSvalToUnsigneds(
+ SValOffsets, Extents, ConcreteOffsets))
+ return *V;
+
+ // Handle InitListExpr.
+ // Example:
+ // const char arr[4][2] = { { 1, 2 }, { 3 }, 4, 5 };
+ if (const auto *ILE = dyn_cast<InitListExpr>(Init))
+ return getSValFromInitListExpr(ILE, ConcreteOffsets, R->getElementType());
+
+ // Handle StringLiteral.
+ // Example:
+ // const char arr[] = "abc";
+ if (const auto *SL = dyn_cast<StringLiteral>(Init))
+ return getSValFromStringLiteral(SL, ConcreteOffsets.front(),
+ R->getElementType());
+
+ // FIXME: Handle CompoundLiteralExpr.
+
+ return std::nullopt;
+}
+
+/// Returns an SVal, if possible, for the specified position of an
+/// initialization list.
+///
+/// \param ILE The given initialization list.
+/// \param Offsets The array of unsigned offsets. E.g. for the expression
+/// `int x = arr[1][2][3];` an array should be { 1, 2, 3 }.
+/// \param ElemT The type of the result SVal expression.
+/// \return Optional SVal for the particular position in the initialization
+/// list. E.g. for the list `{{1, 2},[3, 4],{5, 6}, {}}` offsets:
+/// - {1, 1} returns SVal{4}, because it's the second position in the second
+/// sublist;
+/// - {3, 0} returns SVal{0}, because there's no explicit value at this
+/// position in the sublist.
+///
+/// NOTE: Inorder to get a valid SVal, a caller shall guarantee valid offsets
+/// for the given initialization list. Otherwise SVal can be an equivalent to 0
+/// or lead to assertion.
+std::optional<SVal> RegionStoreManager::getSValFromInitListExpr(
+ const InitListExpr *ILE, const SmallVector<uint64_t, 2> &Offsets,
+ QualType ElemT) {
+ assert(ILE && "InitListExpr should not be null");
+
+ for (uint64_t Offset : Offsets) {
+ // C++20 [dcl.init.string] 9.4.2.1:
+ // An array of ordinary character type [...] can be initialized by [...]
+ // an appropriately-typed string-literal enclosed in braces.
+ // Example:
+ // const char arr[] = { "abc" };
+ if (ILE->isStringLiteralInit())
+ if (const auto *SL = dyn_cast<StringLiteral>(ILE->getInit(0)))
+ return getSValFromStringLiteral(SL, Offset, ElemT);
+
+ // C++20 [expr.add] 9.4.17.5 (excerpt):
+ // i-th array element is value-initialized for each k < i ≤ n,
+ // where k is an expression-list size and n is an array extent.
+ if (Offset >= ILE->getNumInits())
+ return svalBuilder.makeZeroVal(ElemT);
+
+ const Expr *E = ILE->getInit(Offset);
+ const auto *IL = dyn_cast<InitListExpr>(E);
+ if (!IL)
+ // Return a constant value, if it is presented.
+ // FIXME: Support other SVals.
+ return svalBuilder.getConstantVal(E);
+
+ // Go to the nested initializer list.
+ ILE = IL;
+ }
+
+ assert(ILE);
+
+ // FIXME: Unhandeled InitListExpr sub-expression, possibly constructing an
+ // enum?
+ return std::nullopt;
+}
+
+/// Returns an SVal, if possible, for the specified position in a string
+/// literal.
+///
+/// \param SL The given string literal.
+/// \param Offset The unsigned offset. E.g. for the expression
+/// `char x = str[42];` an offset should be 42.
+/// E.g. for the string "abc" offset:
+/// - 1 returns SVal{b}, because it's the second position in the string.
+/// - 42 returns SVal{0}, because there's no explicit value at this
+/// position in the string.
+/// \param ElemT The type of the result SVal expression.
+///
+/// NOTE: We return `0` for every offset >= the literal length for array
+/// declarations, like:
+/// const char str[42] = "123"; // Literal length is 4.
+/// char c = str[41]; // Offset is 41.
+/// FIXME: Nevertheless, we can't do the same for pointer declaraions, like:
+/// const char * const str = "123"; // Literal length is 4.
+/// char c = str[41]; // Offset is 41. Returns `0`, but Undef
+/// // expected.
+/// It should be properly handled before reaching this point.
+/// The main problem is that we can't distinguish between these declarations,
+/// because in case of array we can get the Decl from VarRegion, but in case
+/// of pointer the region is a StringRegion, which doesn't contain a Decl.
+/// Possible solution could be passing an array extent along with the offset.
+SVal RegionStoreManager::getSValFromStringLiteral(const StringLiteral *SL,
+ uint64_t Offset,
+ QualType ElemT) {
+ assert(SL && "StringLiteral should not be null");
+ // C++20 [dcl.init.string] 9.4.2.3:
+ // If there are fewer initializers than there are array elements, each
+ // element not explicitly initialized shall be zero-initialized [dcl.init].
+ uint32_t Code = (Offset >= SL->getLength()) ? 0 : SL->getCodeUnit(Offset);
+ return svalBuilder.makeIntVal(Code, ElemT);
+}
+
+static std::optional<SVal> getDerivedSymbolForBinding(
+ RegionBindingsConstRef B, const TypedValueRegion *BaseRegion,
+ const TypedValueRegion *SubReg, const ASTContext &Ctx, SValBuilder &SVB) {
+ assert(BaseRegion);
+ QualType BaseTy = BaseRegion->getValueType();
+ QualType Ty = SubReg->getValueType();
+ if (BaseTy->isScalarType() && Ty->isScalarType()) {
+ if (Ctx.getTypeSizeInChars(BaseTy) >= Ctx.getTypeSizeInChars(Ty)) {
+ if (const std::optional<SVal> &ParentValue =
+ B.getDirectBinding(BaseRegion)) {
+ if (SymbolRef ParentValueAsSym = ParentValue->getAsSymbol())
+ return SVB.getDerivedRegionValueSymbolVal(ParentValueAsSym, SubReg);
+
+ if (ParentValue->isUndef())
+ return UndefinedVal();
+
+ // Other cases: give up. We are indexing into a larger object
+ // that has some value, but we don't know how to handle that yet.
+ return UnknownVal();
+ }
+ }
+ }
+ return std::nullopt;
+}
+
SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
const ElementRegion* R) {
// Check if the region has a binding.
- if (const Optional<SVal> &V = B.getDirectBinding(R))
+ if (const std::optional<SVal> &V = B.getDirectBinding(R))
return *V;
const MemRegion* superR = R->getSuperRegion();
@@ -1636,59 +1916,21 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
// Check if the region is an element region of a string literal.
if (const StringRegion *StrR = dyn_cast<StringRegion>(superR)) {
// FIXME: Handle loads from strings where the literal is treated as
- // an integer, e.g., *((unsigned int*)"hello")
+ // an integer, e.g., *((unsigned int*)"hello"). Such loads are UB according
+ // to C++20 7.2.1.11 [basic.lval].
QualType T = Ctx.getAsArrayType(StrR->getValueType())->getElementType();
if (!Ctx.hasSameUnqualifiedType(T, R->getElementType()))
return UnknownVal();
-
- const StringLiteral *Str = StrR->getStringLiteral();
- SVal Idx = R->getIndex();
- if (Optional<nonloc::ConcreteInt> CI = Idx.getAs<nonloc::ConcreteInt>()) {
- int64_t i = CI->getValue().getSExtValue();
- // Abort on string underrun. This can be possible by arbitrary
- // clients of getBindingForElement().
- if (i < 0)
+ if (const auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) {
+ const llvm::APSInt &Idx = CI->getValue();
+ if (Idx < 0)
return UndefinedVal();
- int64_t length = Str->getLength();
- // Technically, only i == length is guaranteed to be null.
- // However, such overflows should be caught before reaching this point;
- // the only time such an access would be made is if a string literal was
- // used to initialize a larger array.
- char c = (i >= length) ? '\0' : Str->getCodeUnit(i);
- return svalBuilder.makeIntVal(c, T);
- }
- } else if (const VarRegion *VR = dyn_cast<VarRegion>(superR)) {
- // Check if the containing array has an initialized value that we can trust.
- // We can trust a const value or a value of a global initializer in main().
- const VarDecl *VD = VR->getDecl();
- if (VD->getType().isConstQualified() ||
- R->getElementType().isConstQualified() ||
- (B.isMainAnalysis() && VD->hasGlobalStorage())) {
- if (const Expr *Init = VD->getAnyInitializer()) {
- if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
- // The array index has to be known.
- if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) {
- int64_t i = CI->getValue().getSExtValue();
- // If it is known that the index is out of bounds, we can return
- // an undefined value.
- if (i < 0)
- return UndefinedVal();
-
- if (auto CAT = Ctx.getAsConstantArrayType(VD->getType()))
- if (CAT->getSize().sle(i))
- return UndefinedVal();
-
- // If there is a list, but no init, it must be zero.
- if (i >= InitList->getNumInits())
- return svalBuilder.makeZeroVal(R->getElementType());
-
- if (const Expr *ElemInit = InitList->getInit(i))
- if (Optional<SVal> V = svalBuilder.getConstantVal(ElemInit))
- return *V;
- }
- }
- }
+ const StringLiteral *SL = StrR->getStringLiteral();
+ return getSValFromStringLiteral(SL, Idx.getZExtValue(), T);
}
+ } else if (isa<ElementRegion, VarRegion>(superR)) {
+ if (std::optional<SVal> V = getConstantValFromConstArrayInitializer(B, R))
+ return *V;
}
// Check for loads from a code text region. For such loads, just give up.
@@ -1707,27 +1949,10 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
if (!O.getRegion())
return UnknownVal();
- if (const TypedValueRegion *baseR =
- dyn_cast_or_null<TypedValueRegion>(O.getRegion())) {
- QualType baseT = baseR->getValueType();
- if (baseT->isScalarType()) {
- QualType elemT = R->getElementType();
- if (elemT->isScalarType()) {
- if (Ctx.getTypeSizeInChars(baseT) >= Ctx.getTypeSizeInChars(elemT)) {
- if (const Optional<SVal> &V = B.getDirectBinding(superR)) {
- if (SymbolRef parentSym = V->getAsSymbol())
- return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R);
-
- if (V->isUnknownOrUndef())
- return *V;
- // Other cases: give up. We are indexing into a larger object
- // that has some value, but we don't know how to handle that yet.
- return UnknownVal();
- }
- }
- }
- }
- }
+ if (const TypedValueRegion *baseR = dyn_cast<TypedValueRegion>(O.getRegion()))
+ if (auto V = getDerivedSymbolForBinding(B, baseR, R, Ctx, svalBuilder))
+ return *V;
+
return getBindingForFieldOrElementCommon(B, R, R->getElementType());
}
@@ -1735,18 +1960,12 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
const FieldRegion* R) {
// Check if the region has a binding.
- if (const Optional<SVal> &V = B.getDirectBinding(R))
+ if (const std::optional<SVal> &V = B.getDirectBinding(R))
return *V;
- // Is the field declared constant and has an in-class initializer?
+ // If the containing record was initialized, try to get its constant value.
const FieldDecl *FD = R->getDecl();
QualType Ty = FD->getType();
- if (Ty.isConstQualified())
- if (const Expr *Init = FD->getInClassInitializer())
- if (Optional<SVal> V = svalBuilder.getConstantVal(Init))
- return *V;
-
- // If the containing record was initialized, try to get its constant value.
const MemRegion* superR = R->getSuperRegion();
if (const auto *VR = dyn_cast<VarRegion>(superR)) {
const VarDecl *VD = VR->getDecl();
@@ -1761,7 +1980,7 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
if (Index < InitList->getNumInits()) {
if (const Expr *FieldInit = InitList->getInit(Index))
- if (Optional<SVal> V = svalBuilder.getConstantVal(FieldInit))
+ if (std::optional<SVal> V = svalBuilder.getConstantVal(FieldInit))
return *V;
} else {
return svalBuilder.makeZeroVal(Ty);
@@ -1769,17 +1988,35 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
}
}
+ // Handle the case where we are accessing into a larger scalar object.
+ // For example, this handles:
+ // struct header {
+ // unsigned a : 1;
+ // unsigned b : 1;
+ // };
+ // struct parse_t {
+ // unsigned bits0 : 1;
+ // unsigned bits2 : 2; // <-- header
+ // unsigned bits4 : 4;
+ // };
+ // int parse(parse_t *p) {
+ // unsigned copy = p->bits2;
+ // header *bits = (header *)&copy;
+ // return bits->b; <-- here
+ // }
+ if (const auto *Base = dyn_cast<TypedValueRegion>(R->getBaseRegion()))
+ if (auto V = getDerivedSymbolForBinding(B, Base, R, Ctx, svalBuilder))
+ return *V;
+
return getBindingForFieldOrElementCommon(B, R, Ty);
}
-Optional<SVal>
-RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindingsConstRef B,
- const MemRegion *superR,
- const TypedValueRegion *R,
- QualType Ty) {
+std::optional<SVal> RegionStoreManager::getBindingForDerivedDefaultValue(
+ RegionBindingsConstRef B, const MemRegion *superR,
+ const TypedValueRegion *R, QualType Ty) {
- if (const Optional<SVal> &D = B.getDefaultBinding(superR)) {
- const SVal &val = D.getValue();
+ if (const std::optional<SVal> &D = B.getDefaultBinding(superR)) {
+ SVal val = *D;
if (SymbolRef parentSym = val.getAsSymbol())
return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R);
@@ -1791,14 +2028,13 @@ RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindingsConstRef B,
// Lazy bindings are usually handled through getExistingLazyBinding().
// We should unify these two code paths at some point.
- if (val.getAs<nonloc::LazyCompoundVal>() ||
- val.getAs<nonloc::CompoundVal>())
+ if (isa<nonloc::LazyCompoundVal, nonloc::CompoundVal>(val))
return val;
llvm_unreachable("Unknown default value");
}
- return None;
+ return std::nullopt;
}
SVal RegionStoreManager::getLazyBinding(const SubRegion *LazyBindingRegion,
@@ -1869,7 +2105,8 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
const SubRegion *SR = R;
while (SR) {
const MemRegion *Base = SR->getSuperRegion();
- if (Optional<SVal> D = getBindingForDerivedDefaultValue(B, Base, R, Ty)) {
+ if (std::optional<SVal> D =
+ getBindingForDerivedDefaultValue(B, Base, R, Ty)) {
if (D->getAs<nonloc::LazyCompoundVal>()) {
hasPartialLazyBinding = true;
break;
@@ -1908,8 +2145,13 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
return UnknownVal();
// Additionally allow introspection of a block's internal layout.
- if (!hasPartialLazyBinding && !isa<BlockDataRegion>(R->getBaseRegion()))
+ // Try to get direct binding if all other attempts failed thus far.
+ // Else, return UndefinedVal()
+ if (!hasPartialLazyBinding && !isa<BlockDataRegion>(R->getBaseRegion())) {
+ if (const std::optional<SVal> &V = B.getDefaultBinding(R))
+ return *V;
return UndefinedVal();
+ }
}
// All other values are symbolic.
@@ -1919,13 +2161,13 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
SVal RegionStoreManager::getBindingForObjCIvar(RegionBindingsConstRef B,
const ObjCIvarRegion* R) {
// Check if the region has a binding.
- if (const Optional<SVal> &V = B.getDirectBinding(R))
+ if (const std::optional<SVal> &V = B.getDirectBinding(R))
return *V;
const MemRegion *superR = R->getSuperRegion();
// Check if the super region has a default binding.
- if (const Optional<SVal> &V = B.getDefaultBinding(superR)) {
+ if (const std::optional<SVal> &V = B.getDefaultBinding(superR)) {
if (SymbolRef parentSym = V->getAsSymbol())
return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R);
@@ -1940,10 +2182,10 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B,
const VarRegion *R) {
// Check if the region has a binding.
- if (Optional<SVal> V = B.getDirectBinding(R))
+ if (std::optional<SVal> V = B.getDirectBinding(R))
return *V;
- if (Optional<SVal> V = B.getDefaultBinding(R))
+ if (std::optional<SVal> V = B.getDefaultBinding(R))
return *V;
// Lazily derive a value for the VarRegion.
@@ -1957,7 +2199,7 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B,
// Is 'VD' declared constant? If so, retrieve the constant value.
if (VD->getType().isConstQualified()) {
if (const Expr *Init = VD->getAnyInitializer()) {
- if (Optional<SVal> V = svalBuilder.getConstantVal(Init))
+ if (std::optional<SVal> V = svalBuilder.getConstantVal(Init))
return *V;
// If the variable is const qualified and has an initializer but
@@ -1978,7 +2220,7 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B,
// If we're in main(), then global initializers have not become stale yet.
if (B.isMainAnalysis())
if (const Expr *Init = VD->getAnyInitializer())
- if (Optional<SVal> V = svalBuilder.getConstantVal(Init))
+ if (std::optional<SVal> V = svalBuilder.getConstantVal(Init))
return *V;
// Function-scoped static variables are default-initialized to 0; if they
@@ -1988,9 +2230,9 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B,
if (isa<StaticGlobalSpaceRegion>(MS))
return svalBuilder.makeZeroVal(T);
- if (Optional<SVal> V = getBindingForDerivedDefaultValue(B, MS, R, T)) {
+ if (std::optional<SVal> V = getBindingForDerivedDefaultValue(B, MS, R, T)) {
assert(!V->getAs<nonloc::LazyCompoundVal>());
- return V.getValue();
+ return *V;
}
return svalBuilder.getRegionValueSymbolVal(R);
@@ -2026,18 +2268,13 @@ RegionStoreManager::getInterestingValues(nonloc::LazyCompoundVal LCV) {
SmallVector<BindingPair, 32> Bindings;
collectSubRegionBindings(Bindings, svalBuilder, *Cluster, LazyR,
/*IncludeAllDefaultBindings=*/true);
- for (SmallVectorImpl<BindingPair>::const_iterator I = Bindings.begin(),
- E = Bindings.end();
- I != E; ++I) {
- SVal V = I->second;
+ for (SVal V : llvm::make_second_range(Bindings)) {
if (V.isUnknownOrUndef() || V.isConstant())
continue;
- if (Optional<nonloc::LazyCompoundVal> InnerLCV =
- V.getAs<nonloc::LazyCompoundVal>()) {
+ if (auto InnerLCV = V.getAs<nonloc::LazyCompoundVal>()) {
const SValListTy &InnerList = getInterestingValues(*InnerLCV);
List.insert(List.end(), InnerList.begin(), InnerList.end());
- continue;
}
List.push_back(V);
@@ -2048,8 +2285,8 @@ RegionStoreManager::getInterestingValues(nonloc::LazyCompoundVal LCV) {
NonLoc RegionStoreManager::createLazyBinding(RegionBindingsConstRef B,
const TypedValueRegion *R) {
- if (Optional<nonloc::LazyCompoundVal> V =
- getExistingLazyBinding(svalBuilder, B, R, false))
+ if (std::optional<nonloc::LazyCompoundVal> V =
+ getExistingLazyBinding(svalBuilder, B, R, false))
return *V;
return svalBuilder.makeLazyCompoundVal(StoreRef(B.asStore(), *this), R);
@@ -2094,7 +2331,7 @@ bool RegionStoreManager::includedInBindings(Store store,
const ClusterBindings &Cluster = RI.getData();
for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
CI != CE; ++CI) {
- const SVal &D = CI.getData();
+ SVal D = CI.getData();
if (const MemRegion *R = D.getAsRegion())
if (R->getBaseRegion() == region)
return true;
@@ -2109,7 +2346,7 @@ bool RegionStoreManager::includedInBindings(Store store,
//===----------------------------------------------------------------------===//
StoreRef RegionStoreManager::killBinding(Store ST, Loc L) {
- if (Optional<loc::MemRegionVal> LV = L.getAs<loc::MemRegionVal>())
+ if (std::optional<loc::MemRegionVal> LV = L.getAs<loc::MemRegionVal>())
if (const MemRegion* R = LV->getRegion())
return StoreRef(getRegionBindings(ST).removeBinding(R)
.asImmutableMap()
@@ -2140,22 +2377,21 @@ RegionStoreManager::bind(RegionBindingsConstRef B, Loc L, SVal V) {
return bindAggregate(B, TR, V);
}
- if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) {
- // Binding directly to a symbolic region should be treated as binding
- // to element 0.
- QualType T = SR->getSymbol()->getType();
- if (T->isAnyPointerType() || T->isReferenceType())
- T = T->getPointeeType();
-
- R = GetElementZeroRegion(SR, T);
- }
+ // Binding directly to a symbolic region should be treated as binding
+ // to element 0.
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R))
+ R = GetElementZeroRegion(SR, SR->getPointeeStaticType());
assert((!isa<CXXThisRegion>(R) || !B.lookup(R)) &&
"'this' pointer is not an l-value and is not assignable");
// Clear out bindings that may overlap with this binding.
RegionBindingsRef NewB = removeSubRegionBindings(B, cast<SubRegion>(R));
- return NewB.addBinding(BindingKey::Make(R, BindingKey::Direct), V);
+
+ // LazyCompoundVals should be always bound as 'default' bindings.
+ auto KeyKind = isa<nonloc::LazyCompoundVal>(V) ? BindingKey::Default
+ : BindingKey::Direct;
+ return NewB.addBinding(BindingKey::Make(R, KeyKind), V);
}
RegionBindingsRef
@@ -2165,7 +2401,7 @@ RegionStoreManager::setImplicitDefaultValue(RegionBindingsConstRef B,
SVal V;
if (Loc::isLocType(T))
- V = svalBuilder.makeNull();
+ V = svalBuilder.makeNullWithType(T);
else if (T->isIntegralOrEnumerationType())
V = svalBuilder.makeZeroVal(T);
else if (T->isStructureOrClassType() || T->isArrayType()) {
@@ -2185,6 +2421,40 @@ RegionStoreManager::setImplicitDefaultValue(RegionBindingsConstRef B,
return B.addBinding(R, BindingKey::Default, V);
}
+std::optional<RegionBindingsRef> RegionStoreManager::tryBindSmallArray(
+ RegionBindingsConstRef B, const TypedValueRegion *R, const ArrayType *AT,
+ nonloc::LazyCompoundVal LCV) {
+
+ auto CAT = dyn_cast<ConstantArrayType>(AT);
+
+ // If we don't know the size, create a lazyCompoundVal instead.
+ if (!CAT)
+ return std::nullopt;
+
+ QualType Ty = CAT->getElementType();
+ if (!(Ty->isScalarType() || Ty->isReferenceType()))
+ return std::nullopt;
+
+ // If the array is too big, create a LCV instead.
+ uint64_t ArrSize = CAT->getSize().getLimitedValue();
+ if (ArrSize > SmallArrayLimit)
+ return std::nullopt;
+
+ RegionBindingsRef NewB = B;
+
+ for (uint64_t i = 0; i < ArrSize; ++i) {
+ auto Idx = svalBuilder.makeArrayIndex(i);
+ const ElementRegion *SrcER =
+ MRMgr.getElementRegion(Ty, Idx, LCV.getRegion(), Ctx);
+ SVal V = getBindingForElement(getRegionBindings(LCV.getStore()), SrcER);
+
+ const ElementRegion *DstER = MRMgr.getElementRegion(Ty, Idx, R, Ctx);
+ NewB = bind(NewB, loc::MemRegionVal(DstER), V);
+ }
+
+ return NewB;
+}
+
RegionBindingsRef
RegionStoreManager::bindArray(RegionBindingsConstRef B,
const TypedValueRegion* R,
@@ -2192,7 +2462,7 @@ RegionStoreManager::bindArray(RegionBindingsConstRef B,
const ArrayType *AT =cast<ArrayType>(Ctx.getCanonicalType(R->getValueType()));
QualType ElementTy = AT->getElementType();
- Optional<uint64_t> Size;
+ std::optional<uint64_t> Size;
if (const ConstantArrayType* CAT = dyn_cast<ConstantArrayType>(AT))
Size = CAT->getSize().getZExtValue();
@@ -2200,14 +2470,20 @@ RegionStoreManager::bindArray(RegionBindingsConstRef B,
// Check if the init expr is a literal. If so, bind the rvalue instead.
// FIXME: It's not responsibility of the Store to transform this lvalue
// to rvalue. ExprEngine or maybe even CFG should do this before binding.
- if (Optional<loc::MemRegionVal> MRV = Init.getAs<loc::MemRegionVal>()) {
+ if (std::optional<loc::MemRegionVal> MRV = Init.getAs<loc::MemRegionVal>()) {
SVal V = getBinding(B.asStore(), *MRV, R->getValueType());
return bindAggregate(B, R, V);
}
// Handle lazy compound values.
- if (Init.getAs<nonloc::LazyCompoundVal>())
+ if (std::optional<nonloc::LazyCompoundVal> LCV =
+ Init.getAs<nonloc::LazyCompoundVal>()) {
+ if (std::optional<RegionBindingsRef> NewB =
+ tryBindSmallArray(B, R, AT, *LCV))
+ return *NewB;
+
return bindAggregate(B, R, Init);
+ }
if (Init.isUnknown())
return bindAggregate(B, R, UnknownVal());
@@ -2219,12 +2495,12 @@ RegionStoreManager::bindArray(RegionBindingsConstRef B,
RegionBindingsRef NewB(B);
- for (; Size.hasValue() ? i < Size.getValue() : true ; ++i, ++VI) {
+ for (; Size ? i < *Size : true; ++i, ++VI) {
// The init list might be shorter than the array length.
if (VI == VE)
break;
- const NonLoc &Idx = svalBuilder.makeArrayIndex(i);
+ NonLoc Idx = svalBuilder.makeArrayIndex(i);
const ElementRegion *ER = MRMgr.getElementRegion(ElementTy, Idx, R, Ctx);
if (ElementTy->isStructureOrClassType())
@@ -2238,7 +2514,7 @@ RegionStoreManager::bindArray(RegionBindingsConstRef B,
// If the init list is shorter than the array length (or the array has
// variable length), set the array default value. Values that are already set
// are not overwritten.
- if (!Size.hasValue() || i < Size.getValue())
+ if (!Size || i < *Size)
NewB = setImplicitDefaultValue(NewB, R, ElementTy);
return NewB;
@@ -2251,13 +2527,13 @@ RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B,
const VectorType *VT = T->castAs<VectorType>(); // Use castAs for typedefs.
// Handle lazy compound values and symbolic values.
- if (V.getAs<nonloc::LazyCompoundVal>() || V.getAs<nonloc::SymbolVal>())
+ if (isa<nonloc::LazyCompoundVal, nonloc::SymbolVal>(V))
return bindAggregate(B, R, V);
// We may get non-CompoundVal accidentally due to imprecise cast logic or
// that we are binding symbolic struct value. Kill the field values, and if
// the value is symbolic go and bind it as a "default" binding.
- if (!V.getAs<nonloc::CompoundVal>()) {
+ if (!isa<nonloc::CompoundVal>(V)) {
return bindAggregate(B, R, UnknownVal());
}
@@ -2284,16 +2560,14 @@ RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B,
return NewB;
}
-Optional<RegionBindingsRef>
-RegionStoreManager::tryBindSmallStruct(RegionBindingsConstRef B,
- const TypedValueRegion *R,
- const RecordDecl *RD,
- nonloc::LazyCompoundVal LCV) {
+std::optional<RegionBindingsRef> RegionStoreManager::tryBindSmallStruct(
+ RegionBindingsConstRef B, const TypedValueRegion *R, const RecordDecl *RD,
+ nonloc::LazyCompoundVal LCV) {
FieldVector Fields;
if (const CXXRecordDecl *Class = dyn_cast<CXXRecordDecl>(RD))
if (Class->getNumBases() != 0 || Class->getNumVBases() != 0)
- return None;
+ return std::nullopt;
for (const auto *FD : RD->fields()) {
if (FD->isUnnamedBitfield())
@@ -2302,22 +2576,28 @@ RegionStoreManager::tryBindSmallStruct(RegionBindingsConstRef B,
// If there are too many fields, or if any of the fields are aggregates,
// just use the LCV as a default binding.
if (Fields.size() == SmallStructLimit)
- return None;
+ return std::nullopt;
QualType Ty = FD->getType();
+
+ // Zero length arrays are basically no-ops, so we also ignore them here.
+ if (Ty->isConstantArrayType() &&
+ Ctx.getConstantArrayElementCount(Ctx.getAsConstantArrayType(Ty)) == 0)
+ continue;
+
if (!(Ty->isScalarType() || Ty->isReferenceType()))
- return None;
+ return std::nullopt;
Fields.push_back(FD);
}
RegionBindingsRef NewB = B;
- for (FieldVector::iterator I = Fields.begin(), E = Fields.end(); I != E; ++I){
- const FieldRegion *SourceFR = MRMgr.getFieldRegion(*I, LCV.getRegion());
+ for (const FieldDecl *Field : Fields) {
+ const FieldRegion *SourceFR = MRMgr.getFieldRegion(Field, LCV.getRegion());
SVal V = getBindingForField(getRegionBindings(LCV.getStore()), SourceFR);
- const FieldRegion *DestFR = MRMgr.getFieldRegion(*I, R);
+ const FieldRegion *DestFR = MRMgr.getFieldRegion(Field, R);
NewB = bind(NewB, loc::MemRegionVal(DestFR), V);
}
@@ -2325,11 +2605,8 @@ RegionStoreManager::tryBindSmallStruct(RegionBindingsConstRef B,
}
RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B,
- const TypedValueRegion* R,
+ const TypedValueRegion *R,
SVal V) {
- if (!Features.supportsFields())
- return B;
-
QualType T = R->getValueType();
assert(T->isStructureOrClassType());
@@ -2340,19 +2617,20 @@ RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B,
return B;
// Handle lazy compound values and symbolic values.
- if (Optional<nonloc::LazyCompoundVal> LCV =
- V.getAs<nonloc::LazyCompoundVal>()) {
- if (Optional<RegionBindingsRef> NewB = tryBindSmallStruct(B, R, RD, *LCV))
+ if (std::optional<nonloc::LazyCompoundVal> LCV =
+ V.getAs<nonloc::LazyCompoundVal>()) {
+ if (std::optional<RegionBindingsRef> NewB =
+ tryBindSmallStruct(B, R, RD, *LCV))
return *NewB;
return bindAggregate(B, R, V);
}
- if (V.getAs<nonloc::SymbolVal>())
+ if (isa<nonloc::SymbolVal>(V))
return bindAggregate(B, R, V);
// We may get non-CompoundVal accidentally due to imprecise cast logic or
// that we are binding symbolic struct value. Kill the field values, and if
// the value is symbolic go and bind it as a "default" binding.
- if (V.isUnknown() || !V.getAs<nonloc::CompoundVal>())
+ if (V.isUnknown() || !isa<nonloc::CompoundVal>(V))
return bindAggregate(B, R, UnknownVal());
// The raw CompoundVal is essentially a symbolic InitListExpr: an (immutable)
@@ -2535,25 +2813,26 @@ void RemoveDeadBindingsWorker::VisitCluster(const MemRegion *baseR,
if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(baseR))
SymReaper.markLive(SymR->getSymbol());
- for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I) {
+ for (const auto &[Key, Val] : *C) {
// Element index of a binding key is live.
- SymReaper.markElementIndicesLive(I.getKey().getRegion());
+ SymReaper.markElementIndicesLive(Key.getRegion());
- VisitBinding(I.getData());
+ VisitBinding(Val);
}
}
void RemoveDeadBindingsWorker::VisitBinding(SVal V) {
- // Is it a LazyCompoundVal? All referenced regions are live as well.
- if (Optional<nonloc::LazyCompoundVal> LCS =
- V.getAs<nonloc::LazyCompoundVal>()) {
-
- const RegionStoreManager::SValListTy &Vals = RM.getInterestingValues(*LCS);
-
- for (RegionStoreManager::SValListTy::const_iterator I = Vals.begin(),
- E = Vals.end();
- I != E; ++I)
- VisitBinding(*I);
+ // Is it a LazyCompoundVal? All referenced regions are live as well.
+ // The LazyCompoundVal itself is not live but should be readable.
+ if (auto LCS = V.getAs<nonloc::LazyCompoundVal>()) {
+ SymReaper.markLazilyCopied(LCS->getRegion());
+
+ for (SVal V : RM.getInterestingValues(*LCS)) {
+ if (auto DepLCS = V.getAs<nonloc::LazyCompoundVal>())
+ SymReaper.markLazilyCopied(DepLCS->getRegion());
+ else
+ VisitBinding(V);
+ }
return;
}
@@ -2565,17 +2844,15 @@ void RemoveDeadBindingsWorker::VisitBinding(SVal V) {
// All regions captured by a block are also live.
if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) {
- BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(),
- E = BR->referenced_vars_end();
- for ( ; I != E; ++I)
- AddToWorkList(I.getCapturedRegion());
+ for (auto Var : BR->referenced_vars())
+ AddToWorkList(Var.getCapturedRegion());
}
}
// Update the set of live symbols.
- for (auto SI = V.symbol_begin(), SE = V.symbol_end(); SI!=SE; ++SI)
- SymReaper.markLive(*SI);
+ for (SymbolRef Sym : V.symbols())
+ SymReaper.markLive(Sym);
}
bool RemoveDeadBindingsWorker::UpdatePostponed() {
@@ -2583,12 +2860,10 @@ bool RemoveDeadBindingsWorker::UpdatePostponed() {
// having done a scan.
bool Changed = false;
- for (auto I = Postponed.begin(), E = Postponed.end(); I != E; ++I) {
- if (const SymbolicRegion *SR = *I) {
- if (SymReaper.isLive(SR->getSymbol())) {
- Changed |= AddToWorkList(SR);
- *I = nullptr;
- }
+ for (const SymbolicRegion *SR : Postponed) {
+ if (SymReaper.isLive(SR->getSymbol())) {
+ Changed |= AddToWorkList(SR);
+ SR = nullptr;
}
}
@@ -2603,9 +2878,8 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store,
W.GenerateClusters();
// Enqueue the region roots onto the worklist.
- for (SymbolReaper::region_iterator I = SymReaper.region_begin(),
- E = SymReaper.region_end(); I != E; ++I) {
- W.AddToWorkList(*I);
+ for (const MemRegion *Reg : SymReaper.regions()) {
+ W.AddToWorkList(Reg);
}
do W.RunWorkList(); while (W.UpdatePostponed());
@@ -2613,9 +2887,7 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store,
// We have now scanned the store, marking reachable regions and symbols
// as live. We now remove all the regions that are dead from the store
// as well as update DSymbols with the set symbols that are now dead.
- for (RegionBindingsRef::iterator I = B.begin(), E = B.end(); I != E; ++I) {
- const MemRegion *Base = I.getKey();
-
+ for (const MemRegion *Base : llvm::make_first_range(B)) {
// If the cluster has been visited, we know the region has been marked.
// Otherwise, remove the dead entry.
if (!W.isVisited(Base))
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
index 7395622a659c..04165a443fff 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
@@ -1,9 +1,8 @@
//== SMTConstraintManager.cpp -----------------------------------*- C++ -*--==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
index b459b5adb511..eb9cde5c8918 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -19,25 +19,25 @@
#include "clang/AST/ExprObjC.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/Type.h"
-#include "clang/Basic/LLVM.h"
#include "clang/Analysis/AnalysisDeclContext.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/Basic/LLVM.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.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/SValVisitor.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/APSInt.h"
-#include "llvm/ADT/None.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Compiler.h"
#include <cassert>
+#include <optional>
#include <tuple>
using namespace clang;
@@ -49,9 +49,19 @@ using namespace ento;
void SValBuilder::anchor() {}
+SValBuilder::SValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context,
+ ProgramStateManager &stateMgr)
+ : Context(context), BasicVals(context, alloc),
+ SymMgr(context, BasicVals, alloc), MemMgr(context, alloc),
+ StateMgr(stateMgr),
+ AnOpts(
+ stateMgr.getOwningEngine().getAnalysisManager().getAnalyzerOptions()),
+ ArrayIndexTy(context.LongLongTy),
+ ArrayIndexWidth(context.getTypeSize(ArrayIndexTy)) {}
+
DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) {
if (Loc::isLocType(type))
- return makeNull();
+ return makeNullWithType(type);
if (type->isIntegralOrEnumerationType())
return makeIntVal(0, type);
@@ -64,8 +74,10 @@ DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) {
return UnknownVal();
}
-NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op,
- const llvm::APSInt& rhs, QualType type) {
+nonloc::SymbolVal SValBuilder::makeNonLoc(const SymExpr *lhs,
+ BinaryOperator::Opcode op,
+ const llvm::APSInt &rhs,
+ QualType type) {
// The Environment ensures we always get a persistent APSInt in
// BasicValueFactory, so we don't need to get the APSInt from
// BasicValueFactory again.
@@ -74,25 +86,35 @@ NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op,
return nonloc::SymbolVal(SymMgr.getSymIntExpr(lhs, op, rhs, type));
}
-NonLoc SValBuilder::makeNonLoc(const llvm::APSInt& lhs,
- BinaryOperator::Opcode op, const SymExpr *rhs,
- QualType type) {
+nonloc::SymbolVal SValBuilder::makeNonLoc(const llvm::APSInt &lhs,
+ BinaryOperator::Opcode op,
+ const SymExpr *rhs, QualType type) {
assert(rhs);
assert(!Loc::isLocType(type));
return nonloc::SymbolVal(SymMgr.getIntSymExpr(lhs, op, rhs, type));
}
-NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op,
- const SymExpr *rhs, QualType type) {
+nonloc::SymbolVal SValBuilder::makeNonLoc(const SymExpr *lhs,
+ BinaryOperator::Opcode op,
+ const SymExpr *rhs, QualType type) {
assert(lhs && rhs);
assert(!Loc::isLocType(type));
return nonloc::SymbolVal(SymMgr.getSymSymExpr(lhs, op, rhs, type));
}
-NonLoc SValBuilder::makeNonLoc(const SymExpr *operand,
- QualType fromTy, QualType toTy) {
+NonLoc SValBuilder::makeNonLoc(const SymExpr *operand, UnaryOperator::Opcode op,
+ QualType type) {
+ assert(operand);
+ assert(!Loc::isLocType(type));
+ return nonloc::SymbolVal(SymMgr.getUnarySymExpr(operand, op, type));
+}
+
+nonloc::SymbolVal SValBuilder::makeNonLoc(const SymExpr *operand,
+ QualType fromTy, QualType toTy) {
assert(operand);
assert(!Loc::isLocType(toTy));
+ if (fromTy == toTy)
+ return nonloc::SymbolVal(operand);
return nonloc::SymbolVal(SymMgr.getCastSymbol(operand, fromTy, toTy));
}
@@ -101,7 +123,8 @@ SVal SValBuilder::convertToArrayIndex(SVal val) {
return val;
// Common case: we have an appropriately sized integer.
- if (Optional<nonloc::ConcreteInt> CI = val.getAs<nonloc::ConcreteInt>()) {
+ if (std::optional<nonloc::ConcreteInt> CI =
+ val.getAs<nonloc::ConcreteInt>()) {
const llvm::APSInt& I = CI->getValue();
if (I.getBitWidth() == ArrayIndexWidth && I.isSigned())
return val;
@@ -208,6 +231,14 @@ SValBuilder::getConjuredHeapSymbolVal(const Expr *E,
return loc::MemRegionVal(MemMgr.getSymbolicHeapRegion(sym));
}
+loc::MemRegionVal SValBuilder::getAllocaRegionVal(const Expr *E,
+ const LocationContext *LCtx,
+ unsigned VisitCount) {
+ const AllocaRegion *R =
+ getRegionManager().getAllocaRegion(E, VisitCount, LCtx);
+ return loc::MemRegionVal(R);
+}
+
DefinedSVal SValBuilder::getMetadataSymbolVal(const void *symbolTag,
const MemRegion *region,
const Expr *expr, QualType type,
@@ -244,8 +275,7 @@ SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol,
}
DefinedSVal SValBuilder::getMemberPointer(const NamedDecl *ND) {
- assert(!ND || isa<CXXMethodDecl>(ND) || isa<FieldDecl>(ND) ||
- isa<IndirectFieldDecl>(ND));
+ assert(!ND || (isa<CXXMethodDecl, FieldDecl, IndirectFieldDecl>(ND)));
if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(ND)) {
// Sema treats pointers to static member functions as have function pointer
@@ -253,7 +283,7 @@ DefinedSVal SValBuilder::getMemberPointer(const NamedDecl *ND) {
// We don't need to play a similar trick for static member fields
// because these are represented as plain VarDecls and not FieldDecls
// in the AST.
- if (MD->isStatic())
+ if (!MD->isImplicitObjectMemberFunction())
return getFunctionPointer(MD);
}
@@ -275,11 +305,11 @@ DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block,
return loc::MemRegionVal(BD);
}
-Optional<loc::MemRegionVal>
+std::optional<loc::MemRegionVal>
SValBuilder::getCastedMemRegionVal(const MemRegion *R, QualType Ty) {
if (auto OptR = StateMgr.getStoreManager().castRegion(R, Ty))
return loc::MemRegionVal(*OptR);
- return None;
+ return std::nullopt;
}
/// Return a memory region for the 'this' object reference.
@@ -297,7 +327,7 @@ loc::MemRegionVal SValBuilder::getCXXThis(const CXXRecordDecl *D,
return loc::MemRegionVal(getRegionManager().getCXXThisRegion(PT, SFC));
}
-Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
+std::optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
E = E->IgnoreParens();
switch (E->getStmtClass()) {
@@ -350,7 +380,7 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
return makeBoolVal(cast<ObjCBoolLiteralExpr>(E));
case Stmt::CXXNullPtrLiteralExprClass:
- return makeNull();
+ return makeNullWithType(E->getType());
case Stmt::CStyleCastExprClass:
case Stmt::CXXFunctionalCastExprClass:
@@ -367,21 +397,20 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
case CK_NoOp:
case CK_BitCast: {
const Expr *SE = CE->getSubExpr();
- Optional<SVal> Val = getConstantVal(SE);
+ std::optional<SVal> Val = getConstantVal(SE);
if (!Val)
- return None;
+ return std::nullopt;
return evalCast(*Val, CE->getType(), SE->getType());
}
}
- // FALLTHROUGH
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
}
// If we don't have a special case, fall back to the AST's constant evaluator.
default: {
// Don't try to come up with a value for materialized temporaries.
if (E->isGLValue())
- return None;
+ return std::nullopt;
ASTContext &Ctx = getContext();
Expr::EvalResult Result;
@@ -390,9 +419,9 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
if (Loc::isLocType(E->getType()))
if (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull))
- return makeNull();
+ return makeNullWithType(E->getType());
- return None;
+ return std::nullopt;
}
}
}
@@ -405,25 +434,62 @@ SVal SValBuilder::makeSymExprValNN(BinaryOperator::Opcode Op,
// TODO: When the Max Complexity is reached, we should conjure a symbol
// instead of generating an Unknown value and propagate the taint info to it.
- const unsigned MaxComp = StateMgr.getOwningEngine()
- .getAnalysisManager()
- .options.MaxSymbolComplexity;
+ const unsigned MaxComp = AnOpts.MaxSymbolComplexity;
if (symLHS && symRHS &&
(symLHS->computeComplexity() + symRHS->computeComplexity()) < MaxComp)
return makeNonLoc(symLHS, Op, symRHS, ResultTy);
if (symLHS && symLHS->computeComplexity() < MaxComp)
- if (Optional<nonloc::ConcreteInt> rInt = RHS.getAs<nonloc::ConcreteInt>())
+ if (std::optional<nonloc::ConcreteInt> rInt =
+ RHS.getAs<nonloc::ConcreteInt>())
return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy);
if (symRHS && symRHS->computeComplexity() < MaxComp)
- if (Optional<nonloc::ConcreteInt> lInt = LHS.getAs<nonloc::ConcreteInt>())
+ if (std::optional<nonloc::ConcreteInt> lInt =
+ LHS.getAs<nonloc::ConcreteInt>())
return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy);
return UnknownVal();
}
+SVal SValBuilder::evalMinus(NonLoc X) {
+ switch (X.getKind()) {
+ case nonloc::ConcreteIntKind:
+ return makeIntVal(-X.castAs<nonloc::ConcreteInt>().getValue());
+ case nonloc::SymbolValKind:
+ return makeNonLoc(X.castAs<nonloc::SymbolVal>().getSymbol(), UO_Minus,
+ X.getType(Context));
+ default:
+ return UnknownVal();
+ }
+}
+
+SVal SValBuilder::evalComplement(NonLoc X) {
+ switch (X.getKind()) {
+ case nonloc::ConcreteIntKind:
+ return makeIntVal(~X.castAs<nonloc::ConcreteInt>().getValue());
+ case nonloc::SymbolValKind:
+ return makeNonLoc(X.castAs<nonloc::SymbolVal>().getSymbol(), UO_Not,
+ X.getType(Context));
+ default:
+ return UnknownVal();
+ }
+}
+
+SVal SValBuilder::evalUnaryOp(ProgramStateRef state, UnaryOperator::Opcode opc,
+ SVal operand, QualType type) {
+ auto OpN = operand.getAs<NonLoc>();
+ if (!OpN)
+ return UnknownVal();
+
+ if (opc == UO_Minus)
+ return evalMinus(*OpN);
+ if (opc == UO_Not)
+ return evalComplement(*OpN);
+ llvm_unreachable("Unexpected unary operator");
+}
+
SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op,
SVal lhs, SVal rhs, QualType type) {
if (lhs.isUndef() || rhs.isUndef())
@@ -432,8 +498,7 @@ SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op,
if (lhs.isUnknown() || rhs.isUnknown())
return UnknownVal();
- if (lhs.getAs<nonloc::LazyCompoundVal>() ||
- rhs.getAs<nonloc::LazyCompoundVal>()) {
+ if (isa<nonloc::LazyCompoundVal>(lhs) || isa<nonloc::LazyCompoundVal>(rhs)) {
return UnknownVal();
}
@@ -445,20 +510,30 @@ SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op,
return UnknownVal();
}
- if (Optional<Loc> LV = lhs.getAs<Loc>()) {
- if (Optional<Loc> RV = rhs.getAs<Loc>())
+ if (std::optional<Loc> LV = lhs.getAs<Loc>()) {
+ if (std::optional<Loc> RV = rhs.getAs<Loc>())
return evalBinOpLL(state, op, *LV, *RV, type);
return evalBinOpLN(state, op, *LV, rhs.castAs<NonLoc>(), type);
}
- if (Optional<Loc> RV = rhs.getAs<Loc>()) {
- // Support pointer arithmetic where the addend is on the left
- // and the pointer on the right.
- assert(op == BO_Add);
+ if (const std::optional<Loc> RV = rhs.getAs<Loc>()) {
+ const auto IsCommutative = [](BinaryOperatorKind Op) {
+ return Op == BO_Mul || Op == BO_Add || Op == BO_And || Op == BO_Xor ||
+ Op == BO_Or;
+ };
+
+ if (IsCommutative(op)) {
+ // Swap operands.
+ return evalBinOpLN(state, op, *RV, lhs.castAs<NonLoc>(), type);
+ }
- // Commute the operands.
- return evalBinOpLN(state, op, *RV, lhs.castAs<NonLoc>(), type);
+ // If the right operand is a concrete int location then we have nothing
+ // better but to treat it as a simple nonloc.
+ if (auto RV = rhs.getAs<loc::ConcreteInt>()) {
+ const nonloc::ConcreteInt RhsAsLoc = makeIntVal(RV->getValue());
+ return evalBinOpNN(state, op, lhs.castAs<NonLoc>(), RhsAsLoc, type);
+ }
}
return evalBinOpNN(state, op, lhs.castAs<NonLoc>(), rhs.castAs<NonLoc>(),
@@ -531,11 +606,9 @@ SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val,
APSIntType ToType(getContext().getTypeSize(castTy),
castTy->isUnsignedIntegerType());
llvm::APSInt ToTypeMax = ToType.getMaxValue();
- NonLoc ToTypeMaxVal =
- makeIntVal(ToTypeMax.isUnsigned() ? ToTypeMax.getZExtValue()
- : ToTypeMax.getSExtValue(),
- castTy)
- .castAs<NonLoc>();
+
+ NonLoc ToTypeMaxVal = makeIntVal(ToTypeMax);
+
// Check the range of the symbol being casted against the maximum value of the
// target type.
NonLoc FromVal = val.castAs<NonLoc>();
@@ -546,448 +619,489 @@ SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val,
std::tie(IsNotTruncated, IsTruncated) = state->assume(CompVal);
if (!IsNotTruncated && IsTruncated) {
// Symbol is truncated so we evaluate it as a cast.
- NonLoc CastVal = makeNonLoc(se, originalTy, castTy);
- return CastVal;
+ return makeNonLoc(se, originalTy, castTy);
}
return evalCast(val, castTy, originalTy);
}
//===----------------------------------------------------------------------===//
-// Cast methods.
-// `evalCast` is the main method
-// `evalCastKind` and `evalCastSubKind` are helpers
+// Cast method.
+// `evalCast` and its helper `EvalCastVisitor`
//===----------------------------------------------------------------------===//
-/// Cast a given SVal to another SVal using given QualType's.
-/// \param V -- SVal that should be casted.
-/// \param CastTy -- QualType that V should be casted according to.
-/// \param OriginalTy -- QualType which is associated to V. It provides
-/// additional information about what type the cast performs from.
-/// \returns the most appropriate casted SVal.
-/// Note: Many cases don't use an exact OriginalTy. It can be extracted
-/// from SVal or the cast can performs unconditionaly. Always pass OriginalTy!
-/// It can be crucial in certain cases and generates different results.
-/// FIXME: If `OriginalTy.isNull()` is true, then cast performs based on CastTy
-/// only. This behavior is uncertain and should be improved.
-SVal SValBuilder::evalCast(SVal V, QualType CastTy, QualType OriginalTy) {
- if (CastTy.isNull())
- return V;
-
- CastTy = Context.getCanonicalType(CastTy);
+namespace {
+class EvalCastVisitor : public SValVisitor<EvalCastVisitor, SVal> {
+private:
+ SValBuilder &VB;
+ ASTContext &Context;
+ QualType CastTy, OriginalTy;
- const bool IsUnknownOriginalType = OriginalTy.isNull();
- if (!IsUnknownOriginalType) {
- OriginalTy = Context.getCanonicalType(OriginalTy);
+public:
+ EvalCastVisitor(SValBuilder &VB, QualType CastTy, QualType OriginalTy)
+ : VB(VB), Context(VB.getContext()), CastTy(CastTy),
+ OriginalTy(OriginalTy) {}
- if (CastTy == OriginalTy)
+ SVal Visit(SVal V) {
+ if (CastTy.isNull())
return V;
- // FIXME: Move this check to the most appropriate
- // evalCastKind/evalCastSubKind function. For const casts, casts to void,
- // just propagate the value.
- if (!CastTy->isVariableArrayType() && !OriginalTy->isVariableArrayType())
- if (shouldBeModeledWithNoOp(Context, Context.getPointerType(CastTy),
- Context.getPointerType(OriginalTy)))
- return V;
- }
-
- // Cast SVal according to kinds.
- switch (V.getBaseKind()) {
- case SVal::UndefinedValKind:
- return evalCastKind(V.castAs<UndefinedVal>(), CastTy, OriginalTy);
- case SVal::UnknownValKind:
- return evalCastKind(V.castAs<UnknownVal>(), CastTy, OriginalTy);
- case SVal::LocKind:
- return evalCastKind(V.castAs<Loc>(), CastTy, OriginalTy);
- case SVal::NonLocKind:
- return evalCastKind(V.castAs<NonLoc>(), CastTy, OriginalTy);
- }
-
- llvm_unreachable("Unknown SVal kind");
-}
+ CastTy = Context.getCanonicalType(CastTy);
-SVal SValBuilder::evalCastKind(UndefinedVal V, QualType CastTy,
- QualType OriginalTy) {
- return V;
-}
+ const bool IsUnknownOriginalType = OriginalTy.isNull();
+ if (!IsUnknownOriginalType) {
+ OriginalTy = Context.getCanonicalType(OriginalTy);
-SVal SValBuilder::evalCastKind(UnknownVal V, QualType CastTy,
- QualType OriginalTy) {
- return V;
-}
+ if (CastTy == OriginalTy)
+ return V;
-SVal SValBuilder::evalCastKind(Loc V, QualType CastTy, QualType OriginalTy) {
- switch (V.getSubKind()) {
- case loc::ConcreteIntKind:
- return evalCastSubKind(V.castAs<loc::ConcreteInt>(), CastTy, OriginalTy);
- case loc::GotoLabelKind:
- return evalCastSubKind(V.castAs<loc::GotoLabel>(), CastTy, OriginalTy);
- case loc::MemRegionValKind:
- return evalCastSubKind(V.castAs<loc::MemRegionVal>(), CastTy, OriginalTy);
+ // FIXME: Move this check to the most appropriate
+ // evalCastKind/evalCastSubKind function. For const casts, casts to void,
+ // just propagate the value.
+ if (!CastTy->isVariableArrayType() && !OriginalTy->isVariableArrayType())
+ if (shouldBeModeledWithNoOp(Context, Context.getPointerType(CastTy),
+ Context.getPointerType(OriginalTy)))
+ return V;
+ }
+ return SValVisitor::Visit(V);
}
+ SVal VisitUndefinedVal(UndefinedVal V) { return V; }
+ SVal VisitUnknownVal(UnknownVal V) { return V; }
+ SVal VisitConcreteInt(loc::ConcreteInt V) {
+ // Pointer to bool.
+ if (CastTy->isBooleanType())
+ return VB.makeTruthVal(V.getValue().getBoolValue(), CastTy);
+
+ // Pointer to integer.
+ if (CastTy->isIntegralOrEnumerationType()) {
+ llvm::APSInt Value = V.getValue();
+ VB.getBasicValueFactory().getAPSIntType(CastTy).apply(Value);
+ return VB.makeIntVal(Value);
+ }
- llvm_unreachable("Unknown SVal kind");
-}
+ // Pointer to any pointer.
+ if (Loc::isLocType(CastTy)) {
+ llvm::APSInt Value = V.getValue();
+ VB.getBasicValueFactory().getAPSIntType(CastTy).apply(Value);
+ return loc::ConcreteInt(VB.getBasicValueFactory().getValue(Value));
+ }
-SVal SValBuilder::evalCastKind(NonLoc V, QualType CastTy, QualType OriginalTy) {
- switch (V.getSubKind()) {
- case nonloc::CompoundValKind:
- return evalCastSubKind(V.castAs<nonloc::CompoundVal>(), CastTy, OriginalTy);
- case nonloc::ConcreteIntKind:
- return evalCastSubKind(V.castAs<nonloc::ConcreteInt>(), CastTy, OriginalTy);
- case nonloc::LazyCompoundValKind:
- return evalCastSubKind(V.castAs<nonloc::LazyCompoundVal>(), CastTy,
- OriginalTy);
- case nonloc::LocAsIntegerKind:
- return evalCastSubKind(V.castAs<nonloc::LocAsInteger>(), CastTy,
- OriginalTy);
- case nonloc::SymbolValKind:
- return evalCastSubKind(V.castAs<nonloc::SymbolVal>(), CastTy, OriginalTy);
- case nonloc::PointerToMemberKind:
- return evalCastSubKind(V.castAs<nonloc::PointerToMember>(), CastTy,
- OriginalTy);
+ // Pointer to whatever else.
+ return UnknownVal();
}
+ SVal VisitGotoLabel(loc::GotoLabel V) {
+ // Pointer to bool.
+ if (CastTy->isBooleanType())
+ // Labels are always true.
+ return VB.makeTruthVal(true, CastTy);
+
+ // Pointer to integer.
+ if (CastTy->isIntegralOrEnumerationType()) {
+ const unsigned BitWidth = Context.getIntWidth(CastTy);
+ return VB.makeLocAsInteger(V, BitWidth);
+ }
- llvm_unreachable("Unknown SVal kind");
-}
+ const bool IsUnknownOriginalType = OriginalTy.isNull();
+ if (!IsUnknownOriginalType) {
+ // Array to pointer.
+ if (isa<ArrayType>(OriginalTy))
+ if (CastTy->isPointerType() || CastTy->isReferenceType())
+ return UnknownVal();
+ }
-SVal SValBuilder::evalCastSubKind(loc::ConcreteInt V, QualType CastTy,
- QualType OriginalTy) {
- // Pointer to bool.
- if (CastTy->isBooleanType())
- return makeTruthVal(V.getValue().getBoolValue(), CastTy);
+ // Pointer to any pointer.
+ if (Loc::isLocType(CastTy))
+ return V;
- // Pointer to integer.
- if (CastTy->isIntegralOrEnumerationType()) {
- llvm::APSInt Value = V.getValue();
- BasicVals.getAPSIntType(CastTy).apply(Value);
- return makeIntVal(Value);
+ // Pointer to whatever else.
+ return UnknownVal();
}
+ SVal VisitMemRegionVal(loc::MemRegionVal V) {
+ // Pointer to bool.
+ if (CastTy->isBooleanType()) {
+ const MemRegion *R = V.getRegion();
+ if (const FunctionCodeRegion *FTR = dyn_cast<FunctionCodeRegion>(R))
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(FTR->getDecl()))
+ if (FD->isWeak())
+ // FIXME: Currently we are using an extent symbol here,
+ // because there are no generic region address metadata
+ // symbols to use, only content metadata.
+ return nonloc::SymbolVal(
+ VB.getSymbolManager().getExtentSymbol(FTR));
+
+ if (const SymbolicRegion *SymR = R->getSymbolicBase()) {
+ SymbolRef Sym = SymR->getSymbol();
+ QualType Ty = Sym->getType();
+ // This change is needed for architectures with varying
+ // pointer widths. See the amdgcn opencl reproducer with
+ // this change as an example: solver-sym-simplification-ptr-bool.cl
+ if (!Ty->isReferenceType())
+ return VB.makeNonLoc(
+ Sym, BO_NE, VB.getBasicValueFactory().getZeroWithTypeSize(Ty),
+ CastTy);
+ }
+ // Non-symbolic memory regions are always true.
+ return VB.makeTruthVal(true, CastTy);
+ }
- // Pointer to any pointer.
- if (Loc::isLocType(CastTy))
- return V;
+ const bool IsUnknownOriginalType = OriginalTy.isNull();
+ // Try to cast to array
+ const auto *ArrayTy =
+ IsUnknownOriginalType
+ ? nullptr
+ : dyn_cast<ArrayType>(OriginalTy.getCanonicalType());
+
+ // Pointer to integer.
+ if (CastTy->isIntegralOrEnumerationType()) {
+ SVal Val = V;
+ // Array to integer.
+ if (ArrayTy) {
+ // We will always decay to a pointer.
+ QualType ElemTy = ArrayTy->getElementType();
+ Val = VB.getStateManager().ArrayToPointer(V, ElemTy);
+ // FIXME: Keep these here for now in case we decide soon that we
+ // need the original decayed type.
+ // QualType elemTy = cast<ArrayType>(originalTy)->getElementType();
+ // QualType pointerTy = C.getPointerType(elemTy);
+ }
+ const unsigned BitWidth = Context.getIntWidth(CastTy);
+ return VB.makeLocAsInteger(Val.castAs<Loc>(), BitWidth);
+ }
- // Pointer to whatever else.
- return UnknownVal();
-}
+ // Pointer to pointer.
+ if (Loc::isLocType(CastTy)) {
-SVal SValBuilder::evalCastSubKind(loc::GotoLabel V, QualType CastTy,
- QualType OriginalTy) {
- // Pointer to bool.
- if (CastTy->isBooleanType())
- // Labels are always true.
- return makeTruthVal(true, CastTy);
+ if (IsUnknownOriginalType) {
+ // When retrieving symbolic pointer and expecting a non-void pointer,
+ // wrap them into element regions of the expected type if necessary.
+ // It is necessary to make sure that the retrieved value makes sense,
+ // because there's no other cast in the AST that would tell us to cast
+ // it to the correct pointer type. We might need to do that for non-void
+ // pointers as well.
+ // FIXME: We really need a single good function to perform casts for us
+ // correctly every time we need it.
+ const MemRegion *R = V.getRegion();
+ if (CastTy->isPointerType() && !CastTy->isVoidPointerType()) {
+ if (const auto *SR = dyn_cast<SymbolicRegion>(R)) {
+ QualType SRTy = SR->getSymbol()->getType();
+
+ auto HasSameUnqualifiedPointeeType = [](QualType ty1,
+ QualType ty2) {
+ return ty1->getPointeeType().getCanonicalType().getTypePtr() ==
+ ty2->getPointeeType().getCanonicalType().getTypePtr();
+ };
+ if (!HasSameUnqualifiedPointeeType(SRTy, CastTy)) {
+ if (auto OptMemRegV = VB.getCastedMemRegionVal(SR, CastTy))
+ return *OptMemRegV;
+ }
+ }
+ }
+ // Next fixes pointer dereference using type different from its initial
+ // one. See PR37503 and PR49007 for details.
+ if (const auto *ER = dyn_cast<ElementRegion>(R)) {
+ if (auto OptMemRegV = VB.getCastedMemRegionVal(ER, CastTy))
+ return *OptMemRegV;
+ }
- // Pointer to integer.
- if (CastTy->isIntegralOrEnumerationType()) {
- const unsigned BitWidth = Context.getIntWidth(CastTy);
- return makeLocAsInteger(V, BitWidth);
- }
+ return V;
+ }
- const bool IsUnknownOriginalType = OriginalTy.isNull();
- if (!IsUnknownOriginalType) {
- // Array to pointer.
- if (isa<ArrayType>(OriginalTy))
- if (CastTy->isPointerType() || CastTy->isReferenceType())
- return UnknownVal();
- }
+ if (OriginalTy->isIntegralOrEnumerationType() ||
+ OriginalTy->isBlockPointerType() ||
+ OriginalTy->isFunctionPointerType())
+ return V;
- // Pointer to any pointer.
- if (Loc::isLocType(CastTy))
- return V;
+ // Array to pointer.
+ if (ArrayTy) {
+ // Are we casting from an array to a pointer? If so just pass on
+ // the decayed value.
+ if (CastTy->isPointerType() || CastTy->isReferenceType()) {
+ // We will always decay to a pointer.
+ QualType ElemTy = ArrayTy->getElementType();
+ return VB.getStateManager().ArrayToPointer(V, ElemTy);
+ }
+ // Are we casting from an array to an integer? If so, cast the decayed
+ // pointer value to an integer.
+ assert(CastTy->isIntegralOrEnumerationType());
+ }
- // Pointer to whatever else.
- return UnknownVal();
-}
+ // Other pointer to pointer.
+ assert(Loc::isLocType(OriginalTy) || OriginalTy->isFunctionType() ||
+ CastTy->isReferenceType());
-static bool hasSameUnqualifiedPointeeType(QualType ty1, QualType ty2) {
- return ty1->getPointeeType().getCanonicalType().getTypePtr() ==
- ty2->getPointeeType().getCanonicalType().getTypePtr();
-}
-
-SVal SValBuilder::evalCastSubKind(loc::MemRegionVal V, QualType CastTy,
- QualType OriginalTy) {
- // Pointer to bool.
- if (CastTy->isBooleanType()) {
- const MemRegion *R = V.getRegion();
- if (const FunctionCodeRegion *FTR = dyn_cast<FunctionCodeRegion>(R))
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(FTR->getDecl()))
- if (FD->isWeak())
- // FIXME: Currently we are using an extent symbol here,
- // because there are no generic region address metadata
- // symbols to use, only content metadata.
- return nonloc::SymbolVal(SymMgr.getExtentSymbol(FTR));
-
- if (const SymbolicRegion *SymR = R->getSymbolicBase()) {
- SymbolRef Sym = SymR->getSymbol();
- QualType Ty = Sym->getType();
- // This change is needed for architectures with varying
- // pointer widths. See the amdgcn opencl reproducer with
- // this change as an example: solver-sym-simplification-ptr-bool.cl
- // FIXME: We could encounter a reference here,
- // try returning a concrete 'true' since it might
- // be easier on the solver.
- // FIXME: Cleanup remainder of `getZeroWithPtrWidth ()`
- // and `getIntWithPtrWidth()` functions to prevent future
- // confusion
- const llvm::APSInt &Zero = Ty->isReferenceType()
- ? BasicVals.getZeroWithPtrWidth()
- : BasicVals.getZeroWithTypeSize(Ty);
- return makeNonLoc(Sym, BO_NE, Zero, CastTy);
+ // We get a symbolic function pointer for a dereference of a function
+ // pointer, but it is of function type. Example:
+
+ // struct FPRec {
+ // void (*my_func)(int * x);
+ // };
+ //
+ // int bar(int x);
+ //
+ // int f1_a(struct FPRec* foo) {
+ // int x;
+ // (*foo->my_func)(&x);
+ // return bar(x)+1; // no-warning
+ // }
+
+ // Get the result of casting a region to a different type.
+ const MemRegion *R = V.getRegion();
+ if (auto OptMemRegV = VB.getCastedMemRegionVal(R, CastTy))
+ return *OptMemRegV;
}
- // Non-symbolic memory regions are always true.
- return makeTruthVal(true, CastTy);
+
+ // Pointer to whatever else.
+ // FIXME: There can be gross cases where one casts the result of a
+ // function (that returns a pointer) to some other value that happens to
+ // fit within that pointer value. We currently have no good way to model
+ // such operations. When this happens, the underlying operation is that
+ // the caller is reasoning about bits. Conceptually we are layering a
+ // "view" of a location on top of those bits. Perhaps we need to be more
+ // lazy about mutual possible views, even on an SVal? This may be
+ // necessary for bit-level reasoning as well.
+ return UnknownVal();
}
+ SVal VisitCompoundVal(nonloc::CompoundVal V) {
+ // Compound to whatever.
+ return UnknownVal();
+ }
+ SVal VisitConcreteInt(nonloc::ConcreteInt V) {
+ auto CastedValue = [V, this]() {
+ llvm::APSInt Value = V.getValue();
+ VB.getBasicValueFactory().getAPSIntType(CastTy).apply(Value);
+ return Value;
+ };
+
+ // Integer to bool.
+ if (CastTy->isBooleanType())
+ return VB.makeTruthVal(V.getValue().getBoolValue(), CastTy);
+
+ // Integer to pointer.
+ if (CastTy->isIntegralOrEnumerationType())
+ return VB.makeIntVal(CastedValue());
- const bool IsUnknownOriginalType = OriginalTy.isNull();
- // Try to cast to array
- const auto *ArrayTy =
- IsUnknownOriginalType
- ? nullptr
- : dyn_cast<ArrayType>(OriginalTy.getCanonicalType());
-
- // Pointer to integer.
- if (CastTy->isIntegralOrEnumerationType()) {
- SVal Val = V;
- // Array to integer.
- if (ArrayTy) {
- // We will always decay to a pointer.
- QualType ElemTy = ArrayTy->getElementType();
- Val = StateMgr.ArrayToPointer(V, ElemTy);
- // FIXME: Keep these here for now in case we decide soon that we
- // need the original decayed type.
- // QualType elemTy = cast<ArrayType>(originalTy)->getElementType();
- // QualType pointerTy = C.getPointerType(elemTy);
- }
- const unsigned BitWidth = Context.getIntWidth(CastTy);
- return makeLocAsInteger(Val.castAs<Loc>(), BitWidth);
+ // Integer to pointer.
+ if (Loc::isLocType(CastTy))
+ return VB.makeIntLocVal(CastedValue());
+
+ // Pointer to whatever else.
+ return UnknownVal();
}
+ SVal VisitLazyCompoundVal(nonloc::LazyCompoundVal V) {
+ // LazyCompound to whatever.
+ return UnknownVal();
+ }
+ SVal VisitLocAsInteger(nonloc::LocAsInteger V) {
+ Loc L = V.getLoc();
+
+ // Pointer as integer to bool.
+ if (CastTy->isBooleanType())
+ // Pass to Loc function.
+ return Visit(L);
+
+ const bool IsUnknownOriginalType = OriginalTy.isNull();
+ // Pointer as integer to pointer.
+ if (!IsUnknownOriginalType && Loc::isLocType(CastTy) &&
+ OriginalTy->isIntegralOrEnumerationType()) {
+ if (const MemRegion *R = L.getAsRegion())
+ if (auto OptMemRegV = VB.getCastedMemRegionVal(R, CastTy))
+ return *OptMemRegV;
+ return L;
+ }
- // Pointer to pointer.
- if (Loc::isLocType(CastTy)) {
-
- if (IsUnknownOriginalType) {
- // When retrieving symbolic pointer and expecting a non-void pointer,
- // wrap them into element regions of the expected type if necessary.
- // It is necessary to make sure that the retrieved value makes sense,
- // because there's no other cast in the AST that would tell us to cast
- // it to the correct pointer type. We might need to do that for non-void
- // pointers as well.
- // FIXME: We really need a single good function to perform casts for us
- // correctly every time we need it.
- const MemRegion *R = V.getRegion();
- if (CastTy->isPointerType() && !CastTy->isVoidPointerType()) {
- if (const auto *SR = dyn_cast<SymbolicRegion>(R)) {
- QualType SRTy = SR->getSymbol()->getType();
- if (!hasSameUnqualifiedPointeeType(SRTy, CastTy)) {
- if (auto OptMemRegV = getCastedMemRegionVal(SR, CastTy))
- return *OptMemRegV;
- }
- }
- }
- // Next fixes pointer dereference using type different from its initial
- // one. See PR37503 and PR49007 for details.
- if (const auto *ER = dyn_cast<ElementRegion>(R)) {
- if (auto OptMemRegV = getCastedMemRegionVal(ER, CastTy))
+ // Pointer as integer with region to integer/pointer.
+ const MemRegion *R = L.getAsRegion();
+ if (!IsUnknownOriginalType && R) {
+ if (CastTy->isIntegralOrEnumerationType())
+ return VisitMemRegionVal(loc::MemRegionVal(R));
+
+ if (Loc::isLocType(CastTy)) {
+ assert(Loc::isLocType(OriginalTy) || OriginalTy->isFunctionType() ||
+ CastTy->isReferenceType());
+ // Delegate to store manager to get the result of casting a region to a
+ // different type. If the MemRegion* returned is NULL, this expression
+ // Evaluates to UnknownVal.
+ if (auto OptMemRegV = VB.getCastedMemRegionVal(R, CastTy))
return *OptMemRegV;
}
+ } else {
+ if (Loc::isLocType(CastTy)) {
+ if (IsUnknownOriginalType)
+ return VisitMemRegionVal(loc::MemRegionVal(R));
+ return L;
+ }
- return V;
- }
+ SymbolRef SE = nullptr;
+ if (R) {
+ if (const SymbolicRegion *SR =
+ dyn_cast<SymbolicRegion>(R->StripCasts())) {
+ SE = SR->getSymbol();
+ }
+ }
- if (OriginalTy->isIntegralOrEnumerationType() ||
- OriginalTy->isBlockPointerType() || OriginalTy->isFunctionPointerType())
- return V;
+ if (!CastTy->isFloatingType() || !SE || SE->getType()->isFloatingType()) {
+ // FIXME: Correctly support promotions/truncations.
+ const unsigned CastSize = Context.getIntWidth(CastTy);
+ if (CastSize == V.getNumBits())
+ return V;
- // Array to pointer.
- if (ArrayTy) {
- // Are we casting from an array to a pointer? If so just pass on
- // the decayed value.
- if (CastTy->isPointerType() || CastTy->isReferenceType()) {
- // We will always decay to a pointer.
- QualType ElemTy = ArrayTy->getElementType();
- return StateMgr.ArrayToPointer(V, ElemTy);
+ return VB.makeLocAsInteger(L, CastSize);
}
- // Are we casting from an array to an integer? If so, cast the decayed
- // pointer value to an integer.
- assert(CastTy->isIntegralOrEnumerationType());
}
- // Other pointer to pointer.
- assert(Loc::isLocType(OriginalTy) || OriginalTy->isFunctionType() ||
- CastTy->isReferenceType());
-
- // We get a symbolic function pointer for a dereference of a function
- // pointer, but it is of function type. Example:
-
- // struct FPRec {
- // void (*my_func)(int * x);
- // };
- //
- // int bar(int x);
- //
- // int f1_a(struct FPRec* foo) {
- // int x;
- // (*foo->my_func)(&x);
- // return bar(x)+1; // no-warning
- // }
-
- // Get the result of casting a region to a different type.
- const MemRegion *R = V.getRegion();
- if (auto OptMemRegV = getCastedMemRegionVal(R, CastTy))
- return *OptMemRegV;
+ // Pointer as integer to whatever else.
+ return UnknownVal();
}
+ SVal VisitSymbolVal(nonloc::SymbolVal V) {
+ SymbolRef SE = V.getSymbol();
+
+ const bool IsUnknownOriginalType = OriginalTy.isNull();
+ // Symbol to bool.
+ if (!IsUnknownOriginalType && CastTy->isBooleanType()) {
+ // Non-float to bool.
+ if (Loc::isLocType(OriginalTy) ||
+ OriginalTy->isIntegralOrEnumerationType() ||
+ OriginalTy->isMemberPointerType()) {
+ BasicValueFactory &BVF = VB.getBasicValueFactory();
+ return VB.makeNonLoc(SE, BO_NE, BVF.getValue(0, SE->getType()), CastTy);
+ }
+ } else {
+ // Symbol to integer, float.
+ QualType T = Context.getCanonicalType(SE->getType());
+
+ // Produce SymbolCast if CastTy and T are different integers.
+ // NOTE: In the end the type of SymbolCast shall be equal to CastTy.
+ if (T->isIntegralOrUnscopedEnumerationType() &&
+ CastTy->isIntegralOrUnscopedEnumerationType()) {
+ AnalyzerOptions &Opts = VB.getStateManager()
+ .getOwningEngine()
+ .getAnalysisManager()
+ .getAnalyzerOptions();
+ // If appropriate option is disabled, ignore the cast.
+ // NOTE: ShouldSupportSymbolicIntegerCasts is `false` by default.
+ if (!Opts.ShouldSupportSymbolicIntegerCasts)
+ return V;
+ return simplifySymbolCast(V, CastTy);
+ }
+ if (!Loc::isLocType(CastTy))
+ if (!IsUnknownOriginalType || !CastTy->isFloatingType() ||
+ T->isFloatingType())
+ return VB.makeNonLoc(SE, T, CastTy);
+ }
- // Pointer to whatever else.
- // FIXME: There can be gross cases where one casts the result of a
- // function (that returns a pointer) to some other value that happens to
- // fit within that pointer value. We currently have no good way to model
- // such operations. When this happens, the underlying operation is that
- // the caller is reasoning about bits. Conceptually we are layering a
- // "view" of a location on top of those bits. Perhaps we need to be more
- // lazy about mutual possible views, even on an SVal? This may be
- // necessary for bit-level reasoning as well.
- return UnknownVal();
-}
-
-SVal SValBuilder::evalCastSubKind(nonloc::CompoundVal V, QualType CastTy,
- QualType OriginalTy) {
- // Compound to whatever.
- return UnknownVal();
-}
-
-SVal SValBuilder::evalCastSubKind(nonloc::ConcreteInt V, QualType CastTy,
- QualType OriginalTy) {
- auto CastedValue = [V, CastTy, this]() {
- llvm::APSInt Value = V.getValue();
- BasicVals.getAPSIntType(CastTy).apply(Value);
- return Value;
- };
-
- // Integer to bool.
- if (CastTy->isBooleanType())
- return makeTruthVal(V.getValue().getBoolValue(), CastTy);
-
- // Integer to pointer.
- if (CastTy->isIntegralOrEnumerationType())
- return makeIntVal(CastedValue());
-
- // Integer to pointer.
- if (Loc::isLocType(CastTy))
- return makeIntLocVal(CastedValue());
+ // FIXME: We should be able to cast NonLoc -> Loc
+ // (when Loc::isLocType(CastTy) is true)
+ // But it's hard to do as SymbolicRegions can't refer to SymbolCasts holding
+ // generic SymExprs. Check the commit message for the details.
- // Pointer to whatever else.
- return UnknownVal();
-}
+ // Symbol to pointer and whatever else.
+ return UnknownVal();
+ }
+ SVal VisitPointerToMember(nonloc::PointerToMember V) {
+ // Member pointer to whatever.
+ return V;
+ }
-SVal SValBuilder::evalCastSubKind(nonloc::LazyCompoundVal V, QualType CastTy,
- QualType OriginalTy) {
- // Compound to whatever.
- return UnknownVal();
-}
+ /// Reduce cast expression by removing redundant intermediate casts.
+ /// E.g.
+ /// - (char)(short)(int x) -> (char)(int x)
+ /// - (int)(int x) -> int x
+ ///
+ /// \param V -- SymbolVal, which pressumably contains SymbolCast or any symbol
+ /// that is applicable for cast operation.
+ /// \param CastTy -- QualType, which `V` shall be cast to.
+ /// \return SVal with simplified cast expression.
+ /// \note: Currently only support integral casts.
+ nonloc::SymbolVal simplifySymbolCast(nonloc::SymbolVal V, QualType CastTy) {
+ // We use seven conditions to recognize a simplification case.
+ // For the clarity let `CastTy` be `C`, SE->getType() - `T`, root type -
+ // `R`, prefix `u` for unsigned, `s` for signed, no prefix - any sign: E.g.
+ // (char)(short)(uint x)
+ // ( sC )( sT )( uR x)
+ //
+ // C === R (the same type)
+ // (char)(char x) -> (char x)
+ // (long)(long x) -> (long x)
+ // Note: Comparisons operators below are for bit width.
+ // C == T
+ // (short)(short)(int x) -> (short)(int x)
+ // (int)(long)(char x) -> (int)(char x) (sizeof(long) == sizeof(int))
+ // (long)(ullong)(char x) -> (long)(char x) (sizeof(long) ==
+ // sizeof(ullong))
+ // C < T
+ // (short)(int)(char x) -> (short)(char x)
+ // (char)(int)(short x) -> (char)(short x)
+ // (short)(int)(short x) -> (short x)
+ // C > T > uR
+ // (int)(short)(uchar x) -> (int)(uchar x)
+ // (uint)(short)(uchar x) -> (uint)(uchar x)
+ // (int)(ushort)(uchar x) -> (int)(uchar x)
+ // C > sT > sR
+ // (int)(short)(char x) -> (int)(char x)
+ // (uint)(short)(char x) -> (uint)(char x)
+ // C > sT == sR
+ // (int)(char)(char x) -> (int)(char x)
+ // (uint)(short)(short x) -> (uint)(short x)
+ // C > uT == uR
+ // (int)(uchar)(uchar x) -> (int)(uchar x)
+ // (uint)(ushort)(ushort x) -> (uint)(ushort x)
+ // (llong)(ulong)(uint x) -> (llong)(uint x) (sizeof(ulong) ==
+ // sizeof(uint))
+
+ SymbolRef SE = V.getSymbol();
+ QualType T = Context.getCanonicalType(SE->getType());
-SVal SValBuilder::evalCastSubKind(nonloc::LocAsInteger V, QualType CastTy,
- QualType OriginalTy) {
- Loc L = V.getLoc();
+ if (T == CastTy)
+ return V;
- // Pointer as integer to bool.
- if (CastTy->isBooleanType())
- // Pass to Loc function.
- return evalCastKind(L, CastTy, OriginalTy);
+ if (!isa<SymbolCast>(SE))
+ return VB.makeNonLoc(SE, T, CastTy);
- const bool IsUnknownOriginalType = OriginalTy.isNull();
- // Pointer as integer to pointer.
- if (!IsUnknownOriginalType && Loc::isLocType(CastTy) &&
- OriginalTy->isIntegralOrEnumerationType()) {
- if (const MemRegion *R = L.getAsRegion())
- if (auto OptMemRegV = getCastedMemRegionVal(R, CastTy))
- return *OptMemRegV;
- return L;
- }
+ SymbolRef RootSym = cast<SymbolCast>(SE)->getOperand();
+ QualType RT = RootSym->getType().getCanonicalType();
- // Pointer as integer with region to integer/pointer.
- const MemRegion *R = L.getAsRegion();
- if (!IsUnknownOriginalType && R) {
- if (CastTy->isIntegralOrEnumerationType())
- return evalCastSubKind(loc::MemRegionVal(R), CastTy, OriginalTy);
+ // FIXME support simplification from non-integers.
+ if (!RT->isIntegralOrEnumerationType())
+ return VB.makeNonLoc(SE, T, CastTy);
- if (Loc::isLocType(CastTy)) {
- assert(Loc::isLocType(OriginalTy) || OriginalTy->isFunctionType() ||
- CastTy->isReferenceType());
- // Delegate to store manager to get the result of casting a region to a
- // different type. If the MemRegion* returned is NULL, this expression
- // Evaluates to UnknownVal.
- if (auto OptMemRegV = getCastedMemRegionVal(R, CastTy))
- return *OptMemRegV;
- }
- } else {
- if (Loc::isLocType(CastTy)) {
- if (IsUnknownOriginalType)
- return evalCastSubKind(loc::MemRegionVal(R), CastTy, OriginalTy);
- return L;
- }
-
- SymbolRef SE = nullptr;
- if (R) {
- if (const SymbolicRegion *SR =
- dyn_cast<SymbolicRegion>(R->StripCasts())) {
- SE = SR->getSymbol();
- }
- }
+ BasicValueFactory &BVF = VB.getBasicValueFactory();
+ APSIntType CTy = BVF.getAPSIntType(CastTy);
+ APSIntType TTy = BVF.getAPSIntType(T);
- if (!CastTy->isFloatingType() || !SE || SE->getType()->isFloatingType()) {
- // FIXME: Correctly support promotions/truncations.
- const unsigned CastSize = Context.getIntWidth(CastTy);
- if (CastSize == V.getNumBits())
- return V;
+ const auto WC = CTy.getBitWidth();
+ const auto WT = TTy.getBitWidth();
- return makeLocAsInteger(L, CastSize);
+ if (WC <= WT) {
+ const bool isSameType = (RT == CastTy);
+ if (isSameType)
+ return nonloc::SymbolVal(RootSym);
+ return VB.makeNonLoc(RootSym, RT, CastTy);
}
- }
- // Pointer as integer to whatever else.
- return UnknownVal();
-}
+ APSIntType RTy = BVF.getAPSIntType(RT);
+ const auto WR = RTy.getBitWidth();
+ const bool UT = TTy.isUnsigned();
+ const bool UR = RTy.isUnsigned();
-SVal SValBuilder::evalCastSubKind(nonloc::SymbolVal V, QualType CastTy,
- QualType OriginalTy) {
- SymbolRef SE = V.getSymbol();
+ if (((WT > WR) && (UR || !UT)) || ((WT == WR) && (UT == UR)))
+ return VB.makeNonLoc(RootSym, RT, CastTy);
- const bool IsUnknownOriginalType = OriginalTy.isNull();
- // Symbol to bool.
- if (!IsUnknownOriginalType && CastTy->isBooleanType()) {
- // Non-float to bool.
- if (Loc::isLocType(OriginalTy) ||
- OriginalTy->isIntegralOrEnumerationType() ||
- OriginalTy->isMemberPointerType()) {
- BasicValueFactory &BVF = getBasicValueFactory();
- return makeNonLoc(SE, BO_NE, BVF.getValue(0, SE->getType()), CastTy);
- }
- } else {
- // Symbol to integer, float.
- QualType T = Context.getCanonicalType(SE->getType());
- // If types are the same or both are integers, ignore the cast.
- // FIXME: Remove this hack when we support symbolic truncation/extension.
- // HACK: If both castTy and T are integers, ignore the cast. This is
- // not a permanent solution. Eventually we want to precisely handle
- // extension/truncation of symbolic integers. This prevents us from losing
- // precision when we assign 'x = y' and 'y' is symbolic and x and y are
- // different integer types.
- if (haveSameType(T, CastTy))
- return V;
- if (!Loc::isLocType(CastTy))
- if (!IsUnknownOriginalType || !CastTy->isFloatingType() ||
- T->isFloatingType())
- return makeNonLoc(SE, T, CastTy);
+ return VB.makeNonLoc(SE, T, CastTy);
}
+};
+} // end anonymous namespace
- // Symbol to pointer and whatever else.
- return UnknownVal();
-}
-
-SVal SValBuilder::evalCastSubKind(nonloc::PointerToMember V, QualType CastTy,
- QualType OriginalTy) {
- // Member pointer to whatever.
- return V;
+/// Cast a given SVal to another SVal using given QualType's.
+/// \param V -- SVal that should be casted.
+/// \param CastTy -- QualType that V should be casted according to.
+/// \param OriginalTy -- QualType which is associated to V. It provides
+/// additional information about what type the cast performs from.
+/// \returns the most appropriate casted SVal.
+/// Note: Many cases don't use an exact OriginalTy. It can be extracted
+/// from SVal or the cast can performs unconditionaly. Always pass OriginalTy!
+/// It can be crucial in certain cases and generates different results.
+/// FIXME: If `OriginalTy.isNull()` is true, then cast performs based on CastTy
+/// only. This behavior is uncertain and should be improved.
+SVal SValBuilder::evalCast(SVal V, QualType CastTy, QualType OriginalTy) {
+ EvalCastVisitor TRV{*this, CastTy, OriginalTy};
+ return TRV.Visit(V);
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp
index 117546e43b1a..0e1351215bb4 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp
@@ -25,12 +25,12 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
+#include <optional>
using namespace clang;
using namespace ento;
@@ -43,27 +43,8 @@ using namespace ento;
// Utility methods.
//===----------------------------------------------------------------------===//
-bool SVal::hasConjuredSymbol() const {
- if (Optional<nonloc::SymbolVal> SV = getAs<nonloc::SymbolVal>()) {
- SymbolRef sym = SV->getSymbol();
- if (isa<SymbolConjured>(sym))
- return true;
- }
-
- if (Optional<loc::MemRegionVal> RV = getAs<loc::MemRegionVal>()) {
- const MemRegion *R = RV->getRegion();
- if (const auto *SR = dyn_cast<SymbolicRegion>(R)) {
- SymbolRef sym = SR->getSymbol();
- if (isa<SymbolConjured>(sym))
- return true;
- }
- }
-
- return false;
-}
-
const FunctionDecl *SVal::getAsFunctionDecl() const {
- if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) {
+ if (std::optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) {
const MemRegion* R = X->getRegion();
if (const FunctionCodeRegion *CTR = R->getAs<FunctionCodeRegion>())
if (const auto *FD = dyn_cast<FunctionDecl>(CTR->getDecl()))
@@ -97,7 +78,7 @@ SymbolRef SVal::getAsLocSymbol(bool IncludeBaseRegions) const {
/// Get the symbol in the SVal or its base region.
SymbolRef SVal::getLocSymbolInBase() const {
- Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>();
+ std::optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>();
if (!X)
return nullptr;
@@ -122,17 +103,25 @@ SymbolRef SVal::getLocSymbolInBase() const {
/// should continue to the base regions if the region is not symbolic.
SymbolRef SVal::getAsSymbol(bool IncludeBaseRegions) const {
// FIXME: should we consider SymbolRef wrapped in CodeTextRegion?
- if (Optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>())
+ if (std::optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>())
return X->getSymbol();
return getAsLocSymbol(IncludeBaseRegions);
}
+const llvm::APSInt *SVal::getAsInteger() const {
+ if (auto CI = getAs<nonloc::ConcreteInt>())
+ return &CI->getValue();
+ if (auto CI = getAs<loc::ConcreteInt>())
+ return &CI->getValue();
+ return nullptr;
+}
+
const MemRegion *SVal::getAsRegion() const {
- if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>())
+ if (std::optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>())
return X->getRegion();
- if (Optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>())
+ if (std::optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>())
return X->getLoc().getAsRegion();
return nullptr;
@@ -147,23 +136,19 @@ private:
public:
TypeRetrievingVisitor(const ASTContext &Context) : Context(Context) {}
- QualType VisitLocMemRegionVal(loc::MemRegionVal MRV) {
+ QualType VisitMemRegionVal(loc::MemRegionVal MRV) {
return Visit(MRV.getRegion());
}
- QualType VisitLocGotoLabel(loc::GotoLabel GL) {
+ QualType VisitGotoLabel(loc::GotoLabel GL) {
return QualType{Context.VoidPtrTy};
}
template <class ConcreteInt> QualType VisitConcreteInt(ConcreteInt CI) {
const llvm::APSInt &Value = CI.getValue();
+ if (1 == Value.getBitWidth())
+ return Context.BoolTy;
return Context.getIntTypeForBitwidth(Value.getBitWidth(), Value.isSigned());
}
- QualType VisitLocConcreteInt(loc::ConcreteInt CI) {
- return VisitConcreteInt(CI);
- }
- QualType VisitNonLocConcreteInt(nonloc::ConcreteInt CI) {
- return VisitConcreteInt(CI);
- }
- QualType VisitNonLocLocAsInteger(nonloc::LocAsInteger LI) {
+ QualType VisitLocAsInteger(nonloc::LocAsInteger LI) {
QualType NestedType = Visit(LI.getLoc());
if (NestedType.isNull())
return NestedType;
@@ -171,18 +156,21 @@ public:
return Context.getIntTypeForBitwidth(LI.getNumBits(),
NestedType->isSignedIntegerType());
}
- QualType VisitNonLocCompoundVal(nonloc::CompoundVal CV) {
+ QualType VisitCompoundVal(nonloc::CompoundVal CV) {
return CV.getValue()->getType();
}
- QualType VisitNonLocLazyCompoundVal(nonloc::LazyCompoundVal LCV) {
+ QualType VisitLazyCompoundVal(nonloc::LazyCompoundVal LCV) {
return LCV.getRegion()->getValueType();
}
- QualType VisitNonLocSymbolVal(nonloc::SymbolVal SV) {
+ QualType VisitSymbolVal(nonloc::SymbolVal SV) {
return Visit(SV.getSymbol());
}
QualType VisitSymbolicRegion(const SymbolicRegion *SR) {
return Visit(SR->getSymbol());
}
+ QualType VisitAllocaRegion(const AllocaRegion *) {
+ return QualType{Context.VoidPtrTy};
+ }
QualType VisitTypedRegion(const TypedRegion *TR) {
return TR->getLocationType();
}
@@ -196,8 +184,7 @@ QualType SVal::getType(const ASTContext &Context) const {
}
const MemRegion *loc::MemRegionVal::stripCasts(bool StripBaseCasts) const {
- const MemRegion *R = getRegion();
- return R ? R->StripCasts(StripBaseCasts) : nullptr;
+ return getRegion()->StripCasts(StripBaseCasts);
}
const void *nonloc::LazyCompoundVal::getStore() const {
@@ -261,9 +248,9 @@ bool SVal::isConstant() const {
}
bool SVal::isConstant(int I) const {
- if (Optional<loc::ConcreteInt> LV = getAs<loc::ConcreteInt>())
+ if (std::optional<loc::ConcreteInt> LV = getAs<loc::ConcreteInt>())
return LV->getValue() == I;
- if (Optional<nonloc::ConcreteInt> NV = getAs<nonloc::ConcreteInt>())
+ if (std::optional<nonloc::ConcreteInt> NV = getAs<nonloc::ConcreteInt>())
return NV->getValue() == I;
return false;
}
@@ -273,49 +260,6 @@ bool SVal::isZeroConstant() const {
}
//===----------------------------------------------------------------------===//
-// Transfer function dispatch for Non-Locs.
-//===----------------------------------------------------------------------===//
-
-SVal nonloc::ConcreteInt::evalBinOp(SValBuilder &svalBuilder,
- BinaryOperator::Opcode Op,
- const nonloc::ConcreteInt& R) const {
- const llvm::APSInt* X =
- svalBuilder.getBasicValueFactory().evalAPSInt(Op, getValue(), R.getValue());
-
- if (X)
- return nonloc::ConcreteInt(*X);
- else
- return UndefinedVal();
-}
-
-nonloc::ConcreteInt
-nonloc::ConcreteInt::evalComplement(SValBuilder &svalBuilder) const {
- return svalBuilder.makeIntVal(~getValue());
-}
-
-nonloc::ConcreteInt
-nonloc::ConcreteInt::evalMinus(SValBuilder &svalBuilder) const {
- return svalBuilder.makeIntVal(-getValue());
-}
-
-//===----------------------------------------------------------------------===//
-// Transfer function dispatch for Locs.
-//===----------------------------------------------------------------------===//
-
-SVal loc::ConcreteInt::evalBinOp(BasicValueFactory& BasicVals,
- BinaryOperator::Opcode Op,
- const loc::ConcreteInt& R) const {
- assert(BinaryOperator::isComparisonOp(Op) || Op == BO_Sub);
-
- const llvm::APSInt *X = BasicVals.evalAPSInt(Op, getValue(), R.getValue());
-
- if (X)
- return nonloc::ConcreteInt(*X);
- else
- return UndefinedVal();
-}
-
-//===----------------------------------------------------------------------===//
// Pretty-Printing.
//===----------------------------------------------------------------------===//
@@ -331,30 +275,33 @@ void SVal::printJson(raw_ostream &Out, bool AddQuotes) const {
}
void SVal::dumpToStream(raw_ostream &os) const {
- switch (getBaseKind()) {
- case UnknownValKind:
- os << "Unknown";
- break;
- case NonLocKind:
- castAs<NonLoc>().dumpToStream(os);
- break;
- case LocKind:
- castAs<Loc>().dumpToStream(os);
- break;
- case UndefinedValKind:
- os << "Undefined";
- break;
+ if (isUndef()) {
+ os << "Undefined";
+ return;
}
+ if (isUnknown()) {
+ os << "Unknown";
+ return;
+ }
+ if (NonLoc::classof(*this)) {
+ castAs<NonLoc>().dumpToStream(os);
+ return;
+ }
+ if (Loc::classof(*this)) {
+ castAs<Loc>().dumpToStream(os);
+ return;
+ }
+ llvm_unreachable("Unhandled SVal kind!");
}
void NonLoc::dumpToStream(raw_ostream &os) const {
- switch (getSubKind()) {
- case nonloc::ConcreteIntKind: {
- const auto &Value = castAs<nonloc::ConcreteInt>().getValue();
- os << Value << ' ' << (Value.isSigned() ? 'S' : 'U')
- << Value.getBitWidth() << 'b';
- break;
- }
+ switch (getKind()) {
+ case nonloc::ConcreteIntKind: {
+ const auto &Value = castAs<nonloc::ConcreteInt>().getValue();
+ os << Value << ' ' << (Value.isSigned() ? 'S' : 'U') << Value.getBitWidth()
+ << 'b';
+ break;
+ }
case nonloc::SymbolValKind:
os << castAs<nonloc::SymbolVal>().getSymbol();
break;
@@ -401,7 +348,7 @@ void NonLoc::dumpToStream(raw_ostream &os) const {
else
os << ", ";
- os << (*I).getType().getAsString();
+ os << I->getType();
}
os << '}';
@@ -410,21 +357,21 @@ void NonLoc::dumpToStream(raw_ostream &os) const {
default:
assert(false && "Pretty-printed not implemented for this NonLoc.");
break;
- }
+ }
}
void Loc::dumpToStream(raw_ostream &os) const {
- switch (getSubKind()) {
- case loc::ConcreteIntKind:
- os << castAs<loc::ConcreteInt>().getValue().getZExtValue() << " (Loc)";
- break;
- case loc::GotoLabelKind:
- os << "&&" << castAs<loc::GotoLabel>().getLabel()->getName();
- break;
- case loc::MemRegionValKind:
- os << '&' << castAs<loc::MemRegionVal>().getRegion()->getString();
- break;
- default:
- llvm_unreachable("Pretty-printing not implemented for this Loc.");
+ switch (getKind()) {
+ case loc::ConcreteIntKind:
+ os << castAs<loc::ConcreteInt>().getValue().getZExtValue() << " (Loc)";
+ break;
+ case loc::GotoLabelKind:
+ os << "&&" << castAs<loc::GotoLabel>().getLabel()->getName();
+ break;
+ case loc::MemRegionValKind:
+ os << '&' << castAs<loc::MemRegionVal>().getRegion()->getString();
+ break;
+ default:
+ llvm_unreachable("Pretty-printing not implemented for this Loc.");
}
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
index e1319a4c2e41..fab520098f13 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
@@ -13,6 +13,8 @@
#include "clang/Analysis/MacroExpansionContext.h"
#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Basic/FileManager.h"
+#include "clang/Basic/Sarif.h"
+#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Version.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
@@ -30,10 +32,12 @@ namespace {
class SarifDiagnostics : public PathDiagnosticConsumer {
std::string OutputFile;
const LangOptions &LO;
+ SarifDocumentWriter SarifWriter;
public:
- SarifDiagnostics(const std::string &Output, const LangOptions &LO)
- : OutputFile(Output), LO(LO) {}
+ SarifDiagnostics(const std::string &Output, const LangOptions &LO,
+ const SourceManager &SM)
+ : OutputFile(Output), LO(LO), SarifWriter(SM) {}
~SarifDiagnostics() override = default;
void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
@@ -56,250 +60,12 @@ void ento::createSarifDiagnosticConsumer(
if (Output.empty())
return;
- C.push_back(new SarifDiagnostics(Output, PP.getLangOpts()));
+ C.push_back(
+ new SarifDiagnostics(Output, PP.getLangOpts(), PP.getSourceManager()));
createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, Output, PP,
CTU, MacroExpansions);
}
-static StringRef getFileName(const FileEntry &FE) {
- StringRef Filename = FE.tryGetRealPathName();
- if (Filename.empty())
- Filename = FE.getName();
- return Filename;
-}
-
-static std::string percentEncodeURICharacter(char C) {
- // RFC 3986 claims alpha, numeric, and this handful of
- // characters are not reserved for the path component and
- // should be written out directly. Otherwise, percent
- // encode the character and write that out instead of the
- // reserved character.
- if (llvm::isAlnum(C) ||
- StringRef::npos != StringRef("-._~:@!$&'()*+,;=").find(C))
- return std::string(&C, 1);
- return "%" + llvm::toHex(StringRef(&C, 1));
-}
-
-static std::string fileNameToURI(StringRef Filename) {
- llvm::SmallString<32> Ret = StringRef("file://");
-
- // Get the root name to see if it has a URI authority.
- StringRef Root = sys::path::root_name(Filename);
- if (Root.startswith("//")) {
- // There is an authority, so add it to the URI.
- Ret += Root.drop_front(2).str();
- } else if (!Root.empty()) {
- // There is no authority, so end the component and add the root to the URI.
- Ret += Twine("/" + Root).str();
- }
-
- auto Iter = sys::path::begin(Filename), End = sys::path::end(Filename);
- assert(Iter != End && "Expected there to be a non-root path component.");
- // Add the rest of the path components, encoding any reserved characters;
- // we skip past the first path component, as it was handled it above.
- std::for_each(++Iter, End, [&Ret](StringRef Component) {
- // For reasons unknown to me, we may get a backslash with Windows native
- // paths for the initial backslash following the drive component, which
- // we need to ignore as a URI path part.
- if (Component == "\\")
- return;
-
- // Add the separator between the previous path part and the one being
- // currently processed.
- Ret += "/";
-
- // URI encode the part.
- for (char C : Component) {
- Ret += percentEncodeURICharacter(C);
- }
- });
-
- return std::string(Ret);
-}
-
-static json::Object createArtifactLocation(const FileEntry &FE) {
- return json::Object{{"uri", fileNameToURI(getFileName(FE))}};
-}
-
-static json::Object createArtifact(const FileEntry &FE) {
- return json::Object{{"location", createArtifactLocation(FE)},
- {"roles", json::Array{"resultFile"}},
- {"length", FE.getSize()},
- {"mimeType", "text/plain"}};
-}
-
-static json::Object createArtifactLocation(const FileEntry &FE,
- json::Array &Artifacts) {
- std::string FileURI = fileNameToURI(getFileName(FE));
-
- // See if the Artifacts array contains this URI already. If it does not,
- // create a new artifact object to add to the array.
- auto I = llvm::find_if(Artifacts, [&](const json::Value &File) {
- if (const json::Object *Obj = File.getAsObject()) {
- if (const json::Object *FileLoc = Obj->getObject("location")) {
- Optional<StringRef> URI = FileLoc->getString("uri");
- return URI && URI->equals(FileURI);
- }
- }
- return false;
- });
-
- // Calculate the index within the artifact array so it can be stored in
- // the JSON object.
- auto Index = static_cast<unsigned>(std::distance(Artifacts.begin(), I));
- if (I == Artifacts.end())
- Artifacts.push_back(createArtifact(FE));
-
- return json::Object{{"uri", FileURI}, {"index", Index}};
-}
-
-static unsigned int adjustColumnPos(const SourceManager &SM, SourceLocation Loc,
- unsigned int TokenLen = 0) {
- assert(!Loc.isInvalid() && "invalid Loc when adjusting column position");
-
- std::pair<FileID, unsigned> LocInfo = SM.getDecomposedExpansionLoc(Loc);
- assert(LocInfo.second > SM.getExpansionColumnNumber(Loc) &&
- "position in file is before column number?");
-
- Optional<MemoryBufferRef> Buf = SM.getBufferOrNone(LocInfo.first);
- assert(Buf && "got an invalid buffer for the location's file");
- assert(Buf->getBufferSize() >= (LocInfo.second + TokenLen) &&
- "token extends past end of buffer?");
-
- // Adjust the offset to be the start of the line, since we'll be counting
- // Unicode characters from there until our column offset.
- unsigned int Off = LocInfo.second - (SM.getExpansionColumnNumber(Loc) - 1);
- unsigned int Ret = 1;
- while (Off < (LocInfo.second + TokenLen)) {
- Off += getNumBytesForUTF8(Buf->getBuffer()[Off]);
- Ret++;
- }
-
- return Ret;
-}
-
-static json::Object createTextRegion(const LangOptions &LO, SourceRange R,
- const SourceManager &SM) {
- json::Object Region{
- {"startLine", SM.getExpansionLineNumber(R.getBegin())},
- {"startColumn", adjustColumnPos(SM, R.getBegin())},
- };
- if (R.getBegin() == R.getEnd()) {
- Region["endColumn"] = adjustColumnPos(SM, R.getBegin());
- } else {
- Region["endLine"] = SM.getExpansionLineNumber(R.getEnd());
- Region["endColumn"] = adjustColumnPos(
- SM, R.getEnd(),
- Lexer::MeasureTokenLength(R.getEnd(), SM, LO));
- }
- return Region;
-}
-
-static json::Object createPhysicalLocation(const LangOptions &LO,
- SourceRange R, const FileEntry &FE,
- const SourceManager &SMgr,
- json::Array &Artifacts) {
- return json::Object{
- {{"artifactLocation", createArtifactLocation(FE, Artifacts)},
- {"region", createTextRegion(LO, R, SMgr)}}};
-}
-
-enum class Importance { Important, Essential, Unimportant };
-
-static StringRef importanceToStr(Importance I) {
- switch (I) {
- case Importance::Important:
- return "important";
- case Importance::Essential:
- return "essential";
- case Importance::Unimportant:
- return "unimportant";
- }
- llvm_unreachable("Fully covered switch is not so fully covered");
-}
-
-static json::Object createThreadFlowLocation(json::Object &&Location,
- Importance I) {
- return json::Object{{"location", std::move(Location)},
- {"importance", importanceToStr(I)}};
-}
-
-static json::Object createMessage(StringRef Text) {
- return json::Object{{"text", Text.str()}};
-}
-
-static json::Object createLocation(json::Object &&PhysicalLocation,
- StringRef Message = "") {
- json::Object Ret{{"physicalLocation", std::move(PhysicalLocation)}};
- if (!Message.empty())
- Ret.insert({"message", createMessage(Message)});
- return Ret;
-}
-
-static Importance calculateImportance(const PathDiagnosticPiece &Piece) {
- switch (Piece.getKind()) {
- case PathDiagnosticPiece::Call:
- case PathDiagnosticPiece::Macro:
- case PathDiagnosticPiece::Note:
- case PathDiagnosticPiece::PopUp:
- // FIXME: What should be reported here?
- break;
- case PathDiagnosticPiece::Event:
- return Piece.getTagStr() == "ConditionBRVisitor" ? Importance::Important
- : Importance::Essential;
- case PathDiagnosticPiece::ControlFlow:
- return Importance::Unimportant;
- }
- return Importance::Unimportant;
-}
-
-static json::Object createThreadFlow(const LangOptions &LO,
- const PathPieces &Pieces,
- json::Array &Artifacts) {
- const SourceManager &SMgr = Pieces.front()->getLocation().getManager();
- json::Array Locations;
- for (const auto &Piece : Pieces) {
- const PathDiagnosticLocation &P = Piece->getLocation();
- Locations.push_back(createThreadFlowLocation(
- createLocation(createPhysicalLocation(
- LO, P.asRange(),
- *P.asLocation().getExpansionLoc().getFileEntry(),
- SMgr, Artifacts),
- Piece->getString()),
- calculateImportance(*Piece)));
- }
- return json::Object{{"locations", std::move(Locations)}};
-}
-
-static json::Object createCodeFlow(const LangOptions &LO,
- const PathPieces &Pieces,
- json::Array &Artifacts) {
- return json::Object{
- {"threadFlows", json::Array{createThreadFlow(LO, Pieces, Artifacts)}}};
-}
-
-static json::Object createResult(const LangOptions &LO,
- const PathDiagnostic &Diag,
- json::Array &Artifacts,
- const StringMap<unsigned> &RuleMapping) {
- const PathPieces &Path = Diag.path.flatten(false);
- const SourceManager &SMgr = Path.front()->getLocation().getManager();
-
- auto Iter = RuleMapping.find(Diag.getCheckerName());
- assert(Iter != RuleMapping.end() && "Rule ID is not in the array index map?");
-
- return json::Object{
- {"message", createMessage(Diag.getVerboseDescription())},
- {"codeFlows", json::Array{createCodeFlow(LO, Path, Artifacts)}},
- {"locations",
- json::Array{createLocation(createPhysicalLocation(
- LO, Diag.getLocation().asRange(),
- *Diag.getLocation().asLocation().getExpansionLoc().getFileEntry(),
- SMgr, Artifacts))}},
- {"ruleIndex", Iter->getValue()},
- {"ruleId", Diag.getCheckerName()}};
-}
-
static StringRef getRuleDescription(StringRef CheckName) {
return llvm::StringSwitch<StringRef>(CheckName)
#define GET_CHECKERS
@@ -322,61 +88,99 @@ static StringRef getRuleHelpURIStr(StringRef CheckName) {
;
}
-static json::Object createRule(const PathDiagnostic &Diag) {
- StringRef CheckName = Diag.getCheckerName();
- json::Object Ret{
- {"fullDescription", createMessage(getRuleDescription(CheckName))},
- {"name", CheckName},
- {"id", CheckName}};
-
- std::string RuleURI = std::string(getRuleHelpURIStr(CheckName));
- if (!RuleURI.empty())
- Ret["helpUri"] = RuleURI;
-
- return Ret;
+static ThreadFlowImportance
+calculateImportance(const PathDiagnosticPiece &Piece) {
+ switch (Piece.getKind()) {
+ case PathDiagnosticPiece::Call:
+ case PathDiagnosticPiece::Macro:
+ case PathDiagnosticPiece::Note:
+ case PathDiagnosticPiece::PopUp:
+ // FIXME: What should be reported here?
+ break;
+ case PathDiagnosticPiece::Event:
+ return Piece.getTagStr() == "ConditionBRVisitor"
+ ? ThreadFlowImportance::Important
+ : ThreadFlowImportance::Essential;
+ case PathDiagnosticPiece::ControlFlow:
+ return ThreadFlowImportance::Unimportant;
+ }
+ return ThreadFlowImportance::Unimportant;
+}
+
+/// Accepts a SourceRange corresponding to a pair of the first and last tokens
+/// and converts to a Character granular CharSourceRange.
+static CharSourceRange convertTokenRangeToCharRange(const SourceRange &R,
+ const SourceManager &SM,
+ const LangOptions &LO) {
+ // Caret diagnostics have the first and last locations pointed at the same
+ // location, return these as-is.
+ if (R.getBegin() == R.getEnd())
+ return CharSourceRange::getCharRange(R);
+
+ SourceLocation BeginCharLoc = R.getBegin();
+ // For token ranges, the raw end SLoc points at the first character of the
+ // last token in the range. This must be moved to one past the end of the
+ // last character using the lexer.
+ SourceLocation EndCharLoc =
+ Lexer::getLocForEndOfToken(R.getEnd(), /* Offset = */ 0, SM, LO);
+ return CharSourceRange::getCharRange(BeginCharLoc, EndCharLoc);
+}
+
+static SmallVector<ThreadFlow, 8> createThreadFlows(const PathDiagnostic *Diag,
+ const LangOptions &LO) {
+ SmallVector<ThreadFlow, 8> Flows;
+ const PathPieces &Pieces = Diag->path.flatten(false);
+ for (const auto &Piece : Pieces) {
+ auto Range = convertTokenRangeToCharRange(
+ Piece->getLocation().asRange(), Piece->getLocation().getManager(), LO);
+ auto Flow = ThreadFlow::create()
+ .setImportance(calculateImportance(*Piece))
+ .setRange(Range)
+ .setMessage(Piece->getString());
+ Flows.push_back(Flow);
+ }
+ return Flows;
}
-static json::Array createRules(std::vector<const PathDiagnostic *> &Diags,
- StringMap<unsigned> &RuleMapping) {
- json::Array Rules;
+static StringMap<uint32_t>
+createRuleMapping(const std::vector<const PathDiagnostic *> &Diags,
+ SarifDocumentWriter &SarifWriter) {
+ StringMap<uint32_t> RuleMapping;
llvm::StringSet<> Seen;
- llvm::for_each(Diags, [&](const PathDiagnostic *D) {
- StringRef RuleID = D->getCheckerName();
- std::pair<llvm::StringSet<>::iterator, bool> P = Seen.insert(RuleID);
+ for (const PathDiagnostic *D : Diags) {
+ StringRef CheckName = D->getCheckerName();
+ std::pair<llvm::StringSet<>::iterator, bool> P = Seen.insert(CheckName);
if (P.second) {
- RuleMapping[RuleID] = Rules.size(); // Maps RuleID to an Array Index.
- Rules.push_back(createRule(*D));
+ auto Rule = SarifRule::create()
+ .setName(CheckName)
+ .setRuleId(CheckName)
+ .setDescription(getRuleDescription(CheckName))
+ .setHelpURI(getRuleHelpURIStr(CheckName));
+ size_t RuleIdx = SarifWriter.createRule(Rule);
+ RuleMapping[CheckName] = RuleIdx;
}
- });
-
- return Rules;
+ }
+ return RuleMapping;
}
-static json::Object createTool(std::vector<const PathDiagnostic *> &Diags,
- StringMap<unsigned> &RuleMapping) {
- return json::Object{
- {"driver", json::Object{{"name", "clang"},
- {"fullName", "clang static analyzer"},
- {"language", "en-US"},
- {"version", getClangFullVersion()},
- {"rules", createRules(Diags, RuleMapping)}}}};
-}
+static SarifResult createResult(const PathDiagnostic *Diag,
+ const StringMap<uint32_t> &RuleMapping,
+ const LangOptions &LO) {
-static json::Object createRun(const LangOptions &LO,
- std::vector<const PathDiagnostic *> &Diags) {
- json::Array Results, Artifacts;
- StringMap<unsigned> RuleMapping;
- json::Object Tool = createTool(Diags, RuleMapping);
-
- llvm::for_each(Diags, [&](const PathDiagnostic *D) {
- Results.push_back(createResult(LO, *D, Artifacts, RuleMapping));
- });
+ StringRef CheckName = Diag->getCheckerName();
+ uint32_t RuleIdx = RuleMapping.lookup(CheckName);
+ auto Range = convertTokenRangeToCharRange(
+ Diag->getLocation().asRange(), Diag->getLocation().getManager(), LO);
- return json::Object{{"tool", std::move(Tool)},
- {"results", std::move(Results)},
- {"artifacts", std::move(Artifacts)},
- {"columnKind", "unicodeCodePoints"}};
+ SmallVector<ThreadFlow, 8> Flows = createThreadFlows(Diag, LO);
+ auto Result = SarifResult::create(RuleIdx)
+ .setRuleId(CheckName)
+ .setDiagnosticMessage(Diag->getVerboseDescription())
+ .setDiagnosticLevel(SarifResultLevel::Warning)
+ .setLocations({Range})
+ .setThreadFlows(Flows);
+ return Result;
}
void SarifDiagnostics::FlushDiagnosticsImpl(
@@ -392,10 +196,14 @@ void SarifDiagnostics::FlushDiagnosticsImpl(
llvm::errs() << "warning: could not create file: " << EC.message() << '\n';
return;
}
- json::Object Sarif{
- {"$schema",
- "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"},
- {"version", "2.1.0"},
- {"runs", json::Array{createRun(LO, Diags)}}};
- OS << llvm::formatv("{0:2}\n", json::Value(std::move(Sarif)));
+
+ std::string ToolVersion = getClangFullVersion();
+ SarifWriter.createRun("clang", "clang static analyzer", ToolVersion);
+ StringMap<uint32_t> RuleMapping = createRuleMapping(Diags, SarifWriter);
+ for (const PathDiagnostic *D : Diags) {
+ SarifResult Result = createResult(D, RuleMapping, LO);
+ SarifWriter.appendResult(Result);
+ }
+ auto Document = SarifWriter.createDocument();
+ OS << llvm::formatv("{0:2}\n", json::Value(std::move(Document)));
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
index f96974f97dcc..8ca2cdb9d3ab 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
@@ -15,6 +15,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include <optional>
namespace clang {
@@ -22,11 +23,11 @@ namespace ento {
SimpleConstraintManager::~SimpleConstraintManager() {}
-ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State,
- DefinedSVal Cond,
- bool Assumption) {
+ProgramStateRef SimpleConstraintManager::assumeInternal(ProgramStateRef State,
+ DefinedSVal Cond,
+ bool Assumption) {
// If we have a Loc value, cast it to a bool NonLoc first.
- if (Optional<Loc> LV = Cond.getAs<Loc>()) {
+ if (std::optional<Loc> LV = Cond.getAs<Loc>()) {
SValBuilder &SVB = State->getStateManager().getSValBuilder();
QualType T;
const MemRegion *MR = LV->getAsRegion();
@@ -44,7 +45,7 @@ ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State,
ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State,
NonLoc Cond, bool Assumption) {
State = assumeAux(State, Cond, Assumption);
- if (NotifyAssumeClients && EE)
+ if (EE)
return EE->processAssume(State, Cond, Assumption);
return State;
}
@@ -62,7 +63,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State,
return assumeSymUnsupported(State, Sym, Assumption);
}
- switch (Cond.getSubKind()) {
+ switch (Cond.getKind()) {
default:
llvm_unreachable("'Assume' not implemented for this NonLoc");
@@ -86,12 +87,12 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State,
}
case nonloc::LocAsIntegerKind:
- return assume(State, Cond.castAs<nonloc::LocAsInteger>().getLoc(),
- Assumption);
+ return assumeInternal(State, Cond.castAs<nonloc::LocAsInteger>().getLoc(),
+ Assumption);
} // end switch
}
-ProgramStateRef SimpleConstraintManager::assumeInclusiveRange(
+ProgramStateRef SimpleConstraintManager::assumeInclusiveRangeInternal(
ProgramStateRef State, NonLoc Value, const llvm::APSInt &From,
const llvm::APSInt &To, bool InRange) {
@@ -106,7 +107,7 @@ ProgramStateRef SimpleConstraintManager::assumeInclusiveRange(
return assumeSymInclusiveRange(State, Sym, From, To, InRange);
}
- switch (Value.getSubKind()) {
+ switch (Value.getKind()) {
default:
llvm_unreachable("'assumeInclusiveRange' is not implemented"
"for this NonLoc");
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index e57d92fbcebb..45e48d435aca 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -11,25 +11,63 @@
//===----------------------------------------------------------------------===//
#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/SValVisitor.h"
+#include <optional>
using namespace clang;
using namespace ento;
namespace {
class SimpleSValBuilder : public SValBuilder {
+
+ // Query the constraint manager whether the SVal has only one possible
+ // (integer) value. If that is the case, the value is returned. Otherwise,
+ // returns NULL.
+ // This is an implementation detail. Checkers should use `getKnownValue()`
+ // instead.
+ static const llvm::APSInt *getConstValue(ProgramStateRef state, SVal V);
+
+ // Helper function that returns the value stored in a nonloc::ConcreteInt or
+ // loc::ConcreteInt.
+ static const llvm::APSInt *getConcreteValue(SVal V);
+
+ // With one `simplifySValOnce` call, a compound symbols might collapse to
+ // simpler symbol tree that is still possible to further simplify. Thus, we
+ // do the simplification on a new symbol tree until we reach the simplest
+ // form, i.e. the fixpoint.
+ // Consider the following symbol `(b * b) * b * b` which has this tree:
+ // *
+ // / \
+ // * b
+ // / \
+ // / b
+ // (b * b)
+ // Now, if the `b * b == 1` new constraint is added then during the first
+ // iteration we have the following transformations:
+ // * *
+ // / \ / \
+ // * b --> b b
+ // / \
+ // / b
+ // 1
+ // We need another iteration to reach the final result `1`.
+ SVal simplifyUntilFixpoint(ProgramStateRef State, SVal Val);
+
+ // Recursively descends into symbolic expressions and replaces symbols
+ // with their known values (in the sense of the getConstValue() method).
+ // We traverse the symbol tree and query the constraint values for the
+ // sub-trees and if a value is a constant we do the constant folding.
+ SVal simplifySValOnce(ProgramStateRef State, SVal V);
+
public:
SimpleSValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context,
ProgramStateManager &stateMgr)
- : SValBuilder(alloc, context, stateMgr) {}
+ : SValBuilder(alloc, context, stateMgr) {}
~SimpleSValBuilder() override {}
- SVal evalMinus(NonLoc val) override;
- SVal evalComplement(NonLoc val) override;
SVal evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op,
NonLoc lhs, NonLoc rhs, QualType resultTy) override;
SVal evalBinOpLL(ProgramStateRef state, BinaryOperator::Opcode op,
@@ -37,12 +75,21 @@ public:
SVal evalBinOpLN(ProgramStateRef state, BinaryOperator::Opcode op,
Loc lhs, NonLoc rhs, QualType resultTy) override;
- /// getKnownValue - evaluates a given SVal. If the SVal has only one possible
- /// (integer) value, that value is returned. Otherwise, returns NULL.
+ /// Evaluates a given SVal by recursively evaluating and
+ /// simplifying the children SVals. If the SVal has only one possible
+ /// (integer) value, that value is returned. Otherwise, returns NULL.
const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal V) override;
- /// Recursively descends into symbolic expressions and replaces symbols
- /// with their known values (in the sense of the getKnownValue() method).
+ /// Evaluates a given SVal by recursively evaluating and simplifying the
+ /// children SVals, then returns its minimal possible (integer) value. If the
+ /// constraint manager cannot provide a meaningful answer, this returns NULL.
+ const llvm::APSInt *getMinValue(ProgramStateRef state, SVal V) override;
+
+ /// Evaluates a given SVal by recursively evaluating and simplifying the
+ /// children SVals, then returns its maximal possible (integer) value. If the
+ /// constraint manager cannot provide a meaningful answer, this returns NULL.
+ const llvm::APSInt *getMaxValue(ProgramStateRef state, SVal V) override;
+
SVal simplifySVal(ProgramStateRef State, SVal V) override;
SVal MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op,
@@ -56,26 +103,21 @@ SValBuilder *ento::createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc,
return new SimpleSValBuilder(alloc, context, stateMgr);
}
-//===----------------------------------------------------------------------===//
-// Transfer function for unary operators.
-//===----------------------------------------------------------------------===//
-
-SVal SimpleSValBuilder::evalMinus(NonLoc val) {
- switch (val.getSubKind()) {
- case nonloc::ConcreteIntKind:
- return val.castAs<nonloc::ConcreteInt>().evalMinus(*this);
- default:
- return UnknownVal();
+// Checks if the negation the value and flipping sign preserve
+// the semantics on the operation in the resultType
+static bool isNegationValuePreserving(const llvm::APSInt &Value,
+ APSIntType ResultType) {
+ const unsigned ValueBits = Value.getSignificantBits();
+ if (ValueBits == ResultType.getBitWidth()) {
+ // The value is the lowest negative value that is representable
+ // in signed integer with bitWith of result type. The
+ // negation is representable if resultType is unsigned.
+ return ResultType.isUnsigned();
}
-}
-SVal SimpleSValBuilder::evalComplement(NonLoc X) {
- switch (X.getSubKind()) {
- case nonloc::ConcreteIntKind:
- return X.castAs<nonloc::ConcreteInt>().evalComplement(*this);
- default:
- return UnknownVal();
- }
+ // If resultType bitWith is higher that number of bits required
+ // to represent RHS, the sign flip produce same value.
+ return ValueBits < ResultType.getBitWidth();
}
//===----------------------------------------------------------------------===//
@@ -129,14 +171,14 @@ SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS,
// a&0 and a&(~0)
if (RHS == 0)
return makeIntVal(0, resultTy);
- else if (RHS.isAllOnesValue())
+ else if (RHS.isAllOnes())
isIdempotent = true;
break;
case BO_Or:
// a|0 and a|(~0)
if (RHS == 0)
isIdempotent = true;
- else if (RHS.isAllOnesValue()) {
+ else if (RHS.isAllOnes()) {
const llvm::APSInt &Result = BasicVals.Convert(resultTy, RHS);
return nonloc::ConcreteInt(Result);
}
@@ -171,6 +213,17 @@ SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS,
if (RHS.isSigned() && !SymbolType->isSignedIntegerOrEnumerationType())
ConvertedRHS = &BasicVals.Convert(SymbolType, RHS);
}
+ } else if (BinaryOperator::isAdditiveOp(op) && RHS.isNegative()) {
+ // Change a+(-N) into a-N, and a-(-N) into a+N
+ // Adjust addition/subtraction of negative value, to
+ // subtraction/addition of the negated value.
+ APSIntType resultIntTy = BasicVals.getAPSIntType(resultTy);
+ if (isNegationValuePreserving(RHS, resultIntTy)) {
+ ConvertedRHS = &BasicVals.getValue(-resultIntTy.convert(RHS));
+ op = (op == BO_Add) ? BO_Sub : BO_Add;
+ } else {
+ ConvertedRHS = &BasicVals.Convert(resultTy, RHS);
+ }
} else
ConvertedRHS = &BasicVals.Convert(resultTy, RHS);
@@ -260,7 +313,6 @@ static NonLoc doRearrangeUnchecked(ProgramStateRef State,
else
llvm_unreachable("Operation not suitable for unchecked rearrangement!");
- // FIXME: Can we use assume() without getting into an infinite recursion?
if (LSym == RSym)
return SVB.evalBinOpNN(State, Op, nonloc::ConcreteInt(LInt),
nonloc::ConcreteInt(RInt), ResultTy)
@@ -311,51 +363,48 @@ static bool shouldRearrange(ProgramStateRef State, BinaryOperator::Opcode Op,
isWithinConstantOverflowBounds(Int)));
}
-static Optional<NonLoc> tryRearrange(ProgramStateRef State,
- BinaryOperator::Opcode Op, NonLoc Lhs,
- NonLoc Rhs, QualType ResultTy) {
+static std::optional<NonLoc> tryRearrange(ProgramStateRef State,
+ BinaryOperator::Opcode Op, NonLoc Lhs,
+ NonLoc Rhs, QualType ResultTy) {
ProgramStateManager &StateMgr = State->getStateManager();
SValBuilder &SVB = StateMgr.getSValBuilder();
// We expect everything to be of the same type - this type.
QualType SingleTy;
- auto &Opts =
- StateMgr.getOwningEngine().getAnalysisManager().getAnalyzerOptions();
-
// FIXME: After putting complexity threshold to the symbols we can always
// rearrange additive operations but rearrange comparisons only if
// option is set.
- if(!Opts.ShouldAggressivelySimplifyBinaryOperation)
- return None;
+ if (!SVB.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation)
+ return std::nullopt;
SymbolRef LSym = Lhs.getAsSymbol();
if (!LSym)
- return None;
+ return std::nullopt;
if (BinaryOperator::isComparisonOp(Op)) {
SingleTy = LSym->getType();
if (ResultTy != SVB.getConditionType())
- return None;
+ return std::nullopt;
// Initialize SingleTy later with a symbol's type.
} else if (BinaryOperator::isAdditiveOp(Op)) {
SingleTy = ResultTy;
if (LSym->getType() != SingleTy)
- return None;
+ return std::nullopt;
} else {
// Don't rearrange other operations.
- return None;
+ return std::nullopt;
}
assert(!SingleTy.isNull() && "We should have figured out the type by now!");
// Rearrange signed symbolic expressions only
if (!SingleTy->isSignedIntegerOrEnumerationType())
- return None;
+ return std::nullopt;
SymbolRef RSym = Rhs.getAsSymbol();
if (!RSym || RSym->getType() != SingleTy)
- return None;
+ return std::nullopt;
BasicValueFactory &BV = State->getBasicVals();
llvm::APSInt LInt, RInt;
@@ -363,7 +412,7 @@ static Optional<NonLoc> tryRearrange(ProgramStateRef State,
std::tie(RSym, RInt) = decomposeSymbol(RSym, BV);
if (!shouldRearrange(State, Op, LSym, LInt, SingleTy) ||
!shouldRearrange(State, Op, RSym, RInt, SingleTy))
- return None;
+ return std::nullopt;
// We know that no overflows can occur anymore.
return doRearrangeUnchecked(State, Op, LSym, LInt, RSym, RInt);
@@ -376,6 +425,15 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
NonLoc InputLHS = lhs;
NonLoc InputRHS = rhs;
+ // Constraints may have changed since the creation of a bound SVal. Check if
+ // the values can be simplified based on those new constraints.
+ SVal simplifiedLhs = simplifySVal(state, lhs);
+ SVal simplifiedRhs = simplifySVal(state, rhs);
+ if (auto simplifiedLhsAsNonLoc = simplifiedLhs.getAs<NonLoc>())
+ lhs = *simplifiedLhsAsNonLoc;
+ if (auto simplifiedRhsAsNonLoc = simplifiedRhs.getAs<NonLoc>())
+ rhs = *simplifiedRhsAsNonLoc;
+
// Handle trivial case where left-side and right-side are the same.
if (lhs == rhs)
switch (op) {
@@ -400,12 +458,12 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
return evalCast(lhs, resultTy, QualType{});
}
- while (1) {
- switch (lhs.getSubKind()) {
+ while (true) {
+ switch (lhs.getKind()) {
default:
return makeSymExprValNN(op, lhs, rhs, resultTy);
case nonloc::PointerToMemberKind: {
- assert(rhs.getSubKind() == nonloc::PointerToMemberKind &&
+ assert(rhs.getKind() == nonloc::PointerToMemberKind &&
"Both SVals should have pointer-to-member-type");
auto LPTM = lhs.castAs<nonloc::PointerToMember>(),
RPTM = rhs.castAs<nonloc::PointerToMember>();
@@ -421,36 +479,36 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
}
case nonloc::LocAsIntegerKind: {
Loc lhsL = lhs.castAs<nonloc::LocAsInteger>().getLoc();
- switch (rhs.getSubKind()) {
- case nonloc::LocAsIntegerKind:
- // FIXME: at the moment the implementation
- // of modeling "pointers as integers" is not complete.
- if (!BinaryOperator::isComparisonOp(op))
- return UnknownVal();
- return evalBinOpLL(state, op, lhsL,
- rhs.castAs<nonloc::LocAsInteger>().getLoc(),
- resultTy);
- case nonloc::ConcreteIntKind: {
- // FIXME: at the moment the implementation
- // of modeling "pointers as integers" is not complete.
- if (!BinaryOperator::isComparisonOp(op))
- return UnknownVal();
- // Transform the integer into a location and compare.
- // FIXME: This only makes sense for comparisons. If we want to, say,
- // add 1 to a LocAsInteger, we'd better unpack the Loc and add to it,
- // then pack it back into a LocAsInteger.
- llvm::APSInt i = rhs.castAs<nonloc::ConcreteInt>().getValue();
- // If the region has a symbolic base, pay attention to the type; it
- // might be coming from a non-default address space. For non-symbolic
- // regions it doesn't matter that much because such comparisons would
- // most likely evaluate to concrete false anyway. FIXME: We might
- // still need to handle the non-comparison case.
- if (SymbolRef lSym = lhs.getAsLocSymbol(true))
- BasicVals.getAPSIntType(lSym->getType()).apply(i);
- else
- BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i);
- return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy);
- }
+ switch (rhs.getKind()) {
+ case nonloc::LocAsIntegerKind:
+ // FIXME: at the moment the implementation
+ // of modeling "pointers as integers" is not complete.
+ if (!BinaryOperator::isComparisonOp(op))
+ return UnknownVal();
+ return evalBinOpLL(state, op, lhsL,
+ rhs.castAs<nonloc::LocAsInteger>().getLoc(),
+ resultTy);
+ case nonloc::ConcreteIntKind: {
+ // FIXME: at the moment the implementation
+ // of modeling "pointers as integers" is not complete.
+ if (!BinaryOperator::isComparisonOp(op))
+ return UnknownVal();
+ // Transform the integer into a location and compare.
+ // FIXME: This only makes sense for comparisons. If we want to, say,
+ // add 1 to a LocAsInteger, we'd better unpack the Loc and add to it,
+ // then pack it back into a LocAsInteger.
+ llvm::APSInt i = rhs.castAs<nonloc::ConcreteInt>().getValue();
+ // If the region has a symbolic base, pay attention to the type; it
+ // might be coming from a non-default address space. For non-symbolic
+ // regions it doesn't matter that much because such comparisons would
+ // most likely evaluate to concrete false anyway. FIXME: We might
+ // still need to handle the non-comparison case.
+ if (SymbolRef lSym = lhs.getAsLocSymbol(true))
+ BasicVals.getAPSIntType(lSym->getType()).apply(i);
+ else
+ BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i);
+ return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy);
+ }
default:
switch (op) {
case BO_EQ:
@@ -461,13 +519,13 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
// This case also handles pointer arithmetic.
return makeSymExprValNN(op, InputLHS, InputRHS, resultTy);
}
- }
+ }
}
case nonloc::ConcreteIntKind: {
llvm::APSInt LHSValue = lhs.castAs<nonloc::ConcreteInt>().getValue();
// If we're dealing with two known constants, just perform the operation.
- if (const llvm::APSInt *KnownRHSValue = getKnownValue(state, rhs)) {
+ if (const llvm::APSInt *KnownRHSValue = getConstValue(state, rhs)) {
llvm::APSInt RHSValue = *KnownRHSValue;
if (BinaryOperator::isComparisonOp(op)) {
// We're looking for a type big enough to compare the two values.
@@ -485,8 +543,21 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
const llvm::APSInt *Result =
BasicVals.evalAPSInt(op, LHSValue, RHSValue);
- if (!Result)
+ if (!Result) {
+ if (op == BO_Shl || op == BO_Shr) {
+ // FIXME: At this point the constant folding claims that the result
+ // of a bitwise shift is undefined. However, constant folding
+ // relies on the inaccurate type information that is stored in the
+ // bit size of APSInt objects, and if we reached this point, then
+ // the checker core.BitwiseShift already determined that the shift
+ // is valid (in a PreStmt callback, by querying the real type from
+ // the AST node).
+ // To avoid embarrassing false positives, let's just say that we
+ // don't know anything about the result of the shift.
+ return UnknownVal();
+ }
return UndefinedVal();
+ }
return nonloc::ConcreteInt(*Result);
}
@@ -501,7 +572,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
case BO_LE:
case BO_GE:
op = BinaryOperator::reverseComparisonOp(op);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case BO_EQ:
case BO_NE:
case BO_Add:
@@ -513,9 +584,9 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
continue;
case BO_Shr:
// (~0)>>a
- if (LHSValue.isAllOnesValue() && LHSValue.isSigned())
+ if (LHSValue.isAllOnes() && LHSValue.isSigned())
return evalCast(lhs, resultTy, QualType{});
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case BO_Shl:
// 0<<a and 0>>a
if (LHSValue == 0)
@@ -527,7 +598,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
// 0 % x == 0
if (LHSValue == 0)
return makeZeroVal(resultTy);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
default:
return makeSymExprValNN(op, InputLHS, InputRHS, resultTy);
}
@@ -587,7 +658,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
}
// For now, only handle expressions whose RHS is a constant.
- if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) {
+ if (const llvm::APSInt *RHSValue = getConstValue(state, rhs)) {
// If both the LHS and the current expression are additive,
// fold their constants and try again.
if (BinaryOperator::isAdditiveOp(op)) {
@@ -604,16 +675,26 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
const llvm::APSInt &first = IntType.convert(symIntExpr->getRHS());
const llvm::APSInt &second = IntType.convert(*RHSValue);
+ // If the op and lop agrees, then we just need to
+ // sum the constants. Otherwise, we change to operation
+ // type if substraction would produce negative value
+ // (and cause overflow for unsigned integers),
+ // as consequence x+1U-10 produces x-9U, instead
+ // of x+4294967287U, that would be produced without this
+ // additional check.
const llvm::APSInt *newRHS;
- if (lop == op)
+ if (lop == op) {
newRHS = BasicVals.evalAPSInt(BO_Add, first, second);
- else
+ } else if (first >= second) {
newRHS = BasicVals.evalAPSInt(BO_Sub, first, second);
+ op = lop;
+ } else {
+ newRHS = BasicVals.evalAPSInt(BO_Sub, second, first);
+ }
assert(newRHS && "Invalid operation despite common type!");
rhs = nonloc::ConcreteInt(*newRHS);
lhs = nonloc::SymbolVal(symIntExpr->getLHS());
- op = lop;
continue;
}
}
@@ -623,21 +704,11 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
}
}
- // Does the symbolic expression simplify to a constant?
- // If so, "fold" the constant by setting 'lhs' to a ConcreteInt
- // and try again.
- SVal simplifiedLhs = simplifySVal(state, lhs);
- if (simplifiedLhs != lhs)
- if (auto simplifiedLhsAsNonLoc = simplifiedLhs.getAs<NonLoc>()) {
- lhs = *simplifiedLhsAsNonLoc;
- continue;
- }
-
// Is the RHS a constant?
- if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs))
+ if (const llvm::APSInt *RHSValue = getConstValue(state, rhs))
return MakeSymIntVal(Sym, op, *RHSValue, resultTy);
- if (Optional<NonLoc> V = tryRearrange(state, op, lhs, rhs, resultTy))
+ if (std::optional<NonLoc> V = tryRearrange(state, op, lhs, rhs, resultTy))
return *V;
// Give up -- this is not a symbolic expression we can handle.
@@ -693,11 +764,40 @@ static SVal evalBinOpFieldRegionFieldRegion(const FieldRegion *LeftFR,
llvm_unreachable("Fields not found in parent record's definition");
}
+// This is used in debug builds only for now because some downstream users
+// may hit this assert in their subsequent merges.
+// There are still places in the analyzer where equal bitwidth Locs
+// are compared, and need to be found and corrected. Recent previous fixes have
+// addressed the known problems of making NULLs with specific bitwidths
+// for Loc comparisons along with deprecation of APIs for the same purpose.
+//
+static void assertEqualBitWidths(ProgramStateRef State, Loc RhsLoc,
+ Loc LhsLoc) {
+ // Implements a "best effort" check for RhsLoc and LhsLoc bit widths
+ ASTContext &Ctx = State->getStateManager().getContext();
+ uint64_t RhsBitwidth =
+ RhsLoc.getType(Ctx).isNull() ? 0 : Ctx.getTypeSize(RhsLoc.getType(Ctx));
+ uint64_t LhsBitwidth =
+ LhsLoc.getType(Ctx).isNull() ? 0 : Ctx.getTypeSize(LhsLoc.getType(Ctx));
+ if (RhsBitwidth && LhsBitwidth && (LhsLoc.getKind() == RhsLoc.getKind())) {
+ assert(RhsBitwidth == LhsBitwidth &&
+ "RhsLoc and LhsLoc bitwidth must be same!");
+ }
+}
+
// FIXME: all this logic will change if/when we have MemRegion::getLocation().
SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
BinaryOperator::Opcode op,
Loc lhs, Loc rhs,
QualType resultTy) {
+
+ // Assert that bitwidth of lhs and rhs are the same.
+ // This can happen if two different address spaces are used,
+ // and the bitwidths of the address spaces are different.
+ // See LIT case clang/test/Analysis/cstring-checker-addressspace.c
+ // FIXME: See comment above in the function assertEqualBitWidths
+ assertEqualBitWidths(state, rhs, lhs);
+
// Only comparisons and subtractions are valid operations on two pointers.
// See [C99 6.5.5 through 6.5.14] or [C++0x 5.6 through 5.15].
// However, if a pointer is casted to an integer, evalBinOpNN may end up
@@ -725,7 +825,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
}
}
- switch (lhs.getSubKind()) {
+ switch (lhs.getKind()) {
default:
llvm_unreachable("Ordering not implemented for this Loc.");
@@ -755,6 +855,8 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
return UnknownVal();
case loc::ConcreteIntKind: {
+ auto L = lhs.castAs<loc::ConcreteInt>();
+
// If one of the operands is a symbol and the other is a constant,
// build an expression for use by the constraint manager.
if (SymbolRef rSym = rhs.getAsLocSymbol()) {
@@ -763,19 +865,17 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
if (!BinaryOperator::isComparisonOp(op) || op == BO_Cmp)
return UnknownVal();
- const llvm::APSInt &lVal = lhs.castAs<loc::ConcreteInt>().getValue();
op = BinaryOperator::reverseComparisonOp(op);
- return makeNonLoc(rSym, op, lVal, resultTy);
+ return makeNonLoc(rSym, op, L.getValue(), resultTy);
}
// If both operands are constants, just perform the operation.
- if (Optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) {
- SVal ResultVal =
- lhs.castAs<loc::ConcreteInt>().evalBinOp(BasicVals, op, *rInt);
- if (Optional<NonLoc> Result = ResultVal.getAs<NonLoc>())
- return evalCast(*Result, resultTy, QualType{});
+ if (std::optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) {
+ assert(BinaryOperator::isComparisonOp(op) || op == BO_Sub);
- assert(!ResultVal.getAs<Loc>() && "Loc-Loc ops should not produce Locs");
+ if (const auto *ResultInt =
+ BasicVals.evalAPSInt(op, L.getValue(), rInt->getValue()))
+ return evalCast(nonloc::ConcreteInt(*ResultInt), resultTy, QualType{});
return UnknownVal();
}
@@ -783,7 +883,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
// This must come after the test if the RHS is a symbol, which is used to
// build constraints. The address of any non-symbolic region is guaranteed
// to be non-NULL, as is any label.
- assert(rhs.getAs<loc::MemRegionVal>() || rhs.getAs<loc::GotoLabel>());
+ assert((isa<loc::MemRegionVal, loc::GotoLabel>(rhs)));
if (lhs.isZeroConstant()) {
switch (op) {
default:
@@ -804,7 +904,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
return UnknownVal();
}
case loc::MemRegionValKind: {
- if (Optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) {
+ if (std::optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) {
// If one of the operands is a symbol and the other is a constant,
// build an expression for use by the constraint manager.
if (SymbolRef lSym = lhs.getAsLocSymbol(true)) {
@@ -901,7 +1001,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
// Get the left index and cast it to the correct type.
// If the index is unknown or undefined, bail out here.
SVal LeftIndexVal = LeftER->getIndex();
- Optional<NonLoc> LeftIndex = LeftIndexVal.getAs<NonLoc>();
+ std::optional<NonLoc> LeftIndex = LeftIndexVal.getAs<NonLoc>();
if (!LeftIndex)
return UnknownVal();
LeftIndexVal = evalCast(*LeftIndex, ArrayIndexTy, QualType{});
@@ -911,7 +1011,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
// Do the same for the right index.
SVal RightIndexVal = RightER->getIndex();
- Optional<NonLoc> RightIndex = RightIndexVal.getAs<NonLoc>();
+ std::optional<NonLoc> RightIndex = RightIndexVal.getAs<NonLoc>();
if (!RightIndex)
return UnknownVal();
RightIndexVal = evalCast(*RightIndex, ArrayIndexTy, QualType{});
@@ -1019,8 +1119,10 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
// We are dealing with pointer arithmetic.
// Handle pointer arithmetic on constant values.
- if (Optional<nonloc::ConcreteInt> rhsInt = rhs.getAs<nonloc::ConcreteInt>()) {
- if (Optional<loc::ConcreteInt> lhsInt = lhs.getAs<loc::ConcreteInt>()) {
+ if (std::optional<nonloc::ConcreteInt> rhsInt =
+ rhs.getAs<nonloc::ConcreteInt>()) {
+ if (std::optional<loc::ConcreteInt> lhsInt =
+ lhs.getAs<loc::ConcreteInt>()) {
const llvm::APSInt &leftI = lhsInt->getValue();
assert(leftI.isUnsigned());
llvm::APSInt rightI(rhsInt->getValue(), /* isUnsigned */ true);
@@ -1084,7 +1186,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
if (elementType->isVoidType())
elementType = getContext().CharTy;
- if (Optional<NonLoc> indexV = index.getAs<NonLoc>()) {
+ if (std::optional<NonLoc> indexV = index.getAs<NonLoc>()) {
return loc::MemRegionVal(MemMgr.getElementRegion(elementType, *indexV,
superR, getContext()));
}
@@ -1092,26 +1194,72 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
return UnknownVal();
}
+const llvm::APSInt *SimpleSValBuilder::getConstValue(ProgramStateRef state,
+ SVal V) {
+ if (const llvm::APSInt *Res = getConcreteValue(V))
+ return Res;
+
+ if (SymbolRef Sym = V.getAsSymbol())
+ return state->getConstraintManager().getSymVal(state, Sym);
+
+ return nullptr;
+}
+
+const llvm::APSInt *SimpleSValBuilder::getConcreteValue(SVal V) {
+ if (std::optional<loc::ConcreteInt> X = V.getAs<loc::ConcreteInt>())
+ return &X->getValue();
+
+ if (std::optional<nonloc::ConcreteInt> X = V.getAs<nonloc::ConcreteInt>())
+ return &X->getValue();
+
+ return nullptr;
+}
+
const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state,
+ SVal V) {
+ return getConstValue(state, simplifySVal(state, V));
+}
+
+const llvm::APSInt *SimpleSValBuilder::getMinValue(ProgramStateRef state,
SVal V) {
V = simplifySVal(state, V);
- if (V.isUnknownOrUndef())
- return nullptr;
- if (Optional<loc::ConcreteInt> X = V.getAs<loc::ConcreteInt>())
- return &X->getValue();
+ if (const llvm::APSInt *Res = getConcreteValue(V))
+ return Res;
- if (Optional<nonloc::ConcreteInt> X = V.getAs<nonloc::ConcreteInt>())
- return &X->getValue();
+ if (SymbolRef Sym = V.getAsSymbol())
+ return state->getConstraintManager().getSymMinVal(state, Sym);
+
+ return nullptr;
+}
+
+const llvm::APSInt *SimpleSValBuilder::getMaxValue(ProgramStateRef state,
+ SVal V) {
+ V = simplifySVal(state, V);
+
+ if (const llvm::APSInt *Res = getConcreteValue(V))
+ return Res;
if (SymbolRef Sym = V.getAsSymbol())
- return state->getConstraintManager().getSymVal(state, Sym);
+ return state->getConstraintManager().getSymMaxVal(state, Sym);
- // FIXME: Add support for SymExprs.
return nullptr;
}
+SVal SimpleSValBuilder::simplifyUntilFixpoint(ProgramStateRef State, SVal Val) {
+ SVal SimplifiedVal = simplifySValOnce(State, Val);
+ while (SimplifiedVal != Val) {
+ Val = SimplifiedVal;
+ SimplifiedVal = simplifySValOnce(State, Val);
+ }
+ return SimplifiedVal;
+}
+
SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) {
+ return simplifyUntilFixpoint(State, V);
+}
+
+SVal SimpleSValBuilder::simplifySValOnce(ProgramStateRef State, SVal V) {
// For now, this function tries to constant-fold symbols inside a
// nonloc::SymbolVal, and does nothing else. More simplifications should
// be possible, such as constant-folding an index in an ElementRegion.
@@ -1139,6 +1287,24 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) {
return cache(Sym, SVB.makeSymbolVal(Sym));
}
+ // Return the known const value for the Sym if available, or return Undef
+ // otherwise.
+ SVal getConst(SymbolRef Sym) {
+ const llvm::APSInt *Const =
+ State->getConstraintManager().getSymVal(State, Sym);
+ if (Const)
+ return Loc::isLocType(Sym->getType()) ? (SVal)SVB.makeIntLocVal(*Const)
+ : (SVal)SVB.makeIntVal(*Const);
+ return UndefinedVal();
+ }
+
+ SVal getConstOrVisit(SymbolRef Sym) {
+ const SVal Ret = getConst(Sym);
+ if (Ret.isUndef())
+ return Visit(Sym);
+ return Ret;
+ }
+
public:
Simplifier(ProgramStateRef State)
: State(State), SVB(State->getStateManager().getSValBuilder()) {}
@@ -1146,21 +1312,18 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) {
SVal VisitSymbolData(const SymbolData *S) {
// No cache here.
if (const llvm::APSInt *I =
- SVB.getKnownValue(State, SVB.makeSymbolVal(S)))
+ State->getConstraintManager().getSymVal(State, S))
return Loc::isLocType(S->getType()) ? (SVal)SVB.makeIntLocVal(*I)
: (SVal)SVB.makeIntVal(*I);
return SVB.makeSymbolVal(S);
}
- // TODO: Support SymbolCast. Support IntSymExpr when/if we actually
- // start producing them.
-
SVal VisitSymIntExpr(const SymIntExpr *S) {
auto I = Cached.find(S);
if (I != Cached.end())
return I->second;
- SVal LHS = Visit(S->getLHS());
+ SVal LHS = getConstOrVisit(S->getLHS());
if (isUnchanged(S->getLHS(), LHS))
return skip(S);
@@ -1187,6 +1350,20 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) {
S, SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()));
}
+ SVal VisitIntSymExpr(const IntSymExpr *S) {
+ auto I = Cached.find(S);
+ if (I != Cached.end())
+ return I->second;
+
+ SVal RHS = getConstOrVisit(S->getRHS());
+ if (isUnchanged(S->getRHS(), RHS))
+ return skip(S);
+
+ SVal LHS = SVB.makeIntVal(S->getLHS());
+ return cache(
+ S, SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()));
+ }
+
SVal VisitSymSymExpr(const SymSymExpr *S) {
auto I = Cached.find(S);
if (I != Cached.end())
@@ -1200,8 +1377,9 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) {
Loc::isLocType(S->getRHS()->getType()))
return skip(S);
- SVal LHS = Visit(S->getLHS());
- SVal RHS = Visit(S->getRHS());
+ SVal LHS = getConstOrVisit(S->getLHS());
+ SVal RHS = getConstOrVisit(S->getRHS());
+
if (isUnchanged(S->getLHS(), LHS) && isUnchanged(S->getRHS(), RHS))
return skip(S);
@@ -1209,11 +1387,35 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) {
S, SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()));
}
+ SVal VisitSymbolCast(const SymbolCast *S) {
+ auto I = Cached.find(S);
+ if (I != Cached.end())
+ return I->second;
+ const SymExpr *OpSym = S->getOperand();
+ SVal OpVal = getConstOrVisit(OpSym);
+ if (isUnchanged(OpSym, OpVal))
+ return skip(S);
+
+ return cache(S, SVB.evalCast(OpVal, S->getType(), OpSym->getType()));
+ }
+
+ SVal VisitUnarySymExpr(const UnarySymExpr *S) {
+ auto I = Cached.find(S);
+ if (I != Cached.end())
+ return I->second;
+ SVal Op = getConstOrVisit(S->getOperand());
+ if (isUnchanged(S->getOperand(), Op))
+ return skip(S);
+
+ return cache(
+ S, SVB.evalUnaryOp(State, S->getOpcode(), Op, S->getType()));
+ }
+
SVal VisitSymExpr(SymbolRef S) { return nonloc::SymbolVal(S); }
SVal VisitMemRegion(const MemRegion *R) { return loc::MemRegionVal(R); }
- SVal VisitNonLocSymbolVal(nonloc::SymbolVal V) {
+ SVal VisitSymbolVal(nonloc::SymbolVal V) {
// Simplification is much more costly than computing complexity.
// For high complexity, it may be not worth it.
return Visit(V.getSymbol());
@@ -1222,14 +1424,6 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) {
SVal VisitSVal(SVal V) { return V; }
};
- // A crude way of preventing this function from calling itself from evalBinOp.
- static bool isReentering = false;
- if (isReentering)
- return V;
-
- isReentering = true;
SVal SimplifiedV = Simplifier(State).Visit(V);
- isReentering = false;
-
return SimplifiedV;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Store.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Store.cpp
index b867b0746f90..67ca61bb56ba 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Store.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Store.cpp
@@ -29,12 +29,12 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "llvm/ADT/APSInt.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include <cassert>
#include <cstdint>
+#include <optional>
using namespace clang;
using namespace ento;
@@ -71,8 +71,8 @@ const ElementRegion *StoreManager::GetElementZeroRegion(const SubRegion *R,
return MRMgr.getElementRegion(T, idx, R, Ctx);
}
-Optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R,
- QualType CastToTy) {
+std::optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R,
+ QualType CastToTy) {
ASTContext &Ctx = StateMgr.getContext();
// Handle casts to Objective-C objects.
@@ -84,30 +84,36 @@ Optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R,
// involved. Blocks can be casted to/from 'id', as they can be treated
// as Objective-C objects. This could possibly be handled by enhancing
// our reasoning of downcasts of symbolic objects.
- if (isa<CodeTextRegion>(R) || isa<SymbolicRegion>(R))
+ if (isa<CodeTextRegion, SymbolicRegion>(R))
return R;
// We don't know what to make of it. Return a NULL region, which
// will be interpreted as UnknownVal.
- return None;
+ return std::nullopt;
}
// Now assume we are casting from pointer to pointer. Other cases should
// already be handled.
QualType PointeeTy = CastToTy->getPointeeType();
QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy);
+ CanonPointeeTy = CanonPointeeTy.getLocalUnqualifiedType();
// Handle casts to void*. We just pass the region through.
- if (CanonPointeeTy.getLocalUnqualifiedType() == Ctx.VoidTy)
+ if (CanonPointeeTy == Ctx.VoidTy)
return R;
- // Handle casts from compatible types.
- if (R->isBoundable())
+ const auto IsSameRegionType = [&Ctx](const MemRegion *R, QualType OtherTy) {
if (const auto *TR = dyn_cast<TypedValueRegion>(R)) {
QualType ObjTy = Ctx.getCanonicalType(TR->getValueType());
- if (CanonPointeeTy == ObjTy)
- return R;
+ if (OtherTy == ObjTy.getLocalUnqualifiedType())
+ return true;
}
+ return false;
+ };
+
+ // Handle casts from compatible types.
+ if (R->isBoundable() && IsSameRegionType(R, CanonPointeeTy))
+ return R;
// Process region cast according to the kind of the region being cast.
switch (R->getKind()) {
@@ -138,6 +144,7 @@ Optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R,
case MemRegion::NonParamVarRegionKind:
case MemRegion::ParamVarRegionKind:
case MemRegion::CXXTempObjectRegionKind:
+ case MemRegion::CXXLifetimeExtendedObjectRegionKind:
case MemRegion::CXXBaseObjectRegionKind:
case MemRegion::CXXDerivedObjectRegionKind:
return MakeElementRegion(cast<SubRegion>(R), PointeeTy);
@@ -169,21 +176,16 @@ Optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R,
// If we cannot compute a raw offset, throw up our hands and return
// a NULL MemRegion*.
if (!baseR)
- return None;
+ return std::nullopt;
CharUnits off = rawOff.getOffset();
if (off.isZero()) {
- // Edge case: we are at 0 bytes off the beginning of baseR. We
- // check to see if type we are casting to is the same as the base
- // region. If so, just return the base region.
- if (const auto *TR = dyn_cast<TypedValueRegion>(baseR)) {
- QualType ObjTy = Ctx.getCanonicalType(TR->getValueType());
- QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy);
- if (CanonPointeeTy == ObjTy)
- return baseR;
- }
-
+ // Edge case: we are at 0 bytes off the beginning of baseR. We check to
+ // see if the type we are casting to is the same as the type of the base
+ // region. If so, just return the base region.
+ if (IsSameRegionType(baseR, CanonPointeeTy))
+ return baseR;
// Otherwise, create a new ElementRegion at offset 0.
return MakeElementRegion(cast<SubRegion>(baseR), PointeeTy);
}
@@ -248,17 +250,15 @@ static bool regionMatchesCXXRecordType(SVal V, QualType Ty) {
}
SVal StoreManager::evalDerivedToBase(SVal Derived, const CastExpr *Cast) {
- // Sanity check to avoid doing the wrong thing in the face of
+ // Early return to avoid doing the wrong thing in the face of
// reinterpret_cast.
if (!regionMatchesCXXRecordType(Derived, Cast->getSubExpr()->getType()))
return UnknownVal();
// Walk through the cast path to create nested CXXBaseRegions.
SVal Result = Derived;
- for (CastExpr::path_const_iterator I = Cast->path_begin(),
- E = Cast->path_end();
- I != E; ++I) {
- Result = evalDerivedToBase(Result, (*I)->getType(), (*I)->isVirtual());
+ for (const CXXBaseSpecifier *Base : Cast->path()) {
+ Result = evalDerivedToBase(Result, Base->getType(), Base->isVirtual());
}
return Result;
}
@@ -313,10 +313,8 @@ static const CXXRecordDecl *getCXXRecordType(const MemRegion *MR) {
return nullptr;
}
-SVal StoreManager::attemptDownCast(SVal Base, QualType TargetType,
- bool &Failed) {
- Failed = false;
-
+std::optional<SVal> StoreManager::evalBaseToDerived(SVal Base,
+ QualType TargetType) {
const MemRegion *MR = Base.getAsRegion();
if (!MR)
return UnknownVal();
@@ -391,7 +389,9 @@ SVal StoreManager::attemptDownCast(SVal Base, QualType TargetType,
}
// We failed if the region we ended up with has perfect type info.
- Failed = isa<TypedValueRegion>(MR);
+ if (isa<TypedValueRegion>(MR))
+ return std::nullopt;
+
return UnknownVal();
}
@@ -402,7 +402,7 @@ SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) {
Loc BaseL = Base.castAs<Loc>();
const SubRegion* BaseR = nullptr;
- switch (BaseL.getSubKind()) {
+ switch (BaseL.getKind()) {
case loc::MemRegionValKind:
BaseR = cast<SubRegion>(BaseL.castAs<loc::MemRegionVal>().getRegion());
break;
@@ -442,14 +442,27 @@ SVal StoreManager::getLValueIvar(const ObjCIvarDecl *decl, SVal base) {
SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
SVal Base) {
+
+ // Special case, if index is 0, return the same type as if
+ // this was not an array dereference.
+ if (Offset.isZeroConstant()) {
+ QualType BT = Base.getType(this->Ctx);
+ if (!BT.isNull() && !elementType.isNull()) {
+ QualType PointeeTy = BT->getPointeeType();
+ if (!PointeeTy.isNull() &&
+ PointeeTy.getCanonicalType() == elementType.getCanonicalType())
+ return Base;
+ }
+ }
+
// If the base is an unknown or undefined value, just return it back.
// FIXME: For absolute pointer addresses, we just return that value back as
// well, although in reality we should return the offset added to that
// value. See also the similar FIXME in getLValueFieldOrIvar().
- if (Base.isUnknownOrUndef() || Base.getAs<loc::ConcreteInt>())
+ if (Base.isUnknownOrUndef() || isa<loc::ConcreteInt>(Base))
return Base;
- if (Base.getAs<loc::GotoLabel>())
+ if (isa<loc::GotoLabel>(Base))
return UnknownVal();
const SubRegion *BaseRegion =
@@ -475,7 +488,7 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
SVal BaseIdx = ElemR->getIndex();
- if (!BaseIdx.getAs<nonloc::ConcreteInt>())
+ if (!isa<nonloc::ConcreteInt>(BaseIdx))
return UnknownVal();
const llvm::APSInt &BaseIdxI =
@@ -484,7 +497,7 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
// Only allow non-integer offsets if the base region has no offset itself.
// FIXME: This is a somewhat arbitrary restriction. We should be using
// SValBuilder here to add the two offsets without checking their types.
- if (!Offset.getAs<nonloc::ConcreteInt>()) {
+ if (!isa<nonloc::ConcreteInt>(Offset)) {
if (isa<ElementRegion>(BaseRegion->StripCasts()))
return UnknownVal();
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
index 79a8eef30576..9025e11a3f51 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -65,14 +65,23 @@ void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS,
}
void SymbolCast::dumpToStream(raw_ostream &os) const {
- os << '(' << ToTy.getAsString() << ") (";
+ os << '(' << ToTy << ") (";
Operand->dumpToStream(os);
os << ')';
}
+void UnarySymExpr::dumpToStream(raw_ostream &os) const {
+ os << UnaryOperator::getOpcodeStr(Op);
+ bool Binary = isa<BinarySymExpr>(Operand);
+ if (Binary)
+ os << '(';
+ Operand->dumpToStream(os);
+ if (Binary)
+ os << ')';
+}
+
void SymbolConjured::dumpToStream(raw_ostream &os) const {
- os << getKindStr() << getSymbolID() << '{' << T.getAsString() << ", LC"
- << LCtx->getID();
+ os << getKindStr() << getSymbolID() << '{' << T << ", LC" << LCtx->getID();
if (S)
os << ", S" << S->getID(LCtx->getDecl()->getASTContext());
else
@@ -90,15 +99,13 @@ void SymbolExtent::dumpToStream(raw_ostream &os) const {
}
void SymbolMetadata::dumpToStream(raw_ostream &os) const {
- os << getKindStr() << getSymbolID() << '{' << getRegion() << ','
- << T.getAsString() << '}';
+ os << getKindStr() << getSymbolID() << '{' << getRegion() << ',' << T << '}';
}
void SymbolData::anchor() {}
void SymbolRegionValue::dumpToStream(raw_ostream &os) const {
- os << getKindStr() << getSymbolID() << '<' << getType().getAsString() << ' '
- << R << '>';
+ os << getKindStr() << getSymbolID() << '<' << getType() << ' ' << R << '>';
}
bool SymExpr::symbol_iterator::operator==(const symbol_iterator &X) const {
@@ -137,6 +144,9 @@ void SymExpr::symbol_iterator::expand() {
case SymExpr::SymbolCastKind:
itr.push_back(cast<SymbolCast>(SE)->getOperand());
return;
+ case SymExpr::UnarySymExprKind:
+ itr.push_back(cast<UnarySymExpr>(SE)->getOperand());
+ return;
case SymExpr::SymIntExprKind:
itr.push_back(cast<SymIntExpr>(SE)->getLHS());
return;
@@ -160,8 +170,7 @@ SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) {
void *InsertPos;
SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
if (!SD) {
- SD = (SymExpr*) BPAlloc.Allocate<SymbolRegionValue>();
- new (SD) SymbolRegionValue(SymbolCounter, R);
+ SD = new (BPAlloc) SymbolRegionValue(SymbolCounter, R);
DataSet.InsertNode(SD, InsertPos);
++SymbolCounter;
}
@@ -179,8 +188,7 @@ const SymbolConjured* SymbolManager::conjureSymbol(const Stmt *E,
void *InsertPos;
SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
if (!SD) {
- SD = (SymExpr*) BPAlloc.Allocate<SymbolConjured>();
- new (SD) SymbolConjured(SymbolCounter, E, LCtx, T, Count, SymbolTag);
+ SD = new (BPAlloc) SymbolConjured(SymbolCounter, E, LCtx, T, Count, SymbolTag);
DataSet.InsertNode(SD, InsertPos);
++SymbolCounter;
}
@@ -196,8 +204,7 @@ SymbolManager::getDerivedSymbol(SymbolRef parentSymbol,
void *InsertPos;
SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
if (!SD) {
- SD = (SymExpr*) BPAlloc.Allocate<SymbolDerived>();
- new (SD) SymbolDerived(SymbolCounter, parentSymbol, R);
+ SD = new (BPAlloc) SymbolDerived(SymbolCounter, parentSymbol, R);
DataSet.InsertNode(SD, InsertPos);
++SymbolCounter;
}
@@ -212,8 +219,7 @@ SymbolManager::getExtentSymbol(const SubRegion *R) {
void *InsertPos;
SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
if (!SD) {
- SD = (SymExpr*) BPAlloc.Allocate<SymbolExtent>();
- new (SD) SymbolExtent(SymbolCounter, R);
+ SD = new (BPAlloc) SymbolExtent(SymbolCounter, R);
DataSet.InsertNode(SD, InsertPos);
++SymbolCounter;
}
@@ -230,8 +236,7 @@ SymbolManager::getMetadataSymbol(const MemRegion* R, const Stmt *S, QualType T,
void *InsertPos;
SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
if (!SD) {
- SD = (SymExpr*) BPAlloc.Allocate<SymbolMetadata>();
- new (SD) SymbolMetadata(SymbolCounter, R, S, T, LCtx, Count, SymbolTag);
+ SD = new (BPAlloc) SymbolMetadata(SymbolCounter, R, S, T, LCtx, Count, SymbolTag);
DataSet.InsertNode(SD, InsertPos);
++SymbolCounter;
}
@@ -247,8 +252,7 @@ SymbolManager::getCastSymbol(const SymExpr *Op,
void *InsertPos;
SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos);
if (!data) {
- data = (SymbolCast*) BPAlloc.Allocate<SymbolCast>();
- new (data) SymbolCast(Op, From, To);
+ data = new (BPAlloc) SymbolCast(Op, From, To);
DataSet.InsertNode(data, InsertPos);
}
@@ -265,8 +269,7 @@ const SymIntExpr *SymbolManager::getSymIntExpr(const SymExpr *lhs,
SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos);
if (!data) {
- data = (SymIntExpr*) BPAlloc.Allocate<SymIntExpr>();
- new (data) SymIntExpr(lhs, op, v, t);
+ data = new (BPAlloc) SymIntExpr(lhs, op, v, t);
DataSet.InsertNode(data, InsertPos);
}
@@ -283,8 +286,7 @@ const IntSymExpr *SymbolManager::getIntSymExpr(const llvm::APSInt& lhs,
SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos);
if (!data) {
- data = (IntSymExpr*) BPAlloc.Allocate<IntSymExpr>();
- new (data) IntSymExpr(lhs, op, rhs, t);
+ data = new (BPAlloc) IntSymExpr(lhs, op, rhs, t);
DataSet.InsertNode(data, InsertPos);
}
@@ -301,14 +303,28 @@ const SymSymExpr *SymbolManager::getSymSymExpr(const SymExpr *lhs,
SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos);
if (!data) {
- data = (SymSymExpr*) BPAlloc.Allocate<SymSymExpr>();
- new (data) SymSymExpr(lhs, op, rhs, t);
+ data = new (BPAlloc) SymSymExpr(lhs, op, rhs, t);
DataSet.InsertNode(data, InsertPos);
}
return cast<SymSymExpr>(data);
}
+const UnarySymExpr *SymbolManager::getUnarySymExpr(const SymExpr *Operand,
+ UnaryOperator::Opcode Opc,
+ QualType T) {
+ llvm::FoldingSetNodeID ID;
+ UnarySymExpr::Profile(ID, Operand, Opc, T);
+ void *InsertPos;
+ SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos);
+ if (!data) {
+ data = new (BPAlloc) UnarySymExpr(Operand, Opc, T);
+ DataSet.InsertNode(data, InsertPos);
+ }
+
+ return cast<UnarySymExpr>(data);
+}
+
QualType SymbolConjured::getType() const {
return T;
}
@@ -372,7 +388,7 @@ void SymbolReaper::markDependentsLive(SymbolRef sym) {
if (const SymbolRefSmallVectorTy *Deps = SymMgr.getDependentSymbols(sym)) {
for (const auto I : *Deps) {
- if (TheLiving.find(I) != TheLiving.end())
+ if (TheLiving.contains(I))
continue;
markLive(I);
}
@@ -385,17 +401,21 @@ void SymbolReaper::markLive(SymbolRef sym) {
}
void SymbolReaper::markLive(const MemRegion *region) {
- RegionRoots.insert(region->getBaseRegion());
+ LiveRegionRoots.insert(region->getBaseRegion());
markElementIndicesLive(region);
}
+void SymbolReaper::markLazilyCopied(const clang::ento::MemRegion *region) {
+ LazilyCopiedRegionRoots.insert(region->getBaseRegion());
+}
+
void SymbolReaper::markElementIndicesLive(const MemRegion *region) {
for (auto SR = dyn_cast<SubRegion>(region); SR;
SR = dyn_cast<SubRegion>(SR->getSuperRegion())) {
if (const auto ER = dyn_cast<ElementRegion>(SR)) {
SVal Idx = ER->getIndex();
- for (auto SI = Idx.symbol_begin(), SE = Idx.symbol_end(); SI != SE; ++SI)
- markLive(*SI);
+ for (SymbolRef Sym : Idx.symbols())
+ markLive(Sym);
}
}
}
@@ -411,8 +431,7 @@ bool SymbolReaper::isLiveRegion(const MemRegion *MR) {
// is not used later in the path, we can diagnose a leak of a value within
// that field earlier than, say, the variable that contains the field dies.
MR = MR->getBaseRegion();
-
- if (RegionRoots.count(MR))
+ if (LiveRegionRoots.count(MR))
return true;
if (const auto *SR = dyn_cast<SymbolicRegion>(MR))
@@ -425,19 +444,16 @@ bool SymbolReaper::isLiveRegion(const MemRegion *MR) {
// tell if anything still refers to this region. Unlike SymbolicRegions,
// AllocaRegions don't have associated symbols, though, so we don't actually
// have a way to track their liveness.
- if (isa<AllocaRegion>(MR))
- return true;
-
- if (isa<CXXThisRegion>(MR))
- return true;
-
- if (isa<MemSpaceRegion>(MR))
- return true;
+ return isa<AllocaRegion, CXXThisRegion, MemSpaceRegion, CodeTextRegion>(MR);
+}
- if (isa<CodeTextRegion>(MR))
- return true;
+bool SymbolReaper::isLazilyCopiedRegion(const MemRegion *MR) const {
+ // TODO: See comment in isLiveRegion.
+ return LazilyCopiedRegionRoots.count(MR->getBaseRegion());
+}
- return false;
+bool SymbolReaper::isReadableRegion(const MemRegion *MR) {
+ return isLiveRegion(MR) || isLazilyCopiedRegion(MR);
}
bool SymbolReaper::isLive(SymbolRef sym) {
@@ -450,7 +466,7 @@ bool SymbolReaper::isLive(SymbolRef sym) {
switch (sym->getKind()) {
case SymExpr::SymbolRegionValueKind:
- KnownLive = isLiveRegion(cast<SymbolRegionValue>(sym)->getRegion());
+ KnownLive = isReadableRegion(cast<SymbolRegionValue>(sym)->getRegion());
break;
case SymExpr::SymbolConjuredKind:
KnownLive = false;
@@ -480,6 +496,9 @@ bool SymbolReaper::isLive(SymbolRef sym) {
case SymExpr::SymbolCastKind:
KnownLive = isLive(cast<SymbolCast>(sym)->getOperand());
break;
+ case SymExpr::UnarySymExprKind:
+ KnownLive = isLive(cast<UnarySymExpr>(sym)->getOperand());
+ break;
}
if (KnownLive)
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp
index 4f3be7cae331..71268af22e24 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp
@@ -31,8 +31,8 @@ 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
+/// Emits minimal diagnostics (report message + notes) for the 'none' output
+/// type to the standard error, or to complement many others. Emits detailed
/// diagnostics in textual format for the 'text' output type.
class TextDiagnostics : public PathDiagnosticConsumer {
PathDiagnosticConsumerOptions DiagOpts;
@@ -86,10 +86,7 @@ public:
}
};
- for (std::vector<const PathDiagnostic *>::iterator I = Diags.begin(),
- E = Diags.end();
- I != E; ++I) {
- const PathDiagnostic *PD = *I;
+ for (const PathDiagnostic *PD : Diags) {
std::string WarningMsg = (DiagOpts.ShouldDisplayDiagnosticName
? " [" + PD->getCheckerName() + "]"
: "")
@@ -129,7 +126,7 @@ public:
Rewriter Rewrite(SM, LO);
if (!applyAllReplacements(Repls, Rewrite)) {
- llvm::errs() << "An error occured during applying fix-it.\n";
+ llvm::errs() << "An error occurred during applying fix-it.\n";
}
Rewrite.overwriteChangedFiles();
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/WorkList.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/WorkList.cpp
index 348552ba73a9..7042a9020837 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/WorkList.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/WorkList.cpp
@@ -205,12 +205,6 @@ class UnexploredFirstPriorityQueue : public WorkList {
using QueuePriority = std::pair<int, unsigned long>;
using QueueItem = std::pair<WorkListUnit, QueuePriority>;
- struct ExplorationComparator {
- bool operator() (const QueueItem &LHS, const QueueItem &RHS) {
- return LHS.second < RHS.second;
- }
- };
-
// Number of inserted nodes, used to emulate DFS ordering in the priority
// queue when insertions are equal.
unsigned long Counter = 0;
@@ -219,7 +213,7 @@ class UnexploredFirstPriorityQueue : public WorkList {
VisitedTimesMap NumReached;
// The top item is the largest one.
- llvm::PriorityQueue<QueueItem, std::vector<QueueItem>, ExplorationComparator>
+ llvm::PriorityQueue<QueueItem, std::vector<QueueItem>, llvm::less_second>
queue;
public:
@@ -267,12 +261,6 @@ class UnexploredFirstPriorityLocationQueue : public WorkList {
using QueuePriority = std::pair<int, unsigned long>;
using QueueItem = std::pair<WorkListUnit, QueuePriority>;
- struct ExplorationComparator {
- bool operator() (const QueueItem &LHS, const QueueItem &RHS) {
- return LHS.second < RHS.second;
- }
- };
-
// Number of inserted nodes, used to emulate DFS ordering in the priority
// queue when insertions are equal.
unsigned long Counter = 0;
@@ -281,7 +269,7 @@ class UnexploredFirstPriorityLocationQueue : public WorkList {
VisitedTimesMap NumReached;
// The top item is the largest one.
- llvm::PriorityQueue<QueueItem, std::vector<QueueItem>, ExplorationComparator>
+ llvm::PriorityQueue<QueueItem, std::vector<QueueItem>, llvm::less_second>
queue;
public:
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index 31de49033ac2..b6ef40595e3c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -27,7 +27,6 @@
#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"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -35,6 +34,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
@@ -87,7 +87,7 @@ public:
ASTContext *Ctx;
Preprocessor &PP;
const std::string OutDir;
- AnalyzerOptionsRef Opts;
+ AnalyzerOptions &Opts;
ArrayRef<std::string> Plugins;
CodeInjector *Injector;
cross_tu::CrossTranslationUnitContext CTU;
@@ -121,15 +121,15 @@ public:
FunctionSummariesTy FunctionSummaries;
AnalysisConsumer(CompilerInstance &CI, const std::string &outdir,
- AnalyzerOptionsRef opts, ArrayRef<std::string> plugins,
+ AnalyzerOptions &opts, ArrayRef<std::string> plugins,
CodeInjector *injector)
: RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr),
- PP(CI.getPreprocessor()), OutDir(outdir), Opts(std::move(opts)),
+ PP(CI.getPreprocessor()), OutDir(outdir), Opts(opts),
Plugins(plugins), Injector(injector), CTU(CI),
MacroExpansions(CI.getLangOpts()) {
DigestAnalyzerOptions();
- if (Opts->AnalyzerDisplayProgress || Opts->PrintStats ||
- Opts->ShouldSerializeStats) {
+ if (Opts.AnalyzerDisplayProgress || Opts.PrintStats ||
+ Opts.ShouldSerializeStats) {
AnalyzerTimers = std::make_unique<llvm::TimerGroup>(
"analyzer", "Analyzer timers");
SyntaxCheckTimer = std::make_unique<llvm::Timer>(
@@ -141,27 +141,27 @@ public:
*AnalyzerTimers);
}
- if (Opts->PrintStats || Opts->ShouldSerializeStats) {
- llvm::EnableStatistics(/* PrintOnExit= */ false);
+ if (Opts.PrintStats || Opts.ShouldSerializeStats) {
+ llvm::EnableStatistics(/* DoPrintOnExit= */ false);
}
- if (Opts->ShouldDisplayMacroExpansions)
+ if (Opts.ShouldDisplayMacroExpansions)
MacroExpansions.registerForPreprocessor(PP);
}
~AnalysisConsumer() override {
- if (Opts->PrintStats) {
+ if (Opts.PrintStats) {
llvm::PrintStatistics();
}
}
void DigestAnalyzerOptions() {
- switch (Opts->AnalysisDiagOpt) {
+ switch (Opts.AnalysisDiagOpt) {
case PD_NONE:
break;
#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN) \
case PD_##NAME: \
- CREATEFN(Opts->getDiagOpts(), PathConsumers, OutDir, PP, CTU, \
+ CREATEFN(Opts.getDiagOpts(), PathConsumers, OutDir, PP, CTU, \
MacroExpansions); \
break;
#include "clang/StaticAnalyzer/Core/Analyses.def"
@@ -170,15 +170,9 @@ public:
}
// Create the analyzer component creators.
- switch (Opts->AnalysisStoreOpt) {
- default:
- llvm_unreachable("Unknown store manager.");
-#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN) \
- case NAME##Model: CreateStoreMgr = CREATEFN; break;
-#include "clang/StaticAnalyzer/Core/Analyses.def"
- }
+ CreateStoreMgr = &CreateRegionStoreManager;
- switch (Opts->AnalysisConstraintsOpt) {
+ switch (Opts.AnalysisConstraintsOpt) {
default:
llvm_unreachable("Unknown constraint manager.");
#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \
@@ -188,7 +182,7 @@ public:
}
void DisplayTime(llvm::TimeRecord &Time) {
- if (!Opts->AnalyzerDisplayProgress) {
+ if (!Opts.AnalyzerDisplayProgress) {
return;
}
llvm::errs() << " : " << llvm::format("%1.1f", Time.getWallTime() * 1000)
@@ -197,7 +191,7 @@ public:
void DisplayFunction(const Decl *D, AnalysisMode Mode,
ExprEngine::InliningModes IMode) {
- if (!Opts->AnalyzerDisplayProgress)
+ if (!Opts.AnalyzerDisplayProgress)
return;
SourceManager &SM = Mgr->getASTContext().getSourceManager();
@@ -228,12 +222,12 @@ public:
void Initialize(ASTContext &Context) override {
Ctx = &Context;
- checkerMgr = std::make_unique<CheckerManager>(*Ctx, *Opts, PP, Plugins,
+ checkerMgr = std::make_unique<CheckerManager>(*Ctx, Opts, PP, Plugins,
CheckerRegistrationFns);
Mgr = std::make_unique<AnalysisManager>(*Ctx, PP, PathConsumers,
CreateStoreMgr, CreateConstraintMgr,
- checkerMgr.get(), *Opts, Injector);
+ checkerMgr.get(), Opts, Injector);
}
/// Store the top level decls in the set to be processed later on.
@@ -284,11 +278,11 @@ public:
}
bool VisitVarDecl(VarDecl *VD) {
- if (!Opts->IsNaiveCTUEnabled)
+ if (!Opts.IsNaiveCTUEnabled)
return true;
if (VD->hasExternalStorage() || VD->isStaticDataMember()) {
- if (!cross_tu::containsConst(VD, *Ctx))
+ if (!cross_tu::shouldImport(VD, *Ctx))
return true;
} else {
// Cannot be initialized in another TU.
@@ -299,8 +293,8 @@ public:
return true;
llvm::Expected<const VarDecl *> CTUDeclOrError =
- CTU.getCrossTUDefinition(VD, Opts->CTUDir, Opts->CTUIndexName,
- Opts->DisplayCTUProgress);
+ CTU.getCrossTUDefinition(VD, Opts.CTUDir, Opts.CTUIndexName,
+ Opts.DisplayCTUProgress);
if (!CTUDeclOrError) {
handleAllErrors(CTUDeclOrError.takeError(),
@@ -314,7 +308,7 @@ public:
bool VisitFunctionDecl(FunctionDecl *FD) {
IdentifierInfo *II = FD->getIdentifier();
- if (II && II->getName().startswith("__inline"))
+ if (II && II->getName().starts_with("__inline"))
return true;
// We skip function template definitions, as their semantics is
@@ -357,13 +351,12 @@ public:
private:
void storeTopLevelDecls(DeclGroupRef DG);
- std::string getFunctionName(const Decl *D);
/// Check if we should skip (not analyze) the given function.
AnalysisMode getModeForDecl(Decl *D, AnalysisMode Mode);
void runAnalysisOnTranslationUnit(ASTContext &C);
- /// Print \p S to stderr if \c Opts->AnalyzerDisplayProgress is set.
+ /// Print \p S to stderr if \c Opts.AnalyzerDisplayProgress is set.
void reportAnalyzerProgress(StringRef S);
}; // namespace
} // end anonymous namespace
@@ -382,14 +375,14 @@ void AnalysisConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
}
void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) {
- for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) {
+ for (auto &I : DG) {
// Skip ObjCMethodDecl, wait for the objc container to avoid
// analyzing twice.
- if (isa<ObjCMethodDecl>(*I))
+ if (isa<ObjCMethodDecl>(I))
continue;
- LocalTUDecls.push_back(*I);
+ LocalTUDecls.push_back(I);
}
}
@@ -461,11 +454,9 @@ void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) {
SetOfConstDecls Visited;
SetOfConstDecls VisitedAsTopLevel;
llvm::ReversePostOrderTraversal<clang::CallGraph*> RPOT(&CG);
- for (llvm::ReversePostOrderTraversal<clang::CallGraph*>::rpo_iterator
- I = RPOT.begin(), E = RPOT.end(); I != E; ++I) {
+ for (auto &N : RPOT) {
NumFunctionTopLevel++;
- CallGraphNode *N = *I;
Decl *D = N->getDecl();
// Skip the abstract root node.
@@ -477,6 +468,18 @@ void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) {
if (shouldSkipFunction(D, Visited, VisitedAsTopLevel))
continue;
+ // The CallGraph might have declarations as callees. However, during CTU
+ // the declaration might form a declaration chain with the newly imported
+ // definition from another TU. In this case we don't want to analyze the
+ // function definition as toplevel.
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ // Calling 'hasBody' replaces 'FD' in place with the FunctionDecl
+ // that has the body.
+ FD->hasBody(FD);
+ if (CTU.isImportedAsNew(FD))
+ continue;
+ }
+
// Analyze the function.
SetOfConstDecls VisitedCallees;
@@ -493,13 +496,33 @@ void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) {
}
}
-static bool isBisonFile(ASTContext &C) {
+static bool fileContainsString(StringRef Substring, ASTContext &C) {
const SourceManager &SM = C.getSourceManager();
FileID FID = SM.getMainFileID();
StringRef Buffer = SM.getBufferOrFake(FID).getBuffer();
- if (Buffer.startswith("/* A Bison parser, made by"))
- return true;
- return false;
+ return Buffer.contains(Substring);
+}
+
+static void reportAnalyzerFunctionMisuse(const AnalyzerOptions &Opts,
+ const ASTContext &Ctx) {
+ llvm::errs() << "Every top-level function was skipped.\n";
+
+ if (!Opts.AnalyzerDisplayProgress)
+ llvm::errs() << "Pass the -analyzer-display-progress for tracking which "
+ "functions are analyzed.\n";
+
+ bool HasBrackets =
+ Opts.AnalyzeSpecificFunction.find("(") != std::string::npos;
+
+ if (Ctx.getLangOpts().CPlusPlus && !HasBrackets) {
+ llvm::errs()
+ << "For analyzing C++ code you need to pass the function parameter "
+ "list: -analyze-function=\"foobar(int, _Bool)\"\n";
+ } else if (!Ctx.getLangOpts().CPlusPlus && HasBrackets) {
+ llvm::errs() << "For analyzing C code you shouldn't pass the function "
+ "parameter list, only the name of the function: "
+ "-analyze-function=foobar\n";
+ }
}
void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) {
@@ -538,52 +561,70 @@ void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) {
BR.FlushReports();
RecVisitorBR = nullptr;
+
+ // If the user wanted to analyze a specific function and the number of basic
+ // blocks analyzed is zero, than the user might not specified the function
+ // name correctly.
+ // FIXME: The user might have analyzed the requested function in Syntax mode,
+ // but we are unaware of that.
+ if (!Opts.AnalyzeSpecificFunction.empty() && NumFunctionsAnalyzed == 0)
+ reportAnalyzerFunctionMisuse(Opts, *Ctx);
}
void AnalysisConsumer::reportAnalyzerProgress(StringRef S) {
- if (Opts->AnalyzerDisplayProgress)
+ if (Opts.AnalyzerDisplayProgress)
llvm::errs() << S;
}
void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
-
// Don't run the actions if an error has occurred with parsing the file.
DiagnosticsEngine &Diags = PP.getDiagnostics();
if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
return;
- if (isBisonFile(C)) {
+ // Explicitly destroy the PathDiagnosticConsumer. This will flush its output.
+ // FIXME: This should be replaced with something that doesn't rely on
+ // side-effects in PathDiagnosticConsumer's destructor. This is required when
+ // used with option -disable-free.
+ const auto DiagFlusherScopeExit =
+ llvm::make_scope_exit([this] { Mgr.reset(); });
+
+ if (Opts.ShouldIgnoreBisonGeneratedFiles &&
+ fileContainsString("/* A Bison parser, made by", C)) {
reportAnalyzerProgress("Skipping bison-generated file\n");
- } else if (Opts->DisableAllCheckers) {
+ return;
+ }
- // Don't analyze if the user explicitly asked for no checks to be performed
- // on this file.
+ if (Opts.ShouldIgnoreFlexGeneratedFiles &&
+ fileContainsString("/* A lexical scanner generated by flex", C)) {
+ reportAnalyzerProgress("Skipping flex-generated file\n");
+ return;
+ }
+
+ // Don't analyze if the user explicitly asked for no checks to be performed
+ // on this file.
+ if (Opts.DisableAllCheckers) {
reportAnalyzerProgress("All checks are disabled using a supplied option\n");
- } else {
- // Otherwise, just run the analysis.
- runAnalysisOnTranslationUnit(C);
+ return;
}
+ // Otherwise, just run the analysis.
+ runAnalysisOnTranslationUnit(C);
+
// Count how many basic blocks we have not covered.
NumBlocksInAnalyzedFunctions = FunctionSummaries.getTotalNumBasicBlocks();
NumVisitedBlocksInAnalyzedFunctions =
FunctionSummaries.getTotalNumVisitedBasicBlocks();
if (NumBlocksInAnalyzedFunctions > 0)
PercentReachableBlocks =
- (FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) /
+ (FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) /
NumBlocksInAnalyzedFunctions;
-
- // Explicitly destroy the PathDiagnosticConsumer. This will flush its output.
- // FIXME: This should be replaced with something that doesn't rely on
- // side-effects in PathDiagnosticConsumer's destructor. This is required when
- // used with option -disable-free.
- Mgr.reset();
}
AnalysisConsumer::AnalysisMode
AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) {
- if (!Opts->AnalyzeSpecificFunction.empty() &&
- AnalysisDeclContext::getFunctionName(D) != Opts->AnalyzeSpecificFunction)
+ if (!Opts.AnalyzeSpecificFunction.empty() &&
+ AnalysisDeclContext::getFunctionName(D) != Opts.AnalyzeSpecificFunction)
return AM_None;
// Unless -analyze-all is specified, treat decls differently depending on
@@ -591,16 +632,24 @@ AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) {
// - Main source file: run both path-sensitive and non-path-sensitive checks.
// - Header files: run non-path-sensitive checks only.
// - System headers: don't run any checks.
- SourceManager &SM = Ctx->getSourceManager();
- const Stmt *Body = D->getBody();
- SourceLocation SL = Body ? Body->getBeginLoc() : D->getLocation();
- SL = SM.getExpansionLoc(SL);
-
- if (!Opts->AnalyzeAll && !Mgr->isInCodeFile(SL)) {
- if (SL.isInvalid() || SM.isInSystemHeader(SL))
- return AM_None;
+ if (Opts.AnalyzeAll)
+ return Mode;
+
+ const SourceManager &SM = Ctx->getSourceManager();
+
+ const SourceLocation Loc = [&SM](Decl *D) -> SourceLocation {
+ const Stmt *Body = D->getBody();
+ SourceLocation SL = Body ? Body->getBeginLoc() : D->getLocation();
+ return SM.getExpansionLoc(SL);
+ }(D);
+
+ // Ignore system headers.
+ if (Loc.isInvalid() || SM.isInSystemHeader(Loc))
+ return AM_None;
+
+ // Disable path sensitive analysis in user-headers.
+ if (!Mgr->isInCodeFile(Loc))
return Mode & ~AM_Path;
- }
return Mode;
}
@@ -708,8 +757,8 @@ ento::CreateAnalysisConsumer(CompilerInstance &CI) {
// Disable the effects of '-Werror' when using the AnalysisConsumer.
CI.getPreprocessor().getDiagnostics().setWarningsAsErrors(false);
- AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts();
- bool hasModelPath = analyzerOpts->Config.count("model-path") > 0;
+ AnalyzerOptions &analyzerOpts = CI.getAnalyzerOpts();
+ bool hasModelPath = analyzerOpts.Config.count("model-path") > 0;
return std::make_unique<AnalysisConsumer>(
CI, CI.getFrontendOpts().OutputFile, analyzerOpts,
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp
index eb6014a0629d..ea75c794f0b7 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp
@@ -30,18 +30,18 @@ void ento::printCheckerHelp(raw_ostream &out, CompilerInstance &CI) {
out << "USAGE: -analyzer-checker <CHECKER or PACKAGE,...>\n\n";
auto CheckerMgr = std::make_unique<CheckerManager>(
- *CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(),
+ CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(),
CI.getFrontendOpts().Plugins);
CheckerMgr->getCheckerRegistryData().printCheckerWithDescList(
- *CI.getAnalyzerOpts(), out);
+ CI.getAnalyzerOpts(), out);
}
void ento::printEnabledCheckerList(raw_ostream &out, CompilerInstance &CI) {
out << "OVERVIEW: Clang Static Analyzer Enabled Checkers List\n\n";
auto CheckerMgr = std::make_unique<CheckerManager>(
- *CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(),
+ CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(),
CI.getFrontendOpts().Plugins);
CheckerMgr->getCheckerRegistryData().printEnabledCheckerList(out);
@@ -50,11 +50,11 @@ void ento::printEnabledCheckerList(raw_ostream &out, CompilerInstance &CI) {
void ento::printCheckerConfigList(raw_ostream &out, CompilerInstance &CI) {
auto CheckerMgr = std::make_unique<CheckerManager>(
- *CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(),
+ CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(),
CI.getFrontendOpts().Plugins);
CheckerMgr->getCheckerRegistryData().printCheckerOptionList(
- *CI.getAnalyzerOpts(), out);
+ CI.getAnalyzerOpts(), out);
}
void ento::printAnalyzerConfigList(raw_ostream &out) {
@@ -101,10 +101,7 @@ OPTIONS:
#undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE
};
- llvm::sort(PrintableOptions, [](const OptionAndDescriptionTy &LHS,
- const OptionAndDescriptionTy &RHS) {
- return LHS.first < RHS.first;
- });
+ llvm::sort(PrintableOptions, llvm::less_first());
for (const auto &Pair : PrintableOptions) {
AnalyzerOptions::printFormattedEntry(out, Pair, /*InitialPad*/ 2,
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
index 528284ca8985..317df90a7781 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
@@ -15,7 +15,6 @@
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/DynamicLibrary.h"
@@ -234,7 +233,7 @@ void CheckerRegistry::initializeRegistry(const CheckerManager &Mgr) {
// 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);
+ return Tmp.contains(Checker);
};
for (const CheckerInfo &Checker : Data.Checkers) {
if (!Checker.isEnabled(Mgr))
@@ -311,8 +310,8 @@ template <bool IsWeak> void CheckerRegistry::resolveDependencies() {
"Failed to find the dependency of a checker!");
// We do allow diagnostics from unit test/example dependency checkers.
- assert((DependencyIt->FullName.startswith("test") ||
- DependencyIt->FullName.startswith("example") || IsWeak ||
+ assert((DependencyIt->FullName.starts_with("test") ||
+ DependencyIt->FullName.starts_with("example") || IsWeak ||
DependencyIt->IsHidden) &&
"Strong dependencies are modeling checkers, and as such "
"non-user facing! Mark them hidden in Checkers.td!");
@@ -480,9 +479,7 @@ static void isOptionContainedIn(const CmdLineOptionList &OptionList,
return Opt.OptionName == SuppliedOption;
};
- const auto *OptionIt = llvm::find_if(OptionList, SameOptName);
-
- if (OptionIt == OptionList.end()) {
+ if (llvm::none_of(OptionList, SameOptName)) {
Diags.Report(diag::err_analyzer_checker_option_unknown)
<< SuppliedChecker << SuppliedOption;
return;
@@ -528,4 +525,3 @@ void CheckerRegistry::validateCheckerOptions() const {
<< SuppliedCheckerOrPackage;
}
}
-
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp
index 276f7313b08f..0f1039d81d52 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp
@@ -28,11 +28,10 @@ using namespace ento;
ModelConsumer::ModelConsumer(llvm::StringMap<Stmt *> &Bodies)
: Bodies(Bodies) {}
-bool ModelConsumer::HandleTopLevelDecl(DeclGroupRef D) {
- for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) {
-
+bool ModelConsumer::HandleTopLevelDecl(DeclGroupRef DeclGroup) {
+ for (const Decl *D : DeclGroup) {
// Only interested in definitions.
- const FunctionDecl *func = llvm::dyn_cast<FunctionDecl>(*I);
+ const auto *func = llvm::dyn_cast<FunctionDecl>(D);
if (func && func->hasBody()) {
Bodies.insert(std::make_pair(func->getName(), func->getBody()));
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp
index 7baae6778ebd..ae11fbbe32b7 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp
@@ -48,8 +48,7 @@ void ModelInjector::onBodySynthesis(const NamedDecl *D) {
SourceManager &SM = CI.getSourceManager();
FileID mainFileID = SM.getMainFileID();
- AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts();
- llvm::StringRef modelPath = analyzerOpts->ModelPath;
+ llvm::StringRef modelPath = CI.getAnalyzerOpts().ModelPath;
llvm::SmallString<128> fileName;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h
index d2016c3b112c..4db26028362f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h
@@ -29,10 +29,7 @@
namespace clang {
class CompilerInstance;
-class ASTUnit;
-class ASTReader;
class NamedDecl;
-class Module;
namespace ento {
class ModelInjector : public CodeInjector {