Skip to content

Commit 0f88cae

Browse files
author
Adam Balogh
committed
[Analyzer] Checker for Debugging Iterator Checkers
For white-box testing correct container and iterator modelling it is essential to access the internal data structures stored for container and iterators. This patch introduces a simple debug checkers called debug.IteratorDebugging to achieve this. Differential Revision: https://reviews.llvm.org/D67156
1 parent 7b9f540 commit 0f88cae

File tree

3 files changed

+221
-2
lines changed

3 files changed

+221
-2
lines changed

clang/include/clang/StaticAnalyzer/Checkers/Checkers.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,6 +1343,11 @@ def ReportStmts : Checker<"ReportStmts">,
13431343
HelpText<"Emits a warning for every statement.">,
13441344
Documentation<NotDocumented>;
13451345

1346+
def DebugIteratorModeling : Checker<"DebugIteratorModeling">,
1347+
HelpText<"Check the analyzer's understanding of C++ iterators">,
1348+
Dependencies<[IteratorModeling]>,
1349+
Documentation<NotDocumented>;
1350+
13461351
} // end "debug"
13471352

13481353

clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp

Lines changed: 155 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,12 @@ struct ContainerData {
173173
class IteratorChecker
174174
: public Checker<check::PreCall, check::PostCall,
175175
check::PostStmt<MaterializeTemporaryExpr>, check::Bind,
176-
check::LiveSymbols, check::DeadSymbols> {
176+
check::LiveSymbols, check::DeadSymbols, eval::Call> {
177177

178178
std::unique_ptr<BugType> OutOfRangeBugType;
179179
std::unique_ptr<BugType> MismatchedBugType;
180180
std::unique_ptr<BugType> InvalidatedBugType;
181+
std::unique_ptr<BugType> DebugMsgBugType;
181182

182183
void handleComparison(CheckerContext &C, const Expr *CE, const SVal &RetVal,
183184
const SVal &LVal, const SVal &RVal,
@@ -236,14 +237,43 @@ class IteratorChecker
236237
ExplodedNode *ErrNode) const;
237238
void reportInvalidatedBug(const StringRef &Message, const SVal &Val,
238239
CheckerContext &C, ExplodedNode *ErrNode) const;
239-
240+
template <typename Getter>
241+
void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C,
242+
Getter get) const;
243+
void analyzerContainerBegin(const CallExpr *CE, CheckerContext &C) const;
244+
void analyzerContainerEnd(const CallExpr *CE, CheckerContext &C) const;
245+
template <typename Getter>
246+
void analyzerIteratorDataField(const CallExpr *CE, CheckerContext &C,
247+
Getter get, SVal Default) const;
248+
void analyzerIteratorPosition(const CallExpr *CE, CheckerContext &C) const;
249+
void analyzerIteratorContainer(const CallExpr *CE, CheckerContext &C) const;
250+
void analyzerIteratorValidity(const CallExpr *CE, CheckerContext &C) const;
251+
ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const;
252+
253+
typedef void (IteratorChecker::*FnCheck)(const CallExpr *,
254+
CheckerContext &) const;
255+
256+
CallDescriptionMap<FnCheck> Callbacks = {
257+
{{0, "clang_analyzer_container_begin", 1},
258+
&IteratorChecker::analyzerContainerBegin},
259+
{{0, "clang_analyzer_container_end", 1},
260+
&IteratorChecker::analyzerContainerEnd},
261+
{{0, "clang_analyzer_iterator_position", 1},
262+
&IteratorChecker::analyzerIteratorPosition},
263+
{{0, "clang_analyzer_iterator_container", 1},
264+
&IteratorChecker::analyzerIteratorContainer},
265+
{{0, "clang_analyzer_iterator_validity", 1},
266+
&IteratorChecker::analyzerIteratorValidity},
267+
};
268+
240269
public:
241270
IteratorChecker();
242271

243272
enum CheckKind {
244273
CK_IteratorRangeChecker,
245274
CK_MismatchedIteratorChecker,
246275
CK_InvalidatedIteratorChecker,
276+
CK_DebugIteratorModeling,
247277
CK_NumCheckKinds
248278
};
249279

@@ -259,6 +289,7 @@ class IteratorChecker
259289
CheckerContext &C) const;
260290
void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
261291
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
292+
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
262293
};
263294
} // namespace
264295

