123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- //===-- IteratorRangeChecker.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 dereference of the past-the-end iterator and
- // out-of-range increments and decrements.
- //
- //===----------------------------------------------------------------------===//
- #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
- #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
- #include "clang/StaticAnalyzer/Core/Checker.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.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 IteratorRangeChecker
- : public Checker<check::PreCall, check::PreStmt<UnaryOperator>,
- check::PreStmt<BinaryOperator>,
- check::PreStmt<ArraySubscriptExpr>,
- check::PreStmt<MemberExpr>> {
- std::unique_ptr<BugType> OutOfRangeBugType;
- void verifyDereference(CheckerContext &C, SVal Val) const;
- void verifyIncrement(CheckerContext &C, SVal Iter) const;
- void verifyDecrement(CheckerContext &C, SVal Iter) const;
- void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
- SVal LHS, SVal RHS) const;
- void verifyAdvance(CheckerContext &C, SVal LHS, SVal RHS) const;
- void verifyPrev(CheckerContext &C, SVal LHS, SVal RHS) const;
- void verifyNext(CheckerContext &C, SVal LHS, SVal RHS) const;
- void reportBug(const StringRef &Message, SVal Val, CheckerContext &C,
- ExplodedNode *ErrNode) const;
- public:
- IteratorRangeChecker();
- void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
- void checkPreStmt(const UnaryOperator *UO, CheckerContext &C) const;
- void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const;
- void checkPreStmt(const ArraySubscriptExpr *ASE, CheckerContext &C) const;
- void checkPreStmt(const MemberExpr *ME, CheckerContext &C) const;
- using AdvanceFn = void (IteratorRangeChecker::*)(CheckerContext &, SVal,
- SVal) const;
- CallDescriptionMap<AdvanceFn> AdvanceFunctions = {
- {{{"std", "advance"}, 2}, &IteratorRangeChecker::verifyAdvance},
- {{{"std", "prev"}, 2}, &IteratorRangeChecker::verifyPrev},
- {{{"std", "next"}, 2}, &IteratorRangeChecker::verifyNext},
- };
- };
- bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
- bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos);
- bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
- bool isZero(ProgramStateRef State, const NonLoc &Val);
- } //namespace
- IteratorRangeChecker::IteratorRangeChecker() {
- OutOfRangeBugType.reset(
- new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
- }
- void IteratorRangeChecker::checkPreCall(const CallEvent &Call,
- CheckerContext &C) const {
- // Check for out of range access
- const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
- if (!Func)
- return;
- if (Func->isOverloadedOperator()) {
- if (isIncrementOperator(Func->getOverloadedOperator())) {
- // Check for out-of-range incrementions
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- verifyIncrement(C, InstCall->getCXXThisVal());
- } else {
- if (Call.getNumArgs() >= 1) {
- verifyIncrement(C, Call.getArgSVal(0));
- }
- }
- } else if (isDecrementOperator(Func->getOverloadedOperator())) {
- // Check for out-of-range decrementions
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- verifyDecrement(C, InstCall->getCXXThisVal());
- } else {
- if (Call.getNumArgs() >= 1) {
- verifyDecrement(C, Call.getArgSVal(0));
- }
- }
- } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- // Check for out-of-range incrementions and decrementions
- if (Call.getNumArgs() >= 1 &&
- Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
- verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
- InstCall->getCXXThisVal(),
- Call.getArgSVal(0));
- }
- } else {
- if (Call.getNumArgs() >= 2 &&
- Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
- verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
- Call.getArgSVal(0), Call.getArgSVal(1));
- }
- }
- } else if (isDereferenceOperator(Func->getOverloadedOperator())) {
- // Check for dereference of out-of-range iterators
- if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- verifyDereference(C, InstCall->getCXXThisVal());
- } else {
- verifyDereference(C, Call.getArgSVal(0));
- }
- }
- } else {
- const AdvanceFn *Verifier = AdvanceFunctions.lookup(Call);
- if (Verifier) {
- if (Call.getNumArgs() > 1) {
- (this->**Verifier)(C, Call.getArgSVal(0), Call.getArgSVal(1));
- } else {
- auto &BVF = C.getSValBuilder().getBasicValueFactory();
- (this->**Verifier)(
- C, Call.getArgSVal(0),
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
- }
- }
- }
- }
- void IteratorRangeChecker::checkPreStmt(const UnaryOperator *UO,
- CheckerContext &C) const {
- if (isa<CXXThisExpr>(UO->getSubExpr()))
- return;
- ProgramStateRef State = C.getState();
- UnaryOperatorKind OK = UO->getOpcode();
- SVal SubVal = State->getSVal(UO->getSubExpr(), C.getLocationContext());
- if (isDereferenceOperator(OK)) {
- verifyDereference(C, SubVal);
- } else if (isIncrementOperator(OK)) {
- verifyIncrement(C, SubVal);
- } else if (isDecrementOperator(OK)) {
- verifyDecrement(C, SubVal);
- }
- }
- void IteratorRangeChecker::checkPreStmt(const BinaryOperator *BO,
- CheckerContext &C) const {
- ProgramStateRef State = C.getState();
- BinaryOperatorKind OK = BO->getOpcode();
- SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext());
- if (isDereferenceOperator(OK)) {
- verifyDereference(C, LVal);
- } else if (isRandomIncrOrDecrOperator(OK)) {
- SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext());
- if (!BO->getRHS()->getType()->isIntegralOrEnumerationType())
- return;
- verifyRandomIncrOrDecr(C, BinaryOperator::getOverloadedOperator(OK), LVal,
- RVal);
- }
- }
- void IteratorRangeChecker::checkPreStmt(const ArraySubscriptExpr *ASE,
- CheckerContext &C) const {
- ProgramStateRef State = C.getState();
- SVal LVal = State->getSVal(ASE->getLHS(), C.getLocationContext());
- verifyDereference(C, LVal);
- }
- void IteratorRangeChecker::checkPreStmt(const MemberExpr *ME,
- CheckerContext &C) const {
- if (!ME->isArrow() || ME->isImplicitAccess())
- return;
- ProgramStateRef State = C.getState();
- SVal BaseVal = State->getSVal(ME->getBase(), C.getLocationContext());
- verifyDereference(C, BaseVal);
- }
- void IteratorRangeChecker::verifyDereference(CheckerContext &C,
- SVal Val) const {
- auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, Val);
- if (Pos && isPastTheEnd(State, *Pos)) {
- auto *N = C.generateErrorNode(State);
- if (!N)
- return;
- reportBug("Past-the-end iterator dereferenced.", Val, C, N);
- return;
- }
- }
- void IteratorRangeChecker::verifyIncrement(CheckerContext &C, SVal Iter) const {
- auto &BVF = C.getSValBuilder().getBasicValueFactory();
- verifyRandomIncrOrDecr(C, OO_Plus, Iter,
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
- }
- void IteratorRangeChecker::verifyDecrement(CheckerContext &C, SVal Iter) const {
- auto &BVF = C.getSValBuilder().getBasicValueFactory();
- verifyRandomIncrOrDecr(C, OO_Minus, Iter,
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
- }
- void IteratorRangeChecker::verifyRandomIncrOrDecr(CheckerContext &C,
- OverloadedOperatorKind Op,
- SVal LHS, SVal RHS) const {
- auto State = C.getState();
- auto Value = RHS;
- if (auto ValAsLoc = RHS.getAs<Loc>()) {
- Value = State->getRawSVal(*ValAsLoc);
- }
- if (Value.isUnknownOrUndef())
- return;
- // Incremention or decremention by 0 is never a bug.
- if (isZero(State, Value.castAs<NonLoc>()))
- return;
- // The result may be the past-end iterator of the container, but any other
- // out of range position is undefined behaviour
- auto StateAfter = advancePosition(State, LHS, Op, Value);
- if (!StateAfter)
- return;
- const auto *PosAfter = getIteratorPosition(StateAfter, LHS);
- assert(PosAfter &&
- "Iterator should have position after successful advancement");
- if (isAheadOfRange(State, *PosAfter)) {
- auto *N = C.generateErrorNode(State);
- if (!N)
- return;
- reportBug("Iterator decremented ahead of its valid range.", LHS,
- C, N);
- }
- if (isBehindPastTheEnd(State, *PosAfter)) {
- auto *N = C.generateErrorNode(State);
- if (!N)
- return;
- reportBug("Iterator incremented behind the past-the-end "
- "iterator.", LHS, C, N);
- }
- }
- void IteratorRangeChecker::verifyAdvance(CheckerContext &C, SVal LHS,
- SVal RHS) const {
- verifyRandomIncrOrDecr(C, OO_PlusEqual, LHS, RHS);
- }
- void IteratorRangeChecker::verifyPrev(CheckerContext &C, SVal LHS,
- SVal RHS) const {
- verifyRandomIncrOrDecr(C, OO_Minus, LHS, RHS);
- }
- void IteratorRangeChecker::verifyNext(CheckerContext &C, SVal LHS,
- SVal RHS) const {
- verifyRandomIncrOrDecr(C, OO_Plus, LHS, RHS);
- }
- void IteratorRangeChecker::reportBug(const StringRef &Message, SVal Val,
- CheckerContext &C,
- ExplodedNode *ErrNode) const {
- auto R = std::make_unique<PathSensitiveBugReport>(*OutOfRangeBugType, Message,
- ErrNode);
- const auto *Pos = getIteratorPosition(C.getState(), Val);
- assert(Pos && "Iterator without known position cannot be out-of-range.");
- R->markInteresting(Val);
- R->markInteresting(Pos->getContainer());
- C.emitReport(std::move(R));
- }
- namespace {
- bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
- bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
- bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
- bool isZero(ProgramStateRef State, const NonLoc &Val) {
- auto &BVF = State->getBasicVals();
- return compare(State, Val,
- nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
- BO_EQ);
- }
- bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
- const auto *Cont = Pos.getContainer();
- const auto *CData = getContainerData(State, Cont);
- if (!CData)
- return false;
- const auto End = CData->getEnd();
- if (End) {
- if (isEqual(State, Pos.getOffset(), End)) {
- return true;
- }
- }
- return false;
- }
- bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
- const auto *Cont = Pos.getContainer();
- const auto *CData = getContainerData(State, Cont);
- if (!CData)
- return false;
- const auto Beg = CData->getBegin();
- if (Beg) {
- if (isLess(State, Pos.getOffset(), Beg)) {
- return true;
- }
- }
- return false;
- }
- bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
- const auto *Cont = Pos.getContainer();
- const auto *CData = getContainerData(State, Cont);
- if (!CData)
- return false;
- const auto End = CData->getEnd();
- if (End) {
- if (isGreater(State, Pos.getOffset(), End)) {
- return true;
- }
- }
- return false;
- }
- bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
- return compare(State, Sym1, Sym2, BO_LT);
- }
- bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
- return compare(State, Sym1, Sym2, BO_GT);
- }
- bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
- return compare(State, Sym1, Sym2, BO_EQ);
- }
- } // namespace
- void ento::registerIteratorRangeChecker(CheckerManager &mgr) {
- mgr.registerChecker<IteratorRangeChecker>();
- }
- bool ento::shouldRegisterIteratorRangeChecker(const CheckerManager &mgr) {
- return true;
- }
|