mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-15 12:39:19 +00:00
[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
This commit is contained in:
parent
7b9f5401a6
commit
0f88caeef8
@ -1343,6 +1343,11 @@ def ReportStmts : Checker<"ReportStmts">,
|
||||
HelpText<"Emits a warning for every statement.">,
|
||||
Documentation<NotDocumented>;
|
||||
|
||||
def DebugIteratorModeling : Checker<"DebugIteratorModeling">,
|
||||
HelpText<"Check the analyzer's understanding of C++ iterators">,
|
||||
Dependencies<[IteratorModeling]>,
|
||||
Documentation<NotDocumented>;
|
||||
|
||||
} // end "debug"
|
||||
|
||||
|
||||
|
@ -173,11 +173,12 @@ public:
|
||||
class IteratorChecker
|
||||
: public Checker<check::PreCall, check::PostCall,
|
||||
check::PostStmt<MaterializeTemporaryExpr>, check::Bind,
|
||||
check::LiveSymbols, check::DeadSymbols> {
|
||||
check::LiveSymbols, check::DeadSymbols, eval::Call> {
|
||||
|
||||
std::unique_ptr<BugType> OutOfRangeBugType;
|
||||
std::unique_ptr<BugType> MismatchedBugType;
|
||||
std::unique_ptr<BugType> InvalidatedBugType;
|
||||
std::unique_ptr<BugType> DebugMsgBugType;
|
||||
|
||||
void handleComparison(CheckerContext &C, const Expr *CE, const SVal &RetVal,
|
||||
const SVal &LVal, const SVal &RVal,
|
||||
@ -236,7 +237,35 @@ class IteratorChecker
|
||||
ExplodedNode *ErrNode) const;
|
||||
void reportInvalidatedBug(const StringRef &Message, const SVal &Val,
|
||||
CheckerContext &C, ExplodedNode *ErrNode) const;
|
||||
template <typename Getter>
|
||||
void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C,
|
||||
Getter get) const;
|
||||
void analyzerContainerBegin(const CallExpr *CE, CheckerContext &C) const;
|
||||
void analyzerContainerEnd(const CallExpr *CE, CheckerContext &C) const;
|
||||
template <typename Getter>
|
||||
void analyzerIteratorDataField(const CallExpr *CE, CheckerContext &C,
|
||||
Getter get, SVal Default) const;
|
||||
void analyzerIteratorPosition(const CallExpr *CE, CheckerContext &C) const;
|
||||
void analyzerIteratorContainer(const CallExpr *CE, CheckerContext &C) const;
|
||||
void analyzerIteratorValidity(const CallExpr *CE, CheckerContext &C) const;
|
||||
ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const;
|
||||
|
||||
typedef void (IteratorChecker::*FnCheck)(const CallExpr *,
|
||||
CheckerContext &) const;
|
||||
|
||||
CallDescriptionMap<FnCheck> Callbacks = {
|
||||
{{0, "clang_analyzer_container_begin", 1},
|
||||
&IteratorChecker::analyzerContainerBegin},
|
||||
{{0, "clang_analyzer_container_end", 1},
|
||||
&IteratorChecker::analyzerContainerEnd},
|
||||
{{0, "clang_analyzer_iterator_position", 1},
|
||||
&IteratorChecker::analyzerIteratorPosition},
|
||||
{{0, "clang_analyzer_iterator_container", 1},
|
||||
&IteratorChecker::analyzerIteratorContainer},
|
||||
{{0, "clang_analyzer_iterator_validity", 1},
|
||||
&IteratorChecker::analyzerIteratorValidity},
|
||||
};
|
||||
|
||||
public:
|
||||
IteratorChecker();
|
||||
|
||||
@ -244,6 +273,7 @@ public:
|
||||
CK_IteratorRangeChecker,
|
||||
CK_MismatchedIteratorChecker,
|
||||
CK_InvalidatedIteratorChecker,
|
||||
CK_DebugIteratorModeling,
|
||||
CK_NumCheckKinds
|
||||
};
|
||||
|
||||
@ -259,6 +289,7 @@ public:
|
||||
CheckerContext &C) const;
|
||||
void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
|
||||
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
|
||||
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -362,6 +393,9 @@ IteratorChecker::IteratorChecker() {
|
||||
/*SuppressOnSink=*/true));
|
||||
InvalidatedBugType.reset(
|
||||
new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
|
||||
DebugMsgBugType.reset(
|
||||
new BugType(this, "Checking analyzer assumptions", "debug",
|
||||
/*SuppressOnSink=*/true));
|
||||
}
|
||||
|
||||
void IteratorChecker::checkPreCall(const CallEvent &Call,
|
||||
@ -1627,6 +1661,124 @@ void IteratorChecker::reportInvalidatedBug(const StringRef &Message,
|
||||
C.emitReport(std::move(R));
|
||||
}
|
||||
|
||||
bool IteratorChecker::evalCall(const CallEvent &Call,
|
||||
CheckerContext &C) const {
|
||||
if (!ChecksEnabled[CK_DebugIteratorModeling])
|
||||
return false;
|
||||
|
||||
const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
|
||||
if (!CE)
|
||||
return false;
|
||||
|
||||
const FnCheck *Handler = Callbacks.lookup(Call);
|
||||
if (!Handler)
|
||||
return false;
|
||||
|
||||
(this->**Handler)(CE, C);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Getter>
|
||||
void IteratorChecker::analyzerContainerDataField(const CallExpr *CE,
|
||||
CheckerContext &C,
|
||||
Getter get) const {
|
||||
if (CE->getNumArgs() == 0) {
|
||||
reportDebugMsg("Missing container argument", C);
|
||||
return;
|
||||
}
|
||||
|
||||
auto State = C.getState();
|
||||
const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
|
||||
if (Cont) {
|
||||
const auto *Data = getContainerData(State, Cont);
|
||||
if (Data) {
|
||||
SymbolRef Field = get(Data);
|
||||
if (Field) {
|
||||
State = State->BindExpr(CE, C.getLocationContext(),
|
||||
nonloc::SymbolVal(Field));
|
||||
C.addTransition(State);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto &BVF = C.getSValBuilder().getBasicValueFactory();
|
||||
State = State->BindExpr(CE, C.getLocationContext(),
|
||||
nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
|
||||
}
|
||||
|
||||
void IteratorChecker::analyzerContainerBegin(const CallExpr *CE,
|
||||
CheckerContext &C) const {
|
||||
analyzerContainerDataField(CE, C, [](const ContainerData *D) {
|
||||
return D->getBegin();
|
||||
});
|
||||
}
|
||||
|
||||
void IteratorChecker::analyzerContainerEnd(const CallExpr *CE,
|
||||
CheckerContext &C) const {
|
||||
analyzerContainerDataField(CE, C, [](const ContainerData *D) {
|
||||
return D->getEnd();
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Getter>
|
||||
void IteratorChecker::analyzerIteratorDataField(const CallExpr *CE,
|
||||
CheckerContext &C,
|
||||
Getter get,
|
||||
SVal Default) const {
|
||||
if (CE->getNumArgs() == 0) {
|
||||
reportDebugMsg("Missing iterator argument", C);
|
||||
return;
|
||||
}
|
||||
|
||||
auto State = C.getState();
|
||||
SVal V = C.getSVal(CE->getArg(0));
|
||||
const auto *Pos = getIteratorPosition(State, V);
|
||||
if (Pos) {
|
||||
State = State->BindExpr(CE, C.getLocationContext(), get(Pos));
|
||||
} else {
|
||||
State = State->BindExpr(CE, C.getLocationContext(), Default);
|
||||
}
|
||||
C.addTransition(State);
|
||||
}
|
||||
|
||||
void IteratorChecker::analyzerIteratorPosition(const CallExpr *CE,
|
||||
CheckerContext &C) const {
|
||||
auto &BVF = C.getSValBuilder().getBasicValueFactory();
|
||||
analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
|
||||
return nonloc::SymbolVal(P->getOffset());
|
||||
}, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
|
||||
}
|
||||
|
||||
void IteratorChecker::analyzerIteratorContainer(const CallExpr *CE,
|
||||
CheckerContext &C) const {
|
||||
auto &BVF = C.getSValBuilder().getBasicValueFactory();
|
||||
analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
|
||||
return loc::MemRegionVal(P->getContainer());
|
||||
}, loc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
|
||||
}
|
||||
|
||||
void IteratorChecker::analyzerIteratorValidity(const CallExpr *CE,
|
||||
CheckerContext &C) const {
|
||||
auto &BVF = C.getSValBuilder().getBasicValueFactory();
|
||||
analyzerIteratorDataField(CE, C, [&BVF](const IteratorPosition *P) {
|
||||
return
|
||||
nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get((P->isValid()))));
|
||||
}, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
|
||||
}
|
||||
|
||||
ExplodedNode *IteratorChecker::reportDebugMsg(llvm::StringRef Msg,
|
||||
CheckerContext &C) const {
|
||||
ExplodedNode *N = C.generateNonFatalErrorNode();
|
||||
if (!N)
|
||||
return nullptr;
|
||||
|
||||
auto &BR = C.getBugReporter();
|
||||
BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
|
||||
Msg, N));
|
||||
return N;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
|
||||
@ -2388,3 +2540,4 @@ bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) {
|
||||
REGISTER_CHECKER(IteratorRangeChecker)
|
||||
REGISTER_CHECKER(MismatchedIteratorChecker)
|
||||
REGISTER_CHECKER(InvalidatedIteratorChecker)
|
||||
REGISTER_CHECKER(DebugIteratorModeling)
|
||||
|
61
clang/test/Analysis/debug-iterator-modeling.cpp
Normal file
61
clang/test/Analysis/debug-iterator-modeling.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
// RUN: %clang_analyze_cc1 -std=c++11\
|
||||
// RUN: -analyzer-checker=core,cplusplus\
|
||||
// RUN: -analyzer-checker=debug.DebugIteratorModeling,debug.ExprInspection\
|
||||
// RUN: -analyzer-config aggressive-binary-operation-simplification=true\
|
||||
// RUN: -analyzer-config c++-container-inlining=false %s -verify
|
||||
|
||||
// RUN: %clang_analyze_cc1 -std=c++11\
|
||||
// RUN: -analyzer-checker=core,cplusplus\
|
||||
// RUN: -analyzer-checker=debug.DebugIteratorModeling,debug.ExprInspection\
|
||||
// RUN: -analyzer-config aggressive-binary-operation-simplification=true\
|
||||
// RUN: -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify
|
||||
|
||||
#include "Inputs/system-header-simulator-cxx.h"
|
||||
|
||||
template <typename Container>
|
||||
long clang_analyzer_container_begin(const Container&);
|
||||
template <typename Container>
|
||||
long clang_analyzer_container_end(const Container&);
|
||||
template <typename Iterator>
|
||||
long clang_analyzer_iterator_position(const Iterator&);
|
||||
template <typename Iterator>
|
||||
void* clang_analyzer_iterator_container(const Iterator&);
|
||||
template <typename Iterator>
|
||||
bool clang_analyzer_iterator_validity(const Iterator&);
|
||||
void clang_analyzer_denote(long, const char*);
|
||||
void clang_analyzer_express(long);
|
||||
void clang_analyzer_dump(const void*);
|
||||
void clang_analyzer_eval(bool);
|
||||
|
||||
void iterator_position(const std::vector<int> v0) {
|
||||
auto b0 = v0.begin(), e0 = v0.end();
|
||||
|
||||
clang_analyzer_denote(clang_analyzer_iterator_position(b0), "$b0");
|
||||
clang_analyzer_denote(clang_analyzer_iterator_position(e0), "$e0");
|
||||
|
||||
clang_analyzer_express(clang_analyzer_iterator_position(b0)); // expected-warning{{$b0}}
|
||||
clang_analyzer_express(clang_analyzer_iterator_position(e0)); // expected-warning{{$e0}}
|
||||
|
||||
clang_analyzer_express(clang_analyzer_container_begin(v0)); // expected-warning{{$b0}}
|
||||
clang_analyzer_express(clang_analyzer_container_end(v0)); // expected-warning{{$e0}}
|
||||
|
||||
++b0;
|
||||
|
||||
clang_analyzer_express(clang_analyzer_iterator_position(b0)); // expected-warning{{$b0 + 1}}
|
||||
}
|
||||
|
||||
void iterator_container(const std::vector<int> v0) {
|
||||
auto b0 = v0.begin();
|
||||
|
||||
clang_analyzer_dump(&v0); //expected-warning{{&v0}}
|
||||
clang_analyzer_eval(clang_analyzer_iterator_container(b0) == &v0); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
void iterator_validity(std::vector<int> v0) {
|
||||
auto b0 = v0.begin();
|
||||
clang_analyzer_eval(clang_analyzer_iterator_validity(b0)); //expected-warning{{TRUE}}
|
||||
|
||||
v0.clear();
|
||||
|
||||
clang_analyzer_eval(clang_analyzer_iterator_validity(b0)); //expected-warning{{FALSE}}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user