#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 MismatchedIteratorChecker
: public Checker<check::PreCall, check::PreStmt<BinaryOperator>> {
std::unique_ptr<BugType> MismatchedBugType;
void verifyMatch(CheckerContext &C, const SVal &Iter,
const MemRegion *Cont) const;
void verifyMatch(CheckerContext &C, const SVal &Iter1,
const SVal &Iter2) const;
void reportBug(const StringRef &Message, const SVal &Val1,
const SVal &Val2, CheckerContext &C,
ExplodedNode *ErrNode) const;
void reportBug(const StringRef &Message, const SVal &Val,
const MemRegion *Reg, CheckerContext &C,
ExplodedNode *ErrNode) const;
public:
MismatchedIteratorChecker();
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const;
};
}
MismatchedIteratorChecker::MismatchedIteratorChecker() {
MismatchedBugType.reset(
new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs",
true));
}
void MismatchedIteratorChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
if (!Func)
return;
if (Func->isOverloadedOperator() &&
isComparisonOperator(Func->getOverloadedOperator())) {
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
if (Call.getNumArgs() < 1)
return;
if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) ||
!isIteratorType(Call.getArgExpr(0)->getType()))
return;
verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
} else {
if (Call.getNumArgs() < 2)
return;
if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
!isIteratorType(Call.getArgExpr(1)->getType()))
return;
verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
}
} else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
const auto *ContReg = InstCall->getCXXThisVal().getAsRegion();
if (!ContReg)
return;
if (isEraseCall(Func) || isEraseAfterCall(Func)) {
verifyMatch(C, Call.getArgSVal(0),
InstCall->getCXXThisVal().getAsRegion());
if (Call.getNumArgs() == 2) {
verifyMatch(C, Call.getArgSVal(1),
InstCall->getCXXThisVal().getAsRegion());
}
} else if (isInsertCall(Func)) {
verifyMatch(C, Call.getArgSVal(0),
InstCall->getCXXThisVal().getAsRegion());
if (Call.getNumArgs() == 3 &&
isIteratorType(Call.getArgExpr(1)->getType()) &&
isIteratorType(Call.getArgExpr(2)->getType())) {
verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2));
}
} else if (isEmplaceCall(Func)) {
verifyMatch(C, Call.getArgSVal(0),
InstCall->getCXXThisVal().getAsRegion());
}
} else if (isa<CXXConstructorCall>(&Call)) {
if (Call.getNumArgs() < 2)
return;
const auto *Ctr = cast<CXXConstructorDecl>(Call.getDecl());
if (Ctr->getNumParams() < 2)
return;
if (Ctr->getParamDecl(0)->getName() != "first" ||
Ctr->getParamDecl(1)->getName() != "last")
return;
if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
!isIteratorType(Call.getArgExpr(1)->getType()))
return;
verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
} else {
const auto *Templ = Func->getPrimaryTemplate();
if (!Templ)
return;
const auto *TParams = Templ->getTemplateParameters();
const auto *TArgs = Func->getTemplateSpecializationArgs();
for (size_t I = 0; I < TParams->size(); ++I) {
const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I));
if (!TPDecl)
continue;
if (TPDecl->isParameterPack())
continue;
const auto TAType = TArgs->get(I).getAsType();
if (!isIteratorType(TAType))
continue;
SVal LHS = UndefinedVal();
for (auto J = 0U; J < Func->getNumParams(); ++J) {
const auto *Param = Func->getParamDecl(J);
const auto *ParamType =
Param->getType()->getAs<SubstTemplateTypeParmType>();
if (!ParamType ||
ParamType->getReplacedParameter()->getDecl() != TPDecl)
continue;
if (LHS.isUndef()) {
LHS = Call.getArgSVal(J);
} else {
verifyMatch(C, LHS, Call.getArgSVal(J));
}
}
}
}
}
void MismatchedIteratorChecker::checkPreStmt(const BinaryOperator *BO,
CheckerContext &C) const {
if (!BO->isComparisonOp())
return;
ProgramStateRef State = C.getState();
SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext());
SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext());
verifyMatch(C, LVal, RVal);
}
void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
const MemRegion *Cont) const {
Cont = Cont->getMostDerivedObjectRegion();
if (const auto *ContSym = Cont->getSymbolicBase()) {
if (isa<SymbolConjured>(ContSym->getSymbol()))
return;
}
auto State = C.getState();
const auto *Pos = getIteratorPosition(State, Iter);
if (!Pos)
return;
const auto *IterCont = Pos->getContainer();
if (const auto *ContSym = IterCont->getSymbolicBase()) {
if (isa<SymbolConjured>(ContSym->getSymbol()))
return;
}
if (IterCont != Cont) {
auto *N = C.generateNonFatalErrorNode(State);
if (!N) {
return;
}
reportBug("Container accessed using foreign iterator argument.",
Iter, Cont, C, N);
}
}
void MismatchedIteratorChecker::verifyMatch(CheckerContext &C,
const SVal &Iter1,
const SVal &Iter2) const {
auto State = C.getState();
const auto *Pos1 = getIteratorPosition(State, Iter1);
if (!Pos1)
return;
const auto *IterCont1 = Pos1->getContainer();
if (const auto *ContSym = IterCont1->getSymbolicBase()) {
if (isa<SymbolConjured>(ContSym->getSymbol()))
return;
}
const auto *Pos2 = getIteratorPosition(State, Iter2);
if (!Pos2)
return;
const auto *IterCont2 = Pos2->getContainer();
if (const auto *ContSym = IterCont2->getSymbolicBase()) {
if (isa<SymbolConjured>(ContSym->getSymbol()))
return;
}
if (IterCont1 != IterCont2) {
auto *N = C.generateNonFatalErrorNode(State);
if (!N)
return;
reportBug("Iterators of different containers used where the "
"same container is expected.", Iter1, Iter2, C, N);
}
}
void MismatchedIteratorChecker::reportBug(const StringRef &Message,
const SVal &Val1,
const SVal &Val2,
CheckerContext &C,
ExplodedNode *ErrNode) const {
auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
ErrNode);
R->markInteresting(Val1);
R->markInteresting(Val2);
C.emitReport(std::move(R));
}
void MismatchedIteratorChecker::reportBug(const StringRef &Message,
const SVal &Val, const MemRegion *Reg,
CheckerContext &C,
ExplodedNode *ErrNode) const {
auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
ErrNode);
R->markInteresting(Val);
R->markInteresting(Reg);
C.emitReport(std::move(R));
}
void ento::registerMismatchedIteratorChecker(CheckerManager &mgr) {
mgr.registerChecker<MismatchedIteratorChecker>();
}
bool ento::shouldRegisterMismatchedIteratorChecker(const CheckerManager &mgr) {
return true;
}