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