1234287Sdim//== ObjCContainersChecker.cpp - Path sensitive checker for CFArray *- C++ -*=//
2234287Sdim//
3234287Sdim//                     The LLVM Compiler Infrastructure
4234287Sdim//
5234287Sdim// This file is distributed under the University of Illinois Open Source
6234287Sdim// License. See LICENSE.TXT for details.
7234287Sdim//
8234287Sdim//===----------------------------------------------------------------------===//
9234287Sdim//
10234287Sdim// Performs path sensitive checks of Core Foundation static containers like
11234287Sdim// CFArray.
12234287Sdim// 1) Check for buffer overflows:
13234287Sdim//      In CFArrayGetArrayAtIndex( myArray, index), if the index is outside the
14234287Sdim//      index space of theArray (0 to N-1 inclusive (where N is the count of
15234287Sdim//      theArray), the behavior is undefined.
16234287Sdim//
17234287Sdim//===----------------------------------------------------------------------===//
18234287Sdim
19234287Sdim#include "ClangSACheckers.h"
20249423Sdim#include "clang/AST/ParentMap.h"
21249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
22234287Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
23234287Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h"
24234287Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
25234287Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
26234287Sdim
27234287Sdimusing namespace clang;
28234287Sdimusing namespace ento;
29234287Sdim
30234287Sdimnamespace {
31234287Sdimclass ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>,
32234287Sdim                                             check::PostStmt<CallExpr> > {
33234287Sdim  mutable OwningPtr<BugType> BT;
34234287Sdim  inline void initBugType() const {
35234287Sdim    if (!BT)
36234287Sdim      BT.reset(new BugType("CFArray API",
37234287Sdim                           categories::CoreFoundationObjectiveC));
38234287Sdim  }
39234287Sdim
40234287Sdim  inline SymbolRef getArraySym(const Expr *E, CheckerContext &C) const {
41234287Sdim    SVal ArrayRef = C.getState()->getSVal(E, C.getLocationContext());
42234287Sdim    SymbolRef ArraySym = ArrayRef.getAsSymbol();
43234287Sdim    return ArraySym;
44234287Sdim  }
45234287Sdim
46234287Sdim  void addSizeInfo(const Expr *Array, const Expr *Size,
47234287Sdim                   CheckerContext &C) const;
48234287Sdim
49234287Sdimpublic:
50234287Sdim  /// A tag to id this checker.
51234287Sdim  static void *getTag() { static int Tag; return &Tag; }
52234287Sdim
53234287Sdim  void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
54234287Sdim  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
55234287Sdim};
56234287Sdim} // end anonymous namespace
57234287Sdim
58243830Sdim// ProgramState trait - a map from array symbol to its state.
59243830SdimREGISTER_MAP_WITH_PROGRAMSTATE(ArraySizeMap, SymbolRef, DefinedSVal)
60234287Sdim
61234287Sdimvoid ObjCContainersChecker::addSizeInfo(const Expr *Array, const Expr *Size,
62234287Sdim                                        CheckerContext &C) const {
63234287Sdim  ProgramStateRef State = C.getState();
64234287Sdim  SVal SizeV = State->getSVal(Size, C.getLocationContext());
65234287Sdim  // Undefined is reported by another checker.
66234287Sdim  if (SizeV.isUnknownOrUndef())
67234287Sdim    return;
68234287Sdim
69234287Sdim  // Get the ArrayRef symbol.
70234287Sdim  SVal ArrayRef = State->getSVal(Array, C.getLocationContext());
71234287Sdim  SymbolRef ArraySym = ArrayRef.getAsSymbol();
72234287Sdim  if (!ArraySym)
73234287Sdim    return;
74234287Sdim
75249423Sdim  C.addTransition(
76249423Sdim      State->set<ArraySizeMap>(ArraySym, SizeV.castAs<DefinedSVal>()));
77234287Sdim  return;
78234287Sdim}
79234287Sdim
80234287Sdimvoid ObjCContainersChecker::checkPostStmt(const CallExpr *CE,
81234287Sdim                                          CheckerContext &C) const {
82234287Sdim  StringRef Name = C.getCalleeName(CE);
83234287Sdim  if (Name.empty() || CE->getNumArgs() < 1)
84234287Sdim    return;
85234287Sdim
86234287Sdim  // Add array size information to the state.
87234287Sdim  if (Name.equals("CFArrayCreate")) {
88234287Sdim    if (CE->getNumArgs() < 3)
89234287Sdim      return;
90234287Sdim    // Note, we can visit the Create method in the post-visit because
91234287Sdim    // the CFIndex parameter is passed in by value and will not be invalidated
92234287Sdim    // by the call.
93234287Sdim    addSizeInfo(CE, CE->getArg(2), C);
94234287Sdim    return;
95234287Sdim  }
96234287Sdim
97234287Sdim  if (Name.equals("CFArrayGetCount")) {
98234287Sdim    addSizeInfo(CE->getArg(0), CE, C);
99234287Sdim    return;
100234287Sdim  }
101234287Sdim}
102234287Sdim
103234287Sdimvoid ObjCContainersChecker::checkPreStmt(const CallExpr *CE,
104234287Sdim                                         CheckerContext &C) const {
105234287Sdim  StringRef Name = C.getCalleeName(CE);
106234287Sdim  if (Name.empty() || CE->getNumArgs() < 2)
107234287Sdim    return;
108234287Sdim
109234287Sdim  // Check the array access.
110234287Sdim  if (Name.equals("CFArrayGetValueAtIndex")) {
111234287Sdim    ProgramStateRef State = C.getState();
112234287Sdim    // Retrieve the size.
113234287Sdim    // Find out if we saw this array symbol before and have information about it.
114234287Sdim    const Expr *ArrayExpr = CE->getArg(0);
115234287Sdim    SymbolRef ArraySym = getArraySym(ArrayExpr, C);
116234287Sdim    if (!ArraySym)
117234287Sdim      return;
118234287Sdim
119234287Sdim    const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym);
120234287Sdim
121234287Sdim    if (!Size)
122234287Sdim      return;
123234287Sdim
124234287Sdim    // Get the index.
125234287Sdim    const Expr *IdxExpr = CE->getArg(1);
126234287Sdim    SVal IdxVal = State->getSVal(IdxExpr, C.getLocationContext());
127234287Sdim    if (IdxVal.isUnknownOrUndef())
128234287Sdim      return;
129249423Sdim    DefinedSVal Idx = IdxVal.castAs<DefinedSVal>();
130234287Sdim
131234287Sdim    // Now, check if 'Idx in [0, Size-1]'.
132234287Sdim    const QualType T = IdxExpr->getType();
133234287Sdim    ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T);
134234287Sdim    ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T);
135234287Sdim    if (StOutBound && !StInBound) {
136234287Sdim      ExplodedNode *N = C.generateSink(StOutBound);
137234287Sdim      if (!N)
138234287Sdim        return;
139234287Sdim      initBugType();
140234287Sdim      BugReport *R = new BugReport(*BT, "Index is out of bounds", N);
141234287Sdim      R->addRange(IdxExpr->getSourceRange());
142243830Sdim      C.emitReport(R);
143234287Sdim      return;
144234287Sdim    }
145234287Sdim  }
146234287Sdim}
147234287Sdim
148234287Sdim/// Register checker.
149234287Sdimvoid ento::registerObjCContainersChecker(CheckerManager &mgr) {
150234287Sdim  mgr.registerChecker<ObjCContainersChecker>();
151234287Sdim}
152