ObjCContainersChecker.cpp revision 302408
1179193Sjb//== ObjCContainersChecker.cpp - Path sensitive checker for CFArray *- C++ -*=//
2179193Sjb//
3179193Sjb//                     The LLVM Compiler Infrastructure
4179193Sjb//
5179193Sjb// This file is distributed under the University of Illinois Open Source
6179193Sjb// License. See LICENSE.TXT for details.
7179193Sjb//
8179193Sjb//===----------------------------------------------------------------------===//
9179193Sjb//
10179193Sjb// Performs path sensitive checks of Core Foundation static containers like
11179193Sjb// CFArray.
12179193Sjb// 1) Check for buffer overflows:
13179193Sjb//      In CFArrayGetArrayAtIndex( myArray, index), if the index is outside the
14179193Sjb//      index space of theArray (0 to N-1 inclusive (where N is the count of
15179193Sjb//      theArray), the behavior is undefined.
16179193Sjb//
17179193Sjb//===----------------------------------------------------------------------===//
18179193Sjb
19179193Sjb#include "ClangSACheckers.h"
20211738Srpaulo#include "clang/AST/ParentMap.h"
21211738Srpaulo#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
22211738Srpaulo#include "clang/StaticAnalyzer/Core/Checker.h"
23211738Srpaulo#include "clang/StaticAnalyzer/Core/CheckerManager.h"
24179193Sjb#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
25179193Sjb#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
26179193Sjb
27179198Sjbusing namespace clang;
28179193Sjbusing namespace ento;
29179193Sjb
30179193Sjbnamespace {
31211738Srpauloclass ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>,
32179193Sjb                                             check::PostStmt<CallExpr>,
33211738Srpaulo                                             check::PointerEscape> {
34179193Sjb  mutable std::unique_ptr<BugType> BT;
35179193Sjb  inline void initBugType() const {
36179193Sjb    if (!BT)
37179193Sjb      BT.reset(new BugType(this, "CFArray API",
38179193Sjb                           categories::CoreFoundationObjectiveC));
39179193Sjb  }
40179193Sjb
41211738Srpaulo  inline SymbolRef getArraySym(const Expr *E, CheckerContext &C) const {
42179193Sjb    SVal ArrayRef = C.getState()->getSVal(E, C.getLocationContext());
43211738Srpaulo    SymbolRef ArraySym = ArrayRef.getAsSymbol();
44179193Sjb    return ArraySym;
45179193Sjb  }
46179193Sjb
47211738Srpaulo  void addSizeInfo(const Expr *Array, const Expr *Size,
48179193Sjb                   CheckerContext &C) const;
49211738Srpaulo
50179193Sjbpublic:
51179193Sjb  /// A tag to id this checker.
52179193Sjb  static void *getTag() { static int Tag; return &Tag; }
53179193Sjb
54179193Sjb  void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
55179193Sjb  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
56179193Sjb  ProgramStateRef checkPointerEscape(ProgramStateRef State,
57179193Sjb                                     const InvalidatedSymbols &Escaped,
58211738Srpaulo                                     const CallEvent *Call,
59179193Sjb                                     PointerEscapeKind Kind) const;
60211738Srpaulo};
61211738Srpaulo} // end anonymous namespace
62211738Srpaulo
63211738Srpaulo// ProgramState trait - a map from array symbol to its state.
64211738SrpauloREGISTER_MAP_WITH_PROGRAMSTATE(ArraySizeMap, SymbolRef, DefinedSVal)
65211738Srpaulo
66211738Srpaulovoid ObjCContainersChecker::addSizeInfo(const Expr *Array, const Expr *Size,
67211738Srpaulo                                        CheckerContext &C) const {
68179193Sjb  ProgramStateRef State = C.getState();
69179193Sjb  SVal SizeV = State->getSVal(Size, C.getLocationContext());
70179193Sjb  // Undefined is reported by another checker.
71179193Sjb  if (SizeV.isUnknownOrUndef())
72179193Sjb    return;
73179193Sjb
74179193Sjb  // Get the ArrayRef symbol.
75179193Sjb  SVal ArrayRef = State->getSVal(Array, C.getLocationContext());
76179193Sjb  SymbolRef ArraySym = ArrayRef.getAsSymbol();
77179193Sjb  if (!ArraySym)
78179193Sjb    return;
79179193Sjb
80179193Sjb  C.addTransition(
81179193Sjb      State->set<ArraySizeMap>(ArraySym, SizeV.castAs<DefinedSVal>()));
82179193Sjb  return;
83179193Sjb}
84179193Sjb
85179193Sjbvoid ObjCContainersChecker::checkPostStmt(const CallExpr *CE,
86179193Sjb                                          CheckerContext &C) const {
87179193Sjb  StringRef Name = C.getCalleeName(CE);
88179193Sjb  if (Name.empty() || CE->getNumArgs() < 1)
89179193Sjb    return;
90179193Sjb
91179193Sjb  // Add array size information to the state.
92179193Sjb  if (Name.equals("CFArrayCreate")) {
93179193Sjb    if (CE->getNumArgs() < 3)
94179193Sjb      return;
95179193Sjb    // Note, we can visit the Create method in the post-visit because
96179193Sjb    // the CFIndex parameter is passed in by value and will not be invalidated
97179193Sjb    // by the call.
98179193Sjb    addSizeInfo(CE, CE->getArg(2), C);
99179193Sjb    return;
100179193Sjb  }
101179193Sjb
102179193Sjb  if (Name.equals("CFArrayGetCount")) {
103179193Sjb    addSizeInfo(CE->getArg(0), CE, C);
104179193Sjb    return;
105179193Sjb  }
106179193Sjb}
107179193Sjb
108179193Sjbvoid ObjCContainersChecker::checkPreStmt(const CallExpr *CE,
109179193Sjb                                         CheckerContext &C) const {
110179193Sjb  StringRef Name = C.getCalleeName(CE);
111179193Sjb  if (Name.empty() || CE->getNumArgs() < 2)
112179193Sjb    return;
113179193Sjb
114179193Sjb  // Check the array access.
115179193Sjb  if (Name.equals("CFArrayGetValueAtIndex")) {
116179193Sjb    ProgramStateRef State = C.getState();
117179193Sjb    // Retrieve the size.
118179193Sjb    // Find out if we saw this array symbol before and have information about
119179193Sjb    // it.
120179193Sjb    const Expr *ArrayExpr = CE->getArg(0);
121179193Sjb    SymbolRef ArraySym = getArraySym(ArrayExpr, C);
122179193Sjb    if (!ArraySym)
123179193Sjb      return;
124179193Sjb
125179193Sjb    const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym);
126179193Sjb
127179193Sjb    if (!Size)
128179193Sjb      return;
129179193Sjb
130179193Sjb    // Get the index.
131179193Sjb    const Expr *IdxExpr = CE->getArg(1);
132179193Sjb    SVal IdxVal = State->getSVal(IdxExpr, C.getLocationContext());
133179193Sjb    if (IdxVal.isUnknownOrUndef())
134179193Sjb      return;
135179193Sjb    DefinedSVal Idx = IdxVal.castAs<DefinedSVal>();
136179193Sjb
137179193Sjb    // Now, check if 'Idx in [0, Size-1]'.
138179193Sjb    const QualType T = IdxExpr->getType();
139179193Sjb    ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T);
140179193Sjb    ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T);
141179193Sjb    if (StOutBound && !StInBound) {
142179193Sjb      ExplodedNode *N = C.generateErrorNode(StOutBound);
143179193Sjb      if (!N)
144179193Sjb        return;
145179193Sjb      initBugType();
146211738Srpaulo      auto R = llvm::make_unique<BugReport>(*BT, "Index is out of bounds", N);
147211738Srpaulo      R->addRange(IdxExpr->getSourceRange());
148211738Srpaulo      C.emitReport(std::move(R));
149211738Srpaulo      return;
150211738Srpaulo    }
151211738Srpaulo  }
152211738Srpaulo}
153211738Srpaulo
154211738SrpauloProgramStateRef
155211738SrpauloObjCContainersChecker::checkPointerEscape(ProgramStateRef State,
156179193Sjb                                          const InvalidatedSymbols &Escaped,
157179193Sjb                                          const CallEvent *Call,
158250953Smarkj                                          PointerEscapeKind Kind) const {
159211738Srpaulo  for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
160250953Smarkj                                          E = Escaped.end();
161179193Sjb                                          I != E; ++I) {
162179193Sjb    SymbolRef Sym = *I;
163179193Sjb    // When a symbol for a mutable array escapes, we can't reason precisely
164179193Sjb    // about its size any more -- so remove it from the map.
165179193Sjb    // Note that we aren't notified here when a CFMutableArrayRef escapes as a
166179193Sjb    // CFArrayRef. This is because CFArrayRef is typedef'd as a pointer to a
167179193Sjb    // const-qualified type.
168179193Sjb    State = State->remove<ArraySizeMap>(Sym);
169179193Sjb  }
170179193Sjb  return State;
171179193Sjb}
172179193Sjb/// Register checker.
173179193Sjbvoid ento::registerObjCContainersChecker(CheckerManager &mgr) {
174179193Sjb  mgr.registerChecker<ObjCContainersChecker>();
175179193Sjb}
176179193Sjb