@@ -362,6 +393,9 @@ IteratorChecker::IteratorChecker() {
362393
/*SuppressOnSink=*/true));
363394
InvalidatedBugType.reset(
364395
new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
396+
DebugMsgBugType.reset(
397+
new BugType(this, "Checking analyzer assumptions", "debug",
398+
/*SuppressOnSink=*/true));
365399
}
366400

367401
void IteratorChecker::checkPreCall(const CallEvent &Call,
@@ -1627,6 +1661,124 @@ void IteratorChecker::reportInvalidatedBug(const StringRef &Message,
16271661
C.emitReport(std::move(R));
16281662
}
16291663

1664+
bool IteratorChecker::evalCall(const CallEvent &Call,
1665+
CheckerContext &C) const {
1666+
if (!ChecksEnabled[CK_DebugIteratorModeling])
1667+
return false;
1668+
1669+
const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1670+
if (!CE)
1671+
return false;
1672+
1673+
const FnCheck *Handler = Callbacks.lookup(Call);
1674+
if (!Handler)
1675+
return false;
1676+
1677+
(this->**Handler)(CE, C);
1678+
return true;
1679+
}
1680+
1681+
template <typename Getter>
1682+
void IteratorChecker::analyzerContainerDataField(const CallExpr *CE,
1683+
CheckerContext &C,
1684+
Getter get) const {
1685+
if (CE->getNumArgs() == 0) {
1686+
reportDebugMsg("Missing container argument", C);
1687+
return;
1688+
}
1689+
1690+
auto State = C.getState();
1691+
const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
1692+
if (Cont) {
1693+
const auto *Data = getContainerData(State, Cont);
1694+
if (Data) {
1695+
SymbolRef Field = get(Data);
1696+
if (Field) {
1697+
State = State->BindExpr(CE, C.getLocationContext(),
1698+
nonloc::SymbolVal(Field));
1699+
C.addTransition(State);
1700+
return;
1701+
}
1702+
}
1703+
}
1704+
1705+
auto &BVF = C.getSValBuilder().getBasicValueFactory();
1706+
State = State->BindExpr(CE, C.getLocationContext(),
1707+
nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
1708+
}
1709+
1710+
void IteratorChecker::analyzerContainerBegin(const CallExpr *CE,
1711+
CheckerContext &C) const {
1712+
analyzerContainerDataField(CE, C, [](const ContainerData *D) {
1713+
return D->getBegin();
1714+
});
1715+
}
1716+
1717+
void IteratorChecker::analyzerContainerEnd(const CallExpr *CE,
1718+
CheckerContext &C) const {
1719+
analyzerContainerDataField(CE, C, [](const ContainerData *D) {
1720+
return D->getEnd();
1721+
});
1722+
}
1723+
1724+
template <typename Getter>
1725+
void IteratorChecker::analyzerIteratorDataField(const CallExpr *CE,
1726+
CheckerContext &C,
1727+
Getter get,
1728+
SVal Default) const {
1729+
if (CE->getNumArgs() == 0) {
1730+
reportDebugMsg("Missing iterator argument", C);
1731+
return;
1732+
}
1733+
1734+
auto State = C.getState();
1735+
SVal V = C.getSVal(CE->getArg(0));
1736+
const auto *Pos = getIteratorPosition(State, V);
1737+
if (Pos) {
1738+
State = State->BindExpr(CE, C.getLocationContext(), get(Pos));
1739+
} else {
1740+
State = State->BindExpr(CE, C.getLocationContext(), Default);
1741+
}
1742+
C.addTransition(State);
1743+
}
1744+
1745+
void IteratorChecker::analyzerIteratorPosition(const CallExpr *CE,
1746+
CheckerContext &C) const {
1747+
auto &BVF = C.getSValBuilder().getBasicValueFactory();
1748+
analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
1749+
return nonloc::SymbolVal(P->getOffset());
1750+
}, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
1751+
}
1752+
1753+
void IteratorChecker::analyzerIteratorContainer(const CallExpr *CE,
1754+
CheckerContext &C) const {
1755+
auto &BVF = C.getSValBuilder().getBasicValueFactory();
1756+
analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
1757+
return loc::MemRegionVal(P->getContainer());
1758+
}, loc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
1759+
}
1760+
1761+
void IteratorChecker::analyzerIteratorValidity(const CallExpr *CE,
1762+
CheckerContext &C) const {
1763+
auto &BVF = C.getSValBuilder().getBasicValueFactory();
1764+
analyzerIteratorDataField(CE, C, [&BVF](const IteratorPosition *P) {
1765+
return
1766+
nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get((P->isValid()))));
1767+
}, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
1768+
}
1769+
1770+
ExplodedNode *IteratorChecker::reportDebugMsg(llvm::StringRef Msg,
1771+
CheckerContext &C) const {
1772+
ExplodedNode *N = C.generateNonFatalErrorNode();
1773+
if (!N)
1774+
return nullptr;
1775+
1776+
auto &BR = C.getBugReporter();
1777+
BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
1778+
Msg, N));
1779+
return N;
1780+
}
1781+
16301782
namespace {
16311783

16321784
bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
@@ -2388,3 +2540,4 @@ bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) {
23882540
REGISTER_CHECKER(IteratorRangeChecker)
23892541
REGISTER_CHECKER(MismatchedIteratorChecker)
23902542
REGISTER_CHECKER(InvalidatedIteratorChecker)
2543+
REGISTER_CHECKER(DebugIteratorModeling)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// RUN: %clang_analyze_cc1 -std=c++11\
2+
// RUN: -analyzer-checker=core,cplusplus\
3+
// RUN: -analyzer-checker=debug.DebugIteratorModeling,debug.ExprInspection\
4+
// RUN: -analyzer-config aggressive-binary-operation-simplification=true\
5+
// RUN: -analyzer-config c++-container-inlining=false %s -verify
6+
7+
// RUN: %clang_analyze_cc1 -std=c++11\
8+
// RUN: -analyzer-checker=core,cplusplus\
9+
// RUN: -analyzer-checker=debug.DebugIteratorModeling,debug.ExprInspection\
10+
// RUN: -analyzer-config aggressive-binary-operation-simplification=true\
11+
// RUN: -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify
12+
13+
#include "Inputs/system-header-simulator-cxx.h"
14+
15+
template <typename Container>
16+
long clang_analyzer_container_begin(const Container&);
17+
template <typename Container>
18+
long clang_analyzer_container_end(const Container&);
19+
template <typename Iterator>
20+
long clang_analyzer_iterator_position(const Iterator&);
21+
template <typename Iterator>
22+
void* clang_analyzer_iterator_container(const Iterator&);
23+
template <typename Iterator>
24+
bool clang_analyzer_iterator_validity(const Iterator&);
25+
void clang_analyzer_denote(long, const char*);
26+
void clang_analyzer_express(long);
27+
void clang_analyzer_dump(const void*);
28+
void clang_analyzer_eval(bool);
29+
30+
void iterator_position(const std::vector<int> v0) {
31+
auto b0 = v0.begin(), e0 = v0.end();
32+
33+
clang_analyzer_denote(clang_analyzer_iterator_position(b0), "$b0");
34+
clang_analyzer_denote(clang_analyzer_iterator_position(e0), "$e0");
35+
36+
clang_analyzer_express(clang_analyzer_iterator_position(b0)); // expected-warning{{$b0}}
37+
clang_analyzer_express(clang_analyzer_iterator_position(e0)); // expected-warning{{$e0}}
38+
39+
clang_analyzer_express(clang_analyzer_container_begin(v0)); // expected-warning{{$b0}}
40+
clang_analyzer_express(clang_analyzer_container_end(v0)); // expected-warning{{$e0}}
41+
42+
++b0;
43+
44+
clang_analyzer_express(clang_analyzer_iterator_position(b0)); // expected-warning{{$b0 + 1}}
45+
}
46+
47+
void iterator_container(const std::vector<int> v0) {
48+
auto b0 = v0.begin();
49+
50+
clang_analyzer_dump(&v0); //expected-warning{{&v0}}
51+
clang_analyzer_eval(clang_analyzer_iterator_container(b0) == &v0); // expected-warning{{TRUE}}
52+
}
53+
54+
void iterator_validity(std::vector<int> v0) {
55+
auto b0 = v0.begin();
56+
clang_analyzer_eval(clang_analyzer_iterator_validity(b0)); //expected-warning{{TRUE}}
57+
58+
v0.clear();
59+
60+
clang_analyzer_eval(clang_analyzer_iterator_validity(b0)); //expected-warning{{FALSE}}
61+
}

0 commit comments

Comments
 (0)