Iterator modeling depends on container modeling, but not vice versa. This enables the possibility to arrange these two modeling checkers into separate layers. There are several advantages for doing this: the first one is that this way we can keep the respective modeling checkers moderately simple and small. Furthermore, this enables creation of checkers on container operations which only depend on the container modeling. Thus iterator modeling can be disabled together with the iterator checkers if they are not needed. Since many container operations also affect iterators, container modeling also uses the iterator library: it creates iterator positions upon calling the `begin()` or `end()` method of a containter (but propagation of the abstract position is left to the iterator modeling), shifts or invalidates iterators according to the rules upon calling a container modifier and rebinds the iterator to a new container upon `std::move()`. Iterator modeling propagates the abstract iterator position, handles the relations between iterator positions and models iterator operations such as increments and decrements. Differential Revision: https://reviews.llvm.org/D73547
145 lines
5.1 KiB
C++
145 lines
5.1 KiB
C++
//===-- DebugIteratorModeling.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 debugging iterator modeling.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
|
|
|
#include "Iterator.h"
|
|
|
|
using namespace clang;
|
|
using namespace ento;
|
|
using namespace iterator;
|
|
|
|
namespace {
|
|
|
|
class DebugIteratorModeling
|
|
: public Checker<eval::Call> {
|
|
|
|
std::unique_ptr<BugType> DebugMsgBugType;
|
|
|
|
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 (DebugIteratorModeling::*FnCheck)(const CallExpr *,
|
|
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},
|
|
};
|
|
|
|
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());
|
|
if (!CE)
|
|
return false;
|
|
|
|
const FnCheck *Handler = Callbacks.lookup(Call);
|
|
if (!Handler)
|
|
return false;
|
|
|
|
(this->**Handler)(CE, C);
|
|
return true;
|
|
}
|
|
|
|
template <typename Getter>
|
|
void DebugIteratorModeling::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 DebugIteratorModeling::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 DebugIteratorModeling::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 DebugIteratorModeling::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 *DebugIteratorModeling::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;
|
|
}
|
|
|
|
void ento::registerDebugIteratorModeling(CheckerManager &mgr) {
|
|
mgr.registerChecker<DebugIteratorModeling>();
|
|
}
|
|
|
|
bool ento::shouldRegisterDebugIteratorModeling(const LangOptions &LO) {
|
|
return true;
|
|
}
|