Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- ObjCSuperDeallocChecker.cpp - Check correct use of [super dealloc] -===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// This defines ObjCSuperDeallocChecker, a builtin check that warns when
10
// self is used after a call to [super dealloc] in MRR mode.
11
//
12
//===----------------------------------------------------------------------===//
13
14
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
16
#include "clang/StaticAnalyzer/Core/Checker.h"
17
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
18
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
20
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
21
22
using namespace clang;
23
using namespace ento;
24
25
namespace {
26
class ObjCSuperDeallocChecker
27
    : public Checker<check::PostObjCMessage, check::PreObjCMessage,
28
                     check::PreCall, check::Location> {
29
30
  mutable IdentifierInfo *IIdealloc, *IINSObject;
31
  mutable Selector SELdealloc;
32
33
  std::unique_ptr<BugType> DoubleSuperDeallocBugType;
34
35
  void initIdentifierInfoAndSelectors(ASTContext &Ctx) const;
36
37
  bool isSuperDeallocMessage(const ObjCMethodCall &M) const;
38
39
public:
40
  ObjCSuperDeallocChecker();
41
  void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
42
  void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
43
44
  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
45
46
  void checkLocation(SVal l, bool isLoad, const Stmt *S,
47
                     CheckerContext &C) const;
48
49
private:
50
51
  void diagnoseCallArguments(const CallEvent &CE, CheckerContext &C) const;
52
53
  void reportUseAfterDealloc(SymbolRef Sym, StringRef Desc, const Stmt *S,
54
                             CheckerContext &C) const;
55
};
56
57
} // End anonymous namespace.
58
59
// Remember whether [super dealloc] has previously been called on the
60
// SymbolRef for the receiver.
61
REGISTER_SET_WITH_PROGRAMSTATE(CalledSuperDealloc, SymbolRef)
62
63
namespace {
64
class SuperDeallocBRVisitor final : public BugReporterVisitor {
65
  SymbolRef ReceiverSymbol;
66
  bool Satisfied;
67
68
public:
69
  SuperDeallocBRVisitor(SymbolRef ReceiverSymbol)
70
      : ReceiverSymbol(ReceiverSymbol),
71
15
        Satisfied(false) {}
72
73
  std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ,
74
                                                 BugReporterContext &BRC,
75
                                                 BugReport &BR) override;
76
77
15
  void Profile(llvm::FoldingSetNodeID &ID) const override {
78
15
    ID.Add(ReceiverSymbol);
79
15
  }
80
};
81
} // End anonymous namespace.
82
83
void ObjCSuperDeallocChecker::checkPreObjCMessage(const ObjCMethodCall &M,
84
88
                                                  CheckerContext &C) const {
85
88
86
88
  ProgramStateRef State = C.getState();
87
88
  SymbolRef ReceiverSymbol = M.getReceiverSVal().getAsSymbol();
88
88
  if (!ReceiverSymbol) {
89
4
    diagnoseCallArguments(M, C);
90
4
    return;
91
4
  }
92
84
93
84
  bool AlreadyCalled = State->contains<CalledSuperDealloc>(ReceiverSymbol);
94
84
  if (!AlreadyCalled)
95
76
    return;
96
8
97
8
  StringRef Desc;
98
8
99
8
  if (isSuperDeallocMessage(M)) {
100
3
    Desc = "[super dealloc] should not be called multiple times";
101
5
  } else {
102
5
    Desc = StringRef();
103
5
  }
104
8
105
8
  reportUseAfterDealloc(ReceiverSymbol, Desc, M.getOriginExpr(), C);
106
8
}
107
108
void ObjCSuperDeallocChecker::checkPreCall(const CallEvent &Call,
109
402
                                           CheckerContext &C) const {
110
402
  diagnoseCallArguments(Call, C);
111
402
}
112
113
void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M,
114
89
                                                   CheckerContext &C) const {
115
89
  // Check for [super dealloc] method call.
116
89
  if (!isSuperDeallocMessage(M))
117
62
    return;
118
27
119
27
  ProgramStateRef State = C.getState();
120
27
  SymbolRef ReceiverSymbol = M.getSelfSVal().getAsSymbol();
121
27
  assert(ReceiverSymbol && "No receiver symbol at call to [super dealloc]?");
122
27
123
27
  // We add this transition in checkPostObjCMessage to avoid warning when
124
27
  // we inline a call to [super dealloc] where the inlined call itself
125
27
  // calls [super dealloc].
126
27
  State = State->add<CalledSuperDealloc>(ReceiverSymbol);
127
27
  C.addTransition(State);
128
27
}
129
130
void ObjCSuperDeallocChecker::checkLocation(SVal L, bool IsLoad, const Stmt *S,
131
471
                                  CheckerContext &C) const {
132
471
  SymbolRef BaseSym = L.getLocSymbolInBase();
133
471
  if (!BaseSym)
134
413
    return;
135
58
136
58
  ProgramStateRef State = C.getState();
137
58
138
58
  if (!State->contains<CalledSuperDealloc>(BaseSym))
139
53
    return;
140
5
141
5
  const MemRegion *R = L.getAsRegion();
142
5
  if (!R)
143
0
    return;
144
5
145
5
  // Climb the super regions to find the base symbol while recording
146
5
  // the second-to-last region for error reporting.
147
5
  const MemRegion *PriorSubRegion = nullptr;
148
11
  while (const SubRegion *SR = dyn_cast<SubRegion>(R)) {
149
11
    if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SR)) {
150
5
      BaseSym = SymR->getSymbol();
151
5
      break;
152
6
    } else {
153
6
      R = SR->getSuperRegion();
154
6
      PriorSubRegion = SR;
155
6
    }
156
11
  }
157
5
158
5
  StringRef Desc = StringRef();
159
5
  auto *IvarRegion = dyn_cast_or_null<ObjCIvarRegion>(PriorSubRegion);
160
5
161
5
  std::string Buf;
162
5
  llvm::raw_string_ostream OS(Buf);
163
5
  if (IvarRegion) {
164
5
    OS << "Use of instance variable '" << *IvarRegion->getDecl() <<
165
5
          "' after 'self' has been deallocated";
166
5
    Desc = OS.str();
167
5
  }
168
5
169
5
  reportUseAfterDealloc(BaseSym, Desc, S, C);
170
5
}
171
172
/// Report a use-after-dealloc on Sym. If not empty,
173
/// Desc will be used to describe the error; otherwise,
174
/// a default warning will be used.
175
void ObjCSuperDeallocChecker::reportUseAfterDealloc(SymbolRef Sym,
176
                                                    StringRef Desc,
177
                                                    const Stmt *S,
178
15
                                                    CheckerContext &C) const {
179
15
  // We have a use of self after free.
180
15
  // This likely causes a crash, so stop exploring the
181
15
  // path by generating a sink.
182
15
  ExplodedNode *ErrNode = C.generateErrorNode();
183
15
  // If we've already reached this node on another path, return.
184
15
  if (!ErrNode)
185
0
    return;
186
15
187
15
  if (Desc.empty())
188
7
    Desc = "Use of 'self' after it has been deallocated";
189
15
190
15
  // Generate the report.
191
15
  std::unique_ptr<BugReport> BR(
192
15
      new BugReport(*DoubleSuperDeallocBugType, Desc, ErrNode));
193
15
  BR->addRange(S->getSourceRange());
194
15
  BR->addVisitor(llvm::make_unique<SuperDeallocBRVisitor>(Sym));
195
15
  C.emitReport(std::move(BR));
196
15
}
197
198
/// Diagnose if any of the arguments to CE have already been
199
/// dealloc'd.
200
void ObjCSuperDeallocChecker::diagnoseCallArguments(const CallEvent &CE,
201
406
                                                    CheckerContext &C) const {
202
406
  ProgramStateRef State = C.getState();
203
406
  unsigned ArgCount = CE.getNumArgs();
204
589
  for (unsigned I = 0; I < ArgCount; 
I++183
) {
205
185
    SymbolRef Sym = CE.getArgSVal(I).getAsSymbol();
206
185
    if (!Sym)
207
112
      continue;
208
73
209
73
    if (State->contains<CalledSuperDealloc>(Sym)) {
210
2
      reportUseAfterDealloc(Sym, StringRef(), CE.getArgExpr(I), C);
211
2
      return;
212
2
    }
213
73
  }
214
406
}
215
216
ObjCSuperDeallocChecker::ObjCSuperDeallocChecker()
217
24
    : IIdealloc(nullptr), IINSObject(nullptr) {
218
24
219
24
  DoubleSuperDeallocBugType.reset(
220
24
      new BugType(this, "[super dealloc] should not be called more than once",
221
24
                  categories::CoreFoundationObjectiveC));
222
24
}
223
224
void
225
36
ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(ASTContext &Ctx) const {
226
36
  if (IIdealloc)
227
34
    return;
228
2
229
2
  IIdealloc = &Ctx.Idents.get("dealloc");
230
2
  IINSObject = &Ctx.Idents.get("NSObject");
231
2
232
2
  SELdealloc = Ctx.Selectors.getSelector(0, &IIdealloc);
233
2
}
234
235
bool
236
97
ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const {
237
97
  if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance)
238
61
    return false;
239
36
240
36
  ASTContext &Ctx = M.getState()->getStateManager().getContext();
241
36
  initIdentifierInfoAndSelectors(Ctx);
242
36
243
36
  return M.getSelector() == SELdealloc;
244
36
}
245
246
std::shared_ptr<PathDiagnosticPiece>
247
SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ,
248
207
                                 BugReporterContext &BRC, BugReport &) {
249
207
  if (Satisfied)
250
95
    return nullptr;
251
112
252
112
  ProgramStateRef State = Succ->getState();
253
112
254
112
  bool CalledNow =
255
112
      Succ->getState()->contains<CalledSuperDealloc>(ReceiverSymbol);
256
112
  bool CalledBefore =
257
112
      Succ->getFirstPred()->getState()->contains<CalledSuperDealloc>(
258
112
          ReceiverSymbol);
259
112
260
112
  // Is Succ the node on which the analyzer noted that [super dealloc] was
261
112
  // called on ReceiverSymbol?
262
112
  if (CalledNow && !CalledBefore) {
263
15
    Satisfied = true;
264
15
265
15
    ProgramPoint P = Succ->getLocation();
266
15
    PathDiagnosticLocation L =
267
15
        PathDiagnosticLocation::create(P, BRC.getSourceManager());
268
15
269
15
    if (!L.isValid() || !L.asLocation().isValid())
270
0
      return nullptr;
271
15
272
15
    return std::make_shared<PathDiagnosticEventPiece>(
273
15
        L, "[super dealloc] called here");
274
15
  }
275
97
276
97
  return nullptr;
277
97
}
278
279
//===----------------------------------------------------------------------===//
280
// Checker Registration.
281
//===----------------------------------------------------------------------===//
282
283
24
void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) {
284
24
  Mgr.registerChecker<ObjCSuperDeallocChecker>();
285
24
}
286
287
24
bool ento::shouldRegisterObjCSuperDeallocChecker(const LangOptions &LO) {
288
24
  return true;
289
24
}