Home | History | Annotate | Download | only in Checkers
      1 //== ArrayBoundChecker.cpp ------------------------------*- C++ -*--==//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file defines ArrayBoundChecker, which is a path-sensitive check
     11 // which looks for an out-of-bound array element access.
     12 //
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "ClangSACheckers.h"
     16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
     17 #include "clang/StaticAnalyzer/Core/Checker.h"
     18 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
     19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
     20 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
     21 
     22 using namespace clang;
     23 using namespace ento;
     24 
     25 namespace {
     26 class ArrayBoundChecker :
     27     public Checker<check::Location> {
     28   mutable OwningPtr<BuiltinBug> BT;
     29 public:
     30   void checkLocation(SVal l, bool isLoad, const Stmt* S,
     31                      CheckerContext &C) const;
     32 };
     33 }
     34 
     35 void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS,
     36                                       CheckerContext &C) const {
     37   // Check for out of bound array element access.
     38   const MemRegion *R = l.getAsRegion();
     39   if (!R)
     40     return;
     41 
     42   const ElementRegion *ER = dyn_cast<ElementRegion>(R);
     43   if (!ER)
     44     return;
     45 
     46   // Get the index of the accessed element.
     47   DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
     48 
     49   // Zero index is always in bound, this also passes ElementRegions created for
     50   // pointer casts.
     51   if (Idx.isZeroConstant())
     52     return;
     53 
     54   ProgramStateRef state = C.getState();
     55 
     56   // Get the size of the array.
     57   DefinedOrUnknownSVal NumElements
     58     = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(),
     59                                             ER->getValueType());
     60 
     61   ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true);
     62   ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false);
     63   if (StOutBound && !StInBound) {
     64     ExplodedNode *N = C.generateSink(StOutBound);
     65     if (!N)
     66       return;
     67 
     68     if (!BT)
     69       BT.reset(new BuiltinBug("Out-of-bound array access",
     70                        "Access out-of-bound array element (buffer overflow)"));
     71 
     72     // FIXME: It would be nice to eventually make this diagnostic more clear,
     73     // e.g., by referencing the original declaration or by saying *why* this
     74     // reference is outside the range.
     75 
     76     // Generate a report for this bug.
     77     BugReport *report =
     78       new BugReport(*BT, BT->getDescription(), N);
     79 
     80     report->addRange(LoadS->getSourceRange());
     81     C.emitReport(report);
     82     return;
     83   }
     84 
     85   // Array bound check succeeded.  From this point forward the array bound
     86   // should always succeed.
     87   C.addTransition(StInBound);
     88 }
     89 
     90 void ento::registerArrayBoundChecker(CheckerManager &mgr) {
     91   mgr.registerChecker<ArrayBoundChecker>();
     92 }
     93