clang  6.0.0svn
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 
19 #include "ClangSACheckers.h"
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.getState()->getSVal(E, C.getLocationContext());
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 } // end anonymous namespace
62 
63 // ProgramState trait - a map from array symbol to its state.
65 
66 void ObjCContainersChecker::addSizeInfo(const Expr *Array, const Expr *Size,
67  CheckerContext &C) const {
69  SVal SizeV = State->getSVal(Size, C.getLocationContext());
70  // Undefined is reported by another checker.
71  if (SizeV.isUnknownOrUndef())
72  return;
73 
74  // Get the ArrayRef symbol.
75  SVal ArrayRef = State->getSVal(Array, C.getLocationContext());
76  SymbolRef ArraySym = ArrayRef.getAsSymbol();
77  if (!ArraySym)
78  return;
79 
80  C.addTransition(
81  State->set<ArraySizeMap>(ArraySym, SizeV.castAs<DefinedSVal>()));
82 }
83 
84 void ObjCContainersChecker::checkPostStmt(const CallExpr *CE,
85  CheckerContext &C) const {
86  StringRef Name = C.getCalleeName(CE);
87  if (Name.empty() || CE->getNumArgs() < 1)
88  return;
89 
90  // Add array size information to the state.
91  if (Name.equals("CFArrayCreate")) {
92  if (CE->getNumArgs() < 3)
93  return;
94  // Note, we can visit the Create method in the post-visit because
95  // the CFIndex parameter is passed in by value and will not be invalidated
96  // by the call.
97  addSizeInfo(CE, CE->getArg(2), C);
98  return;
99  }
100 
101  if (Name.equals("CFArrayGetCount")) {
102  addSizeInfo(CE->getArg(0), CE, C);
103  return;
104  }
105 }
106 
107 void ObjCContainersChecker::checkPreStmt(const CallExpr *CE,
108  CheckerContext &C) const {
109  StringRef Name = C.getCalleeName(CE);
110  if (Name.empty() || CE->getNumArgs() < 2)
111  return;
112 
113  // Check the array access.
114  if (Name.equals("CFArrayGetValueAtIndex")) {
115  ProgramStateRef State = C.getState();
116  // Retrieve the size.
117  // Find out if we saw this array symbol before and have information about
118  // it.
119  const Expr *ArrayExpr = CE->getArg(0);
120  SymbolRef ArraySym = getArraySym(ArrayExpr, C);
121  if (!ArraySym)
122  return;
123 
124  const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym);
125 
126  if (!Size)
127  return;
128 
129  // Get the index.
130  const Expr *IdxExpr = CE->getArg(1);
131  SVal IdxVal = State->getSVal(IdxExpr, C.getLocationContext());
132  if (IdxVal.isUnknownOrUndef())
133  return;
134  DefinedSVal Idx = IdxVal.castAs<DefinedSVal>();
135 
136  // Now, check if 'Idx in [0, Size-1]'.
137  const QualType T = IdxExpr->getType();
138  ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T);
139  ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T);
140  if (StOutBound && !StInBound) {
141  ExplodedNode *N = C.generateErrorNode(StOutBound);
142  if (!N)
143  return;
144  initBugType();
145  auto R = llvm::make_unique<BugReport>(*BT, "Index is out of bounds", N);
146  R->addRange(IdxExpr->getSourceRange());
147  C.emitReport(std::move(R));
148  return;
149  }
150  }
151 }
152 
154 ObjCContainersChecker::checkPointerEscape(ProgramStateRef State,
155  const InvalidatedSymbols &Escaped,
156  const CallEvent *Call,
157  PointerEscapeKind Kind) const {
158  for (const auto &Sym : Escaped) {
159  // When a symbol for a mutable array escapes, we can't reason precisely
160  // about its size any more -- so remove it from the map.
161  // Note that we aren't notified here when a CFMutableArrayRef escapes as a
162  // CFArrayRef. This is because CFArrayRef is typedef'd as a pointer to a
163  // const-qualified type.
164  State = State->remove<ArraySizeMap>(Sym);
165  }
166  return State;
167 }
168 
169 /// Register checker.
170 void ento::registerObjCContainersChecker(CheckerManager &mgr) {
171  mgr.registerChecker<ObjCContainersChecker>();
172 }
const char *const CoreFoundationObjectiveC
A (possibly-)qualified type.
Definition: Type.h:653
ExplodedNode * generateErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:2278
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:2266
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
Symbolic value.
Definition: SymExpr.h:29
StringRef getCalleeName(const FunctionDecl *FunDecl) const
Get the name of the called function (path-sensitive).
LineState State
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition: SVals.cpp:116
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
Expr - This represents one expression.
Definition: Expr.h:106
const FunctionProtoType * T
QualType getType() const
Definition: Expr.h:128
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
Kind
CHECKER * registerChecker()
Used to register checkers.
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
Definition: SVals.h:63
Dataflow Directional Tag Classes.
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:140
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition: SVals.h:92
const ProgramStateRef & getState() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:265
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2209
const LocationContext * getLocationContext() const
bool isUnknownOrUndef() const
Definition: SVals.h:133