1218887Sdim//== ArrayBoundChecker.cpp ------------------------------*- C++ -*--==// 2218887Sdim// 3218887Sdim// The LLVM Compiler Infrastructure 4218887Sdim// 5218887Sdim// This file is distributed under the University of Illinois Open Source 6218887Sdim// License. See LICENSE.TXT for details. 7218887Sdim// 8218887Sdim//===----------------------------------------------------------------------===// 9218887Sdim// 10218887Sdim// This file defines ArrayBoundChecker, which is a path-sensitive check 11218887Sdim// which looks for an out-of-bound array element access. 12218887Sdim// 13218887Sdim//===----------------------------------------------------------------------===// 14218887Sdim 15219077Sdim#include "ClangSACheckers.h" 16249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 17221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 18219077Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h" 19219077Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 20218887Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 21218887Sdim 22218887Sdimusing namespace clang; 23218887Sdimusing namespace ento; 24218887Sdim 25218887Sdimnamespace { 26218887Sdimclass ArrayBoundChecker : 27221345Sdim public Checker<check::Location> { 28234353Sdim mutable OwningPtr<BuiltinBug> BT; 29218887Sdimpublic: 30226633Sdim void checkLocation(SVal l, bool isLoad, const Stmt* S, 31226633Sdim CheckerContext &C) const; 32218887Sdim}; 33218887Sdim} 34218887Sdim 35226633Sdimvoid ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS, 36219077Sdim CheckerContext &C) const { 37218887Sdim // Check for out of bound array element access. 38218887Sdim const MemRegion *R = l.getAsRegion(); 39218887Sdim if (!R) 40218887Sdim return; 41218887Sdim 42218887Sdim const ElementRegion *ER = dyn_cast<ElementRegion>(R); 43218887Sdim if (!ER) 44218887Sdim return; 45218887Sdim 46218887Sdim // Get the index of the accessed element. 47249423Sdim DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); 48218887Sdim 49218887Sdim // Zero index is always in bound, this also passes ElementRegions created for 50218887Sdim // pointer casts. 51218887Sdim if (Idx.isZeroConstant()) 52218887Sdim return; 53218887Sdim 54234353Sdim ProgramStateRef state = C.getState(); 55218887Sdim 56218887Sdim // Get the size of the array. 57218887Sdim DefinedOrUnknownSVal NumElements 58218887Sdim = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), 59218887Sdim ER->getValueType()); 60218887Sdim 61234353Sdim ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true); 62234353Sdim ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false); 63218887Sdim if (StOutBound && !StInBound) { 64218887Sdim ExplodedNode *N = C.generateSink(StOutBound); 65218887Sdim if (!N) 66218887Sdim return; 67218887Sdim 68218887Sdim if (!BT) 69219077Sdim BT.reset(new BuiltinBug("Out-of-bound array access", 70219077Sdim "Access out-of-bound array element (buffer overflow)")); 71218887Sdim 72218887Sdim // FIXME: It would be nice to eventually make this diagnostic more clear, 73218887Sdim // e.g., by referencing the original declaration or by saying *why* this 74218887Sdim // reference is outside the range. 75218887Sdim 76218887Sdim // Generate a report for this bug. 77226633Sdim BugReport *report = 78226633Sdim new BugReport(*BT, BT->getDescription(), N); 79218887Sdim 80226633Sdim report->addRange(LoadS->getSourceRange()); 81243830Sdim C.emitReport(report); 82218887Sdim return; 83218887Sdim } 84218887Sdim 85218887Sdim // Array bound check succeeded. From this point forward the array bound 86218887Sdim // should always succeed. 87218887Sdim C.addTransition(StInBound); 88218887Sdim} 89219077Sdim 90219077Sdimvoid ento::registerArrayBoundChecker(CheckerManager &mgr) { 91219077Sdim mgr.registerChecker<ArrayBoundChecker>(); 92219077Sdim} 93