Index: lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -27,8 +27,8 @@ using namespace ento; namespace { -class ArrayBoundCheckerV2 : - public Checker { +class ArrayBoundCheckerV2 + : public Checker> { mutable std::unique_ptr BT; enum OOB_Kind { OOB_Precedes, OOB_Excedes, OOB_Tainted }; @@ -37,7 +37,8 @@ OOB_Kind kind) const; public: - void checkLocation(SVal l, bool isLoad, const Stmt*S, + void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const; + void checkLocation(SVal l, bool isLoad, const Stmt *S, CheckerContext &C) const; }; @@ -64,7 +65,10 @@ void dump() const; void dumpToStream(raw_ostream &os) const; }; -} +} // namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(VariableLengthArrayExtent, + const VariableArrayType *, NonLoc); static SVal computeExtentBegin(SValBuilder &svalBuilder, const MemRegion *region) { @@ -111,8 +115,46 @@ return std::pair(offset, extent); } +void ArrayBoundCheckerV2::checkPreStmt(const DeclStmt *DS, + CheckerContext &checkerContext) const { + const VarDecl *VD = dyn_cast(DS->getSingleDecl()); + if (!VD) + return; + + ASTContext &Ctx = checkerContext.getASTContext(); + const VariableArrayType *VLA = Ctx.getAsVariableArrayType(VD->getType()); + if (!VLA) + return; + + SVal VLASize = checkerContext.getSVal(VLA->getSizeExpr()); + + Optional Sz = VLASize.getAs(); + if (!Sz) + return; + + ProgramStateRef state = checkerContext.getState(); + checkerContext.addTransition( + state->set(VLA, Sz.getValue())); +} + +static const VariableArrayType *getVLAFromExtent(DefinedOrUnknownSVal extentVal, + ASTContext &Ctx) { + if (extentVal.getSubKind() != nonloc::SymbolValKind) + return nullptr; + + SymbolRef SR = extentVal.castAs().getSymbol(); + const SymbolExtent *SE = dyn_cast(SR); + const MemRegion *SEMR = SE->getRegion(); + if (SEMR->getKind() != MemRegion::VarRegionKind) + return nullptr; + + const VarRegion *VR = cast(SEMR); + QualType T = VR->getDecl()->getType(); + return Ctx.getAsVariableArrayType(T); +} + void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, - const Stmt* LoadS, + const Stmt *LoadS, CheckerContext &checkerContext) const { // NOTE: Instead of using ProgramState::assumeInBound(), we are prototyping @@ -175,13 +217,21 @@ } do { + // CHECK UPPER BOUND: Is byteOffset >= extent(baseRegion)? If so, // we are doing a load/store after the last valid offset. DefinedOrUnknownSVal extentVal = - rawOffset.getRegion()->getExtent(svalBuilder); + rawOffset.getRegion()->getExtent(svalBuilder); if (!extentVal.getAs()) break; + if (const VariableArrayType *VLA = + getVLAFromExtent(extentVal, checkerContext.getASTContext())) { + const NonLoc *V = state->get(VLA); + if (V) + extentVal = *V; + } + if (extentVal.getAs()) { std::pair simplifiedOffsets = getSimplifiedOffsets(rawOffset.getByteOffset(), Index: test/Analysis/out-of-bounds.c =================================================================== --- test/Analysis/out-of-bounds.c +++ test/Analysis/out-of-bounds.c @@ -174,3 +174,7 @@ clang_analyzer_eval(x <= 99); // expected-warning{{TRUE}} } +void vla(int X) { + char buf[X]; + buf[X] = 0; // expected-warning {{Out of bound memory access (access exceeds upper limit of memory block)}} +}