clang  8.0.0
ObjCContainersChecker.cpp
Go to the documentation of this file.
1 //== ObjCContainersChecker.cpp - Path sensitive checker for CFArray *- 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 // Performs path sensitive checks of Core Foundation static containers like
11 // CFArray.
12 // 1) Check for buffer overflows:
13 // In CFArrayGetArrayAtIndex( myArray, index), if the index is outside the
14 // index space of theArray (0 to N-1 inclusive (where N is the count of
15 // theArray), the behavior is undefined.
16 //
17 //===----------------------------------------------------------------------===//
18 
20 #include "clang/AST/ParentMap.h"
26 
27 using namespace clang;
28 using namespace ento;
29 
30 namespace {
31 class ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>,
32  check::PostStmt<CallExpr>,
33  check::PointerEscape> {
34  mutable std::unique_ptr<BugType> BT;
35  inline void initBugType() const {
36  if (!BT)
37  BT.reset(new BugType(this, "CFArray API",
39  }
40 
41  inline SymbolRef getArraySym(const Expr *E, CheckerContext &C) const {
42  SVal ArrayRef = C.getSVal(E);
43  SymbolRef ArraySym = ArrayRef.getAsSymbol();
44  return ArraySym;
45  }
46 
47  void addSizeInfo(const Expr *Array, const Expr *Size,
48  CheckerContext &C) const;
49 
50 public:
51  /// A tag to id this checker.
52  static void *getTag() { static int Tag; return &Tag; }
53 
54  void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
55  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
56  ProgramStateRef checkPointerEscape(ProgramStateRef State,
57  const InvalidatedSymbols &Escaped,
58  const CallEvent *Call,
59  PointerEscapeKind Kind) const;
60 
61  void printState(raw_ostream &OS, ProgramStateRef State,
62  const char *NL, const char *Sep) const;
63 };
64 } // end anonymous namespace
65 
66 // ProgramState trait - a map from array symbol to its state.
67 REGISTER_MAP_WITH_PROGRAMSTATE(ArraySizeMap, SymbolRef, DefinedSVal)
68 
69 void ObjCContainersChecker::addSizeInfo(const Expr *Array, const Expr *Size,
70  CheckerContext &C) const {
71  ProgramStateRef State = C.getState();
72  SVal SizeV = C.getSVal(Size);
73  // Undefined is reported by another checker.
74  if (SizeV.isUnknownOrUndef())
75  return;
76 
77  // Get the ArrayRef symbol.
78  SVal ArrayRef = C.getSVal(Array);
79  SymbolRef ArraySym = ArrayRef.getAsSymbol();
80  if (!ArraySym)
81  return;
82 
83  C.addTransition(
84  State->set<ArraySizeMap>(ArraySym, SizeV.castAs<DefinedSVal>()));
85 }
86 
87 void ObjCContainersChecker::checkPostStmt(const CallExpr *CE,
88  CheckerContext &C) const {
89  StringRef Name = C.getCalleeName(CE);
90  if (Name.empty() || CE->getNumArgs() < 1)
91  return;
92 
93  // Add array size information to the state.
94  if (Name.equals("CFArrayCreate")) {
95  if (CE->getNumArgs() < 3)
96  return;
97  // Note, we can visit the Create method in the post-visit because
98  // the CFIndex parameter is passed in by value and will not be invalidated
99  // by the call.
100  addSizeInfo(CE, CE->getArg(2), C);
101  return;
102  }
103 
104  if (Name.equals("CFArrayGetCount")) {
105  addSizeInfo(CE->getArg(0), CE, C);
106  return;
107  }
108 }
109 
110 void ObjCContainersChecker::checkPreStmt(const CallExpr *CE,
111  CheckerContext &C) const {
112  StringRef Name = C.getCalleeName(CE);
113  if (Name.empty() || CE->getNumArgs() < 2)
114  return;
115 
116  // Check the array access.
117  if (Name.equals("CFArrayGetValueAtIndex")) {
118  ProgramStateRef State = C.getState();
119  // Retrieve the size.
120  // Find out if we saw this array symbol before and have information about
121  // it.
122  const Expr *ArrayExpr = CE->getArg(0);
123  SymbolRef ArraySym = getArraySym(ArrayExpr, C);
124  if (!ArraySym)
125  return;
126 
127  const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym);
128 
129  if (!Size)
130  return;
131 
132  // Get the index.
133  const Expr *IdxExpr = CE->getArg(1);
134  SVal IdxVal = C.getSVal(IdxExpr);
135  if (IdxVal.isUnknownOrUndef())
136  return;
137  DefinedSVal Idx = IdxVal.castAs<DefinedSVal>();
138 
139  // Now, check if 'Idx in [0, Size-1]'.
140  const QualType T = IdxExpr->getType();
141  ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T);
142  ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T);
143  if (StOutBound && !StInBound) {
144  ExplodedNode *N = C.generateErrorNode(StOutBound);
145  if (!N)
146  return;
147  initBugType();
148  auto R = llvm::make_unique<BugReport>(*BT, "Index is out of bounds", N);
149  R->addRange(IdxExpr->getSourceRange());
150  bugreporter::trackExpressionValue(N, IdxExpr, *R,
151  /*EnableNullFPSuppression=*/false);
152  C.emitReport(std::move(R));
153  return;
154  }
155  }
156 }
157 
159 ObjCContainersChecker::checkPointerEscape(ProgramStateRef State,
160  const InvalidatedSymbols &Escaped,
161  const CallEvent *Call,
162  PointerEscapeKind Kind) const {
163  for (const auto &Sym : Escaped) {
164  // When a symbol for a mutable array escapes, we can't reason precisely
165  // about its size any more -- so remove it from the map.
166  // Note that we aren't notified here when a CFMutableArrayRef escapes as a
167  // CFArrayRef. This is because CFArrayRef is typedef'd as a pointer to a
168  // const-qualified type.
169  State = State->remove<ArraySizeMap>(Sym);
170  }
171  return State;
172 }
173 
174 void ObjCContainersChecker::printState(raw_ostream &OS, ProgramStateRef State,
175  const char *NL, const char *Sep) const {
176  ArraySizeMapTy Map = State->get<ArraySizeMap>();
177  if (Map.isEmpty())
178  return;
179 
180  OS << Sep << "ObjC container sizes :" << NL;
181  for (auto I : Map) {
182  OS << I.first << " : " << I.second << NL;
183  }
184 }
185 
186 /// Register checker.
187 void ento::registerObjCContainersChecker(CheckerManager &mgr) {
188  mgr.registerChecker<ObjCContainersChecker>();
189 }
const char *const CoreFoundationObjectiveC
A (possibly-)qualified type.
Definition: Type.h:638
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:2553
llvm::DenseSet< SymbolRef > InvalidatedSymbols
Definition: Store.h:52
const SymExpr * SymbolRef
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:2540
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
LineState State
This represents one expression.
Definition: Expr.h:106
QualType getType() const
Definition: Expr.h:128
Kind
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
Dataflow Directional Tag Classes.
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:276
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2396