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; +def ContainerInspectionChecker : Checker<"ContainerInspection">, + HelpText<"Check the analyzer's understanding of C++ containers">, + Dependencies<[IteratorModeling]>, + Documentation; + +def IteratorInspectionChecker : Checker<"IteratorInspection">, + HelpText<"Check the analyzer's understanding of C++ iterators">, + Dependencies<[IteratorModeling]>, + Documentation; + } // end "debug" 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::Bind, - check::LiveSymbols, check::DeadSymbols> { + check::LiveSymbols, check::DeadSymbols, eval::Call> { std::unique_ptr OutOfRangeBugType; std::unique_ptr MismatchedBugType; std::unique_ptr InvalidatedBugType; + std::unique_ptr 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 + 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 + 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(Call.getOriginExpr()); + if (!CE) + return false; + + FnCheck Handler = nullptr; + if (ChecksEnabled[CK_ContainerInspectionChecker]) { + Handler = llvm::StringSwitch(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(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 +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 +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(*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: 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 +long clang_analyzer_container_begin(const Container&); +template +long clang_analyzer_container_end(const Container&); +template +long clang_analyzer_iterator_position(const Iterator&); +template +void* clang_analyzer_iterator_container(const Iterator&); +template +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 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 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 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}} +}