baloghadamsoftware created this revision.
baloghadamsoftware added reviewers: NoQ, Szelethus.
baloghadamsoftware added a project: clang.
Herald added subscribers: Charusso, donat.nagy, mikhail.ramalho, a.sidorin,
rnkovacs, szepet, xazax.hun.
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 two simple debug checkers called `debug.ContainerInspection`
and `debug.IteratorInspection` to achieve this.
Repository:
rC Clang
https://reviews.llvm.org/D67156
Files:
include/clang/StaticAnalyzer/Checkers/Checkers.td
lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
test/Analysis/iterator-inspection.cpp
Index: test/Analysis/iterator-inspection.cpp
===================================================================
--- /dev/null
+++ test/Analysis/iterator-inspection.cpp
@@ -0,0 +1,52 @@
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,debug.ContainerInspection,debug.IteratorInspection,debug.ExprInspection -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=false %s -verify
+// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,debug.ContainerInspection,debug.IteratorInspection,debug.ExprInspection -analyzer-config aggressive-binary-operation-simplification=true -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_dump(clang_analyzer_iterator_container(b0)); //expected-warning{{&v0}}
+}
+
+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}}
+}
Index: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
+++ lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
@@ -173,11 +173,12 @@
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,6 +237,21 @@
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 &C) const;
public:
IteratorChecker();
@@ -244,6 +260,8 @@
CK_IteratorRangeChecker,
CK_MismatchedIteratorChecker,
CK_InvalidatedIteratorChecker,
+ CK_ContainerInspectionChecker,
+ CK_IteratorInspectionChecker,
CK_NumCheckKinds
};
@@ -259,6 +277,7 @@
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
@@ -364,6 +383,9 @@
InvalidatedBugType.reset(
new BugType(this, "Iterator invalidated", "Misuse of STL APIs",
/*SuppressOnSink=*/true));
+ DebugMsgBugType.reset(
+ new BugType(this, "Checking analyzer assumptions", "debug",
+ /*SuppressOnSink=*/true));
}
void IteratorChecker::checkPreCall(const CallEvent &Call,
@@ -1625,6 +1647,140 @@
C.emitReport(std::move(R));
}
+bool IteratorChecker::evalCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return false;
+
+ FnCheck Handler = nullptr;
+ if (ChecksEnabled[CK_ContainerInspectionChecker]) {
+ Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
+ .Case("clang_analyzer_container_begin",
+ &IteratorChecker::analyzerContainerBegin)
+ .Case("clang_analyzer_container_end",
+ &IteratorChecker::analyzerContainerEnd)
+ .Default(nullptr);
+ }
+
+ if (!Handler && ChecksEnabled[CK_IteratorInspectionChecker]) {
+ Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
+ .Case("clang_analyzer_iterator_position",
+ &IteratorChecker::analyzerIteratorPosition)
+ .Case("clang_analyzer_iterator_container",
+ &IteratorChecker::analyzerIteratorContainer)
+ .Case("clang_analyzer_iterator_validity",
+ &IteratorChecker::analyzerIteratorValidity)
+ .Default(nullptr);
+ }
+
+ 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<BugReport>(*DebugMsgBugType, Msg, N));
+ return N;
+}
+
namespace {
bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
@@ -2388,3 +2544,5 @@
REGISTER_CHECKER(IteratorRangeChecker)
REGISTER_CHECKER(MismatchedIteratorChecker)
REGISTER_CHECKER(InvalidatedIteratorChecker)
+REGISTER_CHECKER(ContainerInspectionChecker)
+REGISTER_CHECKER(IteratorInspectionChecker)
Index: include/clang/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -1325,6 +1325,16 @@
HelpText<"Emits a warning for every statement.">,
Documentation<NotDocumented>;
+def ContainerInspectionChecker : Checker<"ContainerInspection">,
+ HelpText<"Check the analyzer's understanding of C++ containers">,
+ Dependencies<[IteratorModeling]>,
+ Documentation<NotDocumented>;
+
+def IteratorInspectionChecker : Checker<"IteratorInspection">,
+ HelpText<"Check the analyzer's understanding of C++ iterators">,
+ Dependencies<[IteratorModeling]>,
+ Documentation<NotDocumented>;
+
} // end "debug"
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits