Coverage Report

Created: 2022-01-18 06:27

/Users/buildslave/jenkins/workspace/coverage/llvm-project/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
15
      : ReceiverSymbol(ReceiverSymbol), Satisfied(false) {}
71
72
  PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ,
73
                                   BugReporterContext &BRC,
74
                                   PathSensitiveBugReport &BR) override;
75
76
15
  void Profile(llvm::FoldingSetNodeID &ID) const override {
77
15
    ID.Add(ReceiverSymbol);
78
15
  }
79
};
80
} // End anonymous namespace.
81
82
void ObjCSuperDeallocChecker::checkPreObjCMessage(const ObjCMethodCall &M,
83
89
                                                  CheckerContext &C) const {
84
85
89
  ProgramStateRef State = C.getState();
86
89
  SymbolRef ReceiverSymbol = M.getReceiverSVal().getAsSymbol();
87
89
  if (!ReceiverSymbol) {
88
5
    diagnoseCallArguments(M, C);
89
5
    return;
90
5
  }
91
92
84
  bool AlreadyCalled = State->contains<CalledSuperDealloc>(ReceiverSymbol);
93
84
  if (!AlreadyCalled)
94
76
    return;
95
96
8
  StringRef Desc;
97
98
8
  if (isSuperDeallocMessage(M)) {
99
3
    Desc = "[super dealloc] should not be called multiple times";
100
5
  } else {
101
5
    Desc = StringRef();
102
5
  }
103
104
8
  reportUseAfterDealloc(ReceiverSymbol, Desc, M.getOriginExpr(), C);
105
8
}
106
107
void ObjCSuperDeallocChecker::checkPreCall(const CallEvent &Call,
108
468
                                           CheckerContext &C) const {
109
468
  diagnoseCallArguments(Call, C);
110
468
}
111
112
void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M,
113
90
                                                   CheckerContext &C) const {
114
  // Check for [super dealloc] method call.
115
90
  if (!isSuperDeallocMessage(M))
116
63
    return;
117
118
27
  ProgramStateRef State = C.getState();
119
27
  const LocationContext *LC = C.getLocationContext();
120
27
  SymbolRef SelfSymbol = State->getSelfSVal(LC).getAsSymbol();
121
27
  assert(SelfSymbol && "No receiver symbol at call to [super dealloc]?");
122
123
  // We add this transition in checkPostObjCMessage to avoid warning when
124
  // we inline a call to [super dealloc] where the inlined call itself
125
  // calls [super dealloc].
126
0
  State = State->add<CalledSuperDealloc>(SelfSymbol);
127
27
  C.addTransition(State);
128
27
}
129
130
void ObjCSuperDeallocChecker::checkLocation(SVal L, bool IsLoad, const Stmt *S,
131
609
                                  CheckerContext &C) const {
132
609
  SymbolRef BaseSym = L.getLocSymbolInBase();
133
609
  if (!BaseSym)
134
537
    return;
135
136
72
  ProgramStateRef State = C.getState();
137
138
72
  if (!State->contains<CalledSuperDealloc>(BaseSym))
139
67
    return;
140
141
5
  const MemRegion *R = L.getAsRegion();
142
5
  if (!R)
143
0
    return;
144
145
  // Climb the super regions to find the base symbol while recording
146
  // 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
158
5
  StringRef Desc = StringRef();
159
5
  auto *IvarRegion = dyn_cast_or_null<ObjCIvarRegion>(PriorSubRegion);
160
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
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
  // We have a use of self after free.
180
  // This likely causes a crash, so stop exploring the
181
  // path by generating a sink.
182
15
  ExplodedNode *ErrNode = C.generateErrorNode();
183
  // If we've already reached this node on another path, return.
184
15
  if (!ErrNode)
185
0
    return;
186
187
15
  if (Desc.empty())
188
7
    Desc = "Use of 'self' after it has been deallocated";
189
190
  // Generate the report.
191
15
  auto BR = std::make_unique<PathSensitiveBugReport>(*DoubleSuperDeallocBugType,
192
15
                                                     Desc, ErrNode);
193
15
  BR->addRange(S->getSourceRange());
194
15
  BR->addVisitor(std::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
473
                                                    CheckerContext &C) const {
202
473
  ProgramStateRef State = C.getState();
203
473
  unsigned ArgCount = CE.getNumArgs();
204
699
  for (unsigned I = 0; I < ArgCount; 
I++226
) {
205
228
    SymbolRef Sym = CE.getArgSVal(I).getAsSymbol();
206
228
    if (!Sym)
207
140
      continue;
208
209
88
    if (State->contains<CalledSuperDealloc>(Sym)) {
210
2
      reportUseAfterDealloc(Sym, StringRef(), CE.getArgExpr(I), C);
211
2
      return;
212
2
    }
213
88
  }
214
473
}
215
216
ObjCSuperDeallocChecker::ObjCSuperDeallocChecker()
217
47
    : IIdealloc(nullptr), IINSObject(nullptr) {
218
219
47
  DoubleSuperDeallocBugType.reset(
220
47
      new BugType(this, "[super dealloc] should not be called more than once",
221
47
                  categories::CoreFoundationObjectiveC));
222
47
}
223
224
void
225
36
ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(ASTContext &Ctx) const {
226
36
  if (IIdealloc)
227
34
    return;
228
229
2
  IIdealloc = &Ctx.Idents.get("dealloc");
230
2
  IINSObject = &Ctx.Idents.get("NSObject");
231
232
2
  SELdealloc = Ctx.Selectors.getSelector(0, &IIdealloc);
233
2
}
234
235
bool
236
98
ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const {
237
98
  if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance)
238
62
    return false;
239
240
36
  ASTContext &Ctx = M.getState()->getStateManager().getContext();
241
36
  initIdentifierInfoAndSelectors(Ctx);
242
243
36
  return M.getSelector() == SELdealloc;
244
98
}
245
246
PathDiagnosticPieceRef
247
SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ,
248
                                 BugReporterContext &BRC,
249
207
                                 PathSensitiveBugReport &) {
250
207
  if (Satisfied)
251
95
    return nullptr;
252
253
112
  ProgramStateRef State = Succ->getState();
254
255
112
  bool CalledNow =
256
112
      Succ->getState()->contains<CalledSuperDealloc>(ReceiverSymbol);
257
112
  bool CalledBefore =
258
112
      Succ->getFirstPred()->getState()->contains<CalledSuperDealloc>(
259
112
          ReceiverSymbol);
260
261
  // Is Succ the node on which the analyzer noted that [super dealloc] was
262
  // called on ReceiverSymbol?
263
112
  if (CalledNow && !CalledBefore) {
264
15
    Satisfied = true;
265
266
15
    ProgramPoint P = Succ->getLocation();
267
15
    PathDiagnosticLocation L =
268
15
        PathDiagnosticLocation::create(P, BRC.getSourceManager());
269
270
15
    if (!L.isValid() || !L.asLocation().isValid())
271
0
      return nullptr;
272
273
15
    return std::make_shared<PathDiagnosticEventPiece>(
274
15
        L, "[super dealloc] called here");
275
15
  }
276
277
97
  return nullptr;
278
112
}
279
280
//===----------------------------------------------------------------------===//
281
// Checker Registration.
282
//===----------------------------------------------------------------------===//
283
284
47
void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) {
285
47
  Mgr.registerChecker<ObjCSuperDeallocChecker>();
286
47
}
287
288
94
bool ento::shouldRegisterObjCSuperDeallocChecker(const CheckerManager &mgr) {
289
94
  return true;
290
94
}