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 std::unique_ptr<BuiltinBug> BT;
     29 
     30 public:
     31   void checkLocation(SVal l, bool isLoad, const Stmt* S,
     32                      CheckerContext &C) const;
     33 };
     34 }
     35 
     36 void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS,
     37                                       CheckerContext &C) const {
     38   // Check for out of bound array element access.
     39   const MemRegion *R = l.getAsRegion();
     40   if (!R)
     41     return;
     42 
     43   const ElementRegion *ER = dyn_cast<ElementRegion>(R);
     44   if (!ER)
     45     return;
     46 
     47   // Get the index of the accessed element.
     48   DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
     49 
     50   // Zero index is always in bound, this also passes ElementRegions created for
     51   // pointer casts.
     52   if (Idx.isZeroConstant())
     53     return;
     54 
     55   ProgramStateRef state = C.getState();
     56 
     57   // Get the size of the array.
     58   DefinedOrUnknownSVal NumElements
     59     = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(),
     60                                             ER->getValueType());
     61 
     62   ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true);
     63   ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false);
     64   if (StOutBound && !StInBound) {
     65     ExplodedNode *N = C.generateSink(StOutBound);
     66     if (!N)
     67       return;
     68 
     69     if (!BT)
     70       BT.reset(new BuiltinBug(
     71           this, "Out-of-bound array access",
     72           "Access out-of-bound array element (buffer overflow)"));
     73 
     74     // FIXME: It would be nice to eventually make this diagnostic more clear,
     75     // e.g., by referencing the original declaration or by saying *why* this
     76     // reference is outside the range.
     77 
     78     // Generate a report for this bug.
     79     BugReport *report =
     80       new BugReport(*BT, BT->getDescription(), N);
     81 
     82     report->addRange(LoadS->getSourceRange());
     83     C.emitReport(report);
     84     return;
     85   }
     86 
     87   // Array bound check succeeded.  From this point forward the array bound
     88   // should always succeed.
     89   C.addTransition(StInBound);
     90 }
     91 
     92 void ento::registerArrayBoundChecker(CheckerManager &mgr) {
     93   mgr.registerChecker<ArrayBoundChecker>();
     94 }
     95