Skip to content

Commit afb13af

Browse files
author
Adam Balogh
committed
[Analyzer][NFC] Iterator Checkers - Separate iterator modeling and the actual checkers
A monolithic checker class is hard to maintain. This patch splits it up into a modeling part, the three checkers and a debug checker. The common functions are moved into a library. Differential Revision: https://reviews.llvm.org/D70320
1 parent 170ee64 commit afb13af

8 files changed

+1442
-1144
lines changed

clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ add_clang_library(clangStaticAnalyzerCheckers
2828
CXXSelfAssignmentChecker.cpp
2929
DeadStoresChecker.cpp
3030
DebugCheckers.cpp
31+
DebugIteratorModeling.cpp
3132
DeleteWithNonVirtualDtorChecker.cpp
3233
DereferenceChecker.cpp
3334
DirectIvarAssignment.cpp
@@ -42,7 +43,10 @@ add_clang_library(clangStaticAnalyzerCheckers
4243
GTestChecker.cpp
4344
IdenticalExprChecker.cpp
4445
InnerPointerChecker.cpp
45-
IteratorChecker.cpp
46+
InvalidatedIteratorChecker.cpp
47+
Iterator.cpp
48+
IteratorModeling.cpp
49+
IteratorRangeChecker.cpp
4650
IvarInvalidationChecker.cpp
4751
LLVMConventionsChecker.cpp
4852
LocalizationChecker.cpp
@@ -51,6 +55,7 @@ add_clang_library(clangStaticAnalyzerCheckers
5155
MallocChecker.cpp
5256
MallocOverflowSecurityChecker.cpp
5357
MallocSizeofChecker.cpp
58+
MismatchedIteratorChecker.cpp
5459
MmapWriteExecChecker.cpp
5560
MIGChecker.cpp
5661
MoveChecker.cpp
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
//===-- DebugIteratorModeling.cpp ---------------------------------*- C++ -*--//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Defines a checker for debugging iterator modeling.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14+
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
15+
#include "clang/StaticAnalyzer/Core/Checker.h"
16+
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
17+
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
18+
19+
#include "Iterator.h"
20+
21+
using namespace clang;
22+
using namespace ento;
23+
using namespace iterator;
24+
25+
namespace {
26+
27+
class DebugIteratorModeling
28+
: public Checker<eval::Call> {
29+
30+
std::unique_ptr<BugType> DebugMsgBugType;
31+
32+
template <typename Getter>
33+
void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C,
34+
Getter get) const;
35+
void analyzerContainerBegin(const CallExpr *CE, CheckerContext &C) const;
36+
void analyzerContainerEnd(const CallExpr *CE, CheckerContext &C) const;
37+
template <typename Getter>
38+
void analyzerIteratorDataField(const CallExpr *CE, CheckerContext &C,
39+
Getter get, SVal Default) const;
40+
void analyzerIteratorPosition(const CallExpr *CE, CheckerContext &C) const;
41+
void analyzerIteratorContainer(const CallExpr *CE, CheckerContext &C) const;
42+
void analyzerIteratorValidity(const CallExpr *CE, CheckerContext &C) const;
43+
ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const;
44+
45+
typedef void (DebugIteratorModeling::*FnCheck)(const CallExpr *,
46+
CheckerContext &) const;
47+
48+
CallDescriptionMap<FnCheck> Callbacks = {
49+
{{0, "clang_analyzer_container_begin", 1},
50+
&DebugIteratorModeling::analyzerContainerBegin},
51+
{{0, "clang_analyzer_container_end", 1},
52+
&DebugIteratorModeling::analyzerContainerEnd},
53+
{{0, "clang_analyzer_iterator_position", 1},
54+
&DebugIteratorModeling::analyzerIteratorPosition},
55+
{{0, "clang_analyzer_iterator_container", 1},
56+
&DebugIteratorModeling::analyzerIteratorContainer},
57+
{{0, "clang_analyzer_iterator_validity", 1},
58+
&DebugIteratorModeling::analyzerIteratorValidity},
59+
};
60+
61+
public:
62+
DebugIteratorModeling();
63+
64+
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
65+
};
66+
67+
} //namespace
68+
69+
DebugIteratorModeling::DebugIteratorModeling() {
70+
DebugMsgBugType.reset(
71+
new BugType(this, "Checking analyzer assumptions", "debug",
72+
/*SuppressOnSink=*/true));
73+
}
74+
75+
bool DebugIteratorModeling::evalCall(const CallEvent &Call,
76+
CheckerContext &C) const {
77+
const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
78+
if (!CE)
79+
return false;
80+
81+
const FnCheck *Handler = Callbacks.lookup(Call);
82+
if (!Handler)
83+
return false;
84+
85+
(this->**Handler)(CE, C);
86+
return true;
87+
}
88+
89+
template <typename Getter>
90+
void DebugIteratorModeling::analyzerContainerDataField(const CallExpr *CE,
91+
CheckerContext &C,
92+
Getter get) const {
93+
if (CE->getNumArgs() == 0) {
94+
reportDebugMsg("Missing container argument", C);
95+
return;
96+
}
97+
98+
auto State = C.getState();
99+
const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
100+
if (Cont) {
101+
const auto *Data = getContainerData(State, Cont);
102+
if (Data) {
103+
SymbolRef Field = get(Data);
104+
if (Field) {
105+
State = State->BindExpr(CE, C.getLocationContext(),
106+
nonloc::SymbolVal(Field));
107+
C.addTransition(State);
108+
return;
109+
}
110+
}
111+
}
112+
113+
auto &BVF = C.getSValBuilder().getBasicValueFactory();
114+
State = State->BindExpr(CE, C.getLocationContext(),
115+
nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
116+
}
117+
118+
void DebugIteratorModeling::analyzerContainerBegin(const CallExpr *CE,
119+
CheckerContext &C) const {
120+
analyzerContainerDataField(CE, C, [](const ContainerData *D) {
121+
return D->getBegin();
122+
});
123+
}
124+
125+
void DebugIteratorModeling::analyzerContainerEnd(const CallExpr *CE,
126+
CheckerContext &C) const {
127+
analyzerContainerDataField(CE, C, [](const ContainerData *D) {
128+
return D->getEnd();
129+
});
130+
}
131+
132+
template <typename Getter>
133+
void DebugIteratorModeling::analyzerIteratorDataField(const CallExpr *CE,
134+
CheckerContext &C,
135+
Getter get,
136+
SVal Default) const {
137+
if (CE->getNumArgs() == 0) {
138+
reportDebugMsg("Missing iterator argument", C);
139+
return;
140+
}
141+
142+
auto State = C.getState();
143+
SVal V = C.getSVal(CE->getArg(0));
144+
const auto *Pos = getIteratorPosition(State, V);
145+
if (Pos) {
146+
State = State->BindExpr(CE, C.getLocationContext(), get(Pos));
147+
} else {
148+
State = State->BindExpr(CE, C.getLocationContext(), Default);
149+
}
150+
C.addTransition(State);
151+
}
152+
153+
void DebugIteratorModeling::analyzerIteratorPosition(const CallExpr *CE,
154+
CheckerContext &C) const {
155+
auto &BVF = C.getSValBuilder().getBasicValueFactory();
156+
analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
157+
return nonloc::SymbolVal(P->getOffset());
158+
}, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
159+
}
160+
161+
void DebugIteratorModeling::analyzerIteratorContainer(const CallExpr *CE,
162+
CheckerContext &C) const {
163+
auto &BVF = C.getSValBuilder().getBasicValueFactory();
164+
analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
165+
return loc::MemRegionVal(P->getContainer());
166+
}, loc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
167+
}
168+
169+
void DebugIteratorModeling::analyzerIteratorValidity(const CallExpr *CE,
170+
CheckerContext &C) const {
171+
auto &BVF = C.getSValBuilder().getBasicValueFactory();
172+
analyzerIteratorDataField(CE, C, [&BVF](const IteratorPosition *P) {
173+
return
174+
nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get((P->isValid()))));
175+
}, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
176+
}
177+
178+
ExplodedNode *DebugIteratorModeling::reportDebugMsg(llvm::StringRef Msg,
179+
CheckerContext &C) const {
180+
ExplodedNode *N = C.generateNonFatalErrorNode();
181+
if (!N)
182+
return nullptr;
183+
184+
auto &BR = C.getBugReporter();
185+
BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
186+
Msg, N));
187+
return N;
188+
}
189+
190+
void ento::registerDebugIteratorModeling(CheckerManager &mgr) {
191+
mgr.registerChecker<DebugIteratorModeling>();
192+
}
193+
194+
bool ento::shouldRegisterDebugIteratorModeling(const LangOptions &LO) {
195+
return true;
196+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//===-- InvalidatedIteratorChecker.cpp ----------------------------*- C++ -*--//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Defines a checker for access of invalidated iterators.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14+
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
15+
#include "clang/StaticAnalyzer/Core/Checker.h"
16+
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
17+
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
18+
19+
20+
#include "Iterator.h"
21+
22+
using namespace clang;
23+
using namespace ento;
24+
using namespace iterator;
25+
26+
namespace {
27+
28+
class InvalidatedIteratorChecker
29+
: public Checker<check::PreCall> {
30+
31+
std::unique_ptr<BugType> InvalidatedBugType;
32+
33+
void verifyAccess(CheckerContext &C, const SVal &Val) const;
34+
void reportBug(const StringRef &Message, const SVal &Val,
35+
CheckerContext &C, ExplodedNode *ErrNode) const;
36+
public:
37+
InvalidatedIteratorChecker();
38+
39+
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
40+
41+
};
42+
43+
} //namespace
44+
45+
InvalidatedIteratorChecker::InvalidatedIteratorChecker() {
46+
InvalidatedBugType.reset(
47+
new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
48+
}
49+
50+
void InvalidatedIteratorChecker::checkPreCall(const CallEvent &Call,
51+
CheckerContext &C) const {
52+
// Check for access of invalidated position
53+
const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
54+
if (!Func)
55+
return;
56+
57+
if (Func->isOverloadedOperator() &&
58+
isAccessOperator(Func->getOverloadedOperator())) {
59+
// Check for any kind of access of invalidated iterator positions
60+
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
61+
verifyAccess(C, InstCall->getCXXThisVal());
62+
} else {
63+
verifyAccess(C, Call.getArgSVal(0));
64+
}
65+
}
66+
}
67+
68+
void InvalidatedIteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const {
69+
auto State = C.getState();
70+
const auto *Pos = getIteratorPosition(State, Val);
71+
if (Pos && !Pos->isValid()) {
72+
auto *N = C.generateErrorNode(State);
73+
if (!N) {
74+
return;
75+
}
76+
reportBug("Invalidated iterator accessed.", Val, C, N);
77+
}
78+
}
79+
80+
void InvalidatedIteratorChecker::reportBug(const StringRef &Message,
81+
const SVal &Val, CheckerContext &C,
82+
ExplodedNode *ErrNode) const {
83+
auto R = std::make_unique<PathSensitiveBugReport>(*InvalidatedBugType,
84+
Message, ErrNode);
85+
R->markInteresting(Val);
86+
C.emitReport(std::move(R));
87+
}
88+
89+
void ento::registerInvalidatedIteratorChecker(CheckerManager &mgr) {
90+
mgr.registerChecker<InvalidatedIteratorChecker>();
91+
}
92+
93+
bool ento::shouldRegisterInvalidatedIteratorChecker(const LangOptions &LO) {
94+
return true;
95+
}

0 commit comments

Comments
 (0)