Coverage Report

Created: 2023-09-21 18:56

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
Line
Count
Source (jump to first uncovered line)
1
// RetainCountDiagnostics.cpp - Checks for leaks and other issues -*- C++ -*--//
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 file defines diagnostics for RetainCountChecker, which implements
10
//  a reference count checker for Core Foundation and Cocoa on (Mac OS X).
11
//
12
//===----------------------------------------------------------------------===//
13
14
#include "RetainCountDiagnostics.h"
15
#include "RetainCountChecker.h"
16
#include "llvm/ADT/STLExtras.h"
17
#include "llvm/ADT/SmallVector.h"
18
#include <optional>
19
20
using namespace clang;
21
using namespace ento;
22
using namespace retaincountchecker;
23
24
1.12k
StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugKind BT) {
25
1.12k
  switch (BT) {
26
140
  case UseAfterRelease:
27
140
    return "Use-after-release";
28
140
  case ReleaseNotOwned:
29
140
    return "Bad release";
30
140
  case DeallocNotOwned:
31
140
    return "-dealloc sent to non-exclusively owned object";
32
140
  case FreeNotOwned:
33
140
    return "freeing non-exclusively owned object";
34
140
  case OverAutorelease:
35
140
    return "Object autoreleased too many times";
36
140
  case ReturnNotOwnedForOwned:
37
140
    return "Method should return an owned object";
38
140
  case LeakWithinFunction:
39
140
    return "Leak";
40
140
  case LeakAtReturn:
41
140
    return "Leak of returned object";
42
1.12k
  }
43
0
  llvm_unreachable("Unknown RefCountBugKind");
44
0
}
45
46
607
StringRef RefCountBug::getDescription() const {
47
607
  switch (BT) {
48
59
  case UseAfterRelease:
49
59
    return "Reference-counted object is used after it is released";
50
51
  case ReleaseNotOwned:
51
51
    return "Incorrect decrement of the reference count of an object that is "
52
51
           "not owned at this point by the caller";
53
0
  case DeallocNotOwned:
54
0
    return "-dealloc sent to object that may be referenced elsewhere";
55
1
  case FreeNotOwned:
56
1
    return  "'free' called on an object that may be referenced elsewhere";
57
32
  case OverAutorelease:
58
32
    return "Object autoreleased too many times";
59
25
  case ReturnNotOwnedForOwned:
60
25
    return "Object with a +0 retain count returned to caller where a +1 "
61
25
           "(owning) retain count is expected";
62
386
  case LeakWithinFunction:
63
439
  case LeakAtReturn:
64
439
    return "";
65
607
  }
66
0
  llvm_unreachable("Unknown RefCountBugKind");
67
0
}
68
69
RefCountBug::RefCountBug(CheckerNameRef Checker, RefCountBugKind BT)
70
1.12k
    : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount,
71
1.12k
              /*SuppressOnSink=*/BT == LeakWithinFunction ||
72
1.12k
                  
BT == LeakAtReturn980
),
73
1.12k
      BT(BT) {}
74
75
20
static bool isNumericLiteralExpression(const Expr *E) {
76
  // FIXME: This set of cases was copied from SemaExprObjC.
77
20
  return isa<IntegerLiteral, CharacterLiteral, FloatingLiteral,
78
20
             ObjCBoolLiteralExpr, CXXBoolLiteralExpr>(E);
79
20
}
80
81
/// If type represents a pointer to CXXRecordDecl,
82
/// and is not a typedef, return the decl name.
83
/// Otherwise, return the serialization of type.
84
287
static std::string getPrettyTypeName(QualType QT) {
85
287
  QualType PT = QT->getPointeeType();
86
287
  if (!PT.isNull() && !QT->getAs<TypedefType>())
87
137
    if (const auto *RD = PT->getAsCXXRecordDecl())
88
49
      return std::string(RD->getName());
89
238
  return QT.getAsString();
90
287
}
91
92
/// Write information about the type state change to @c os,
93
/// return whether the note should be generated.
94
static bool shouldGenerateNote(llvm::raw_string_ostream &os,
95
                               const RefVal *PrevT,
96
                               const RefVal &CurrV,
97
10.8k
                               bool DeallocSent) {
98
  // Get the previous type state.
99
10.8k
  RefVal PrevV = *PrevT;
100
101
  // Specially handle -dealloc.
102
10.8k
  if (DeallocSent) {
103
    // Determine if the object's reference count was pushed to zero.
104
5
    assert(!PrevV.hasSameState(CurrV) && "The state should have changed.");
105
    // We may not have transitioned to 'release' if we hit an error.
106
    // This case is handled elsewhere.
107
5
    if (CurrV.getKind() == RefVal::Released) {
108
5
      assert(CurrV.getCombinedCounts() == 0);
109
5
      os << "Object released by directly sending the '-dealloc' message";
110
5
      return true;
111
5
    }
112
5
  }
113
114
  // Determine if the typestate has changed.
115
10.8k
  if (!PrevV.hasSameState(CurrV))
116
292
    switch (CurrV.getKind()) {
117
132
    case RefVal::Owned:
118
241
    case RefVal::NotOwned:
119
241
      if (PrevV.getCount() == CurrV.getCount()) {
120
        // Did an autorelease message get sent?
121
100
        if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount())
122
26
          return false;
123
124
74
        assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount());
125
74
        os << "Object autoreleased";
126
74
        return true;
127
74
      }
128
129
141
      if (PrevV.getCount() > CurrV.getCount())
130
31
        os << "Reference count decremented.";
131
110
      else
132
110
        os << "Reference count incremented.";
133
134
141
      if (unsigned Count = CurrV.getCount())
135
133
        os << " The object now has a +" << Count << " retain count.";
136
137
141
      return true;
138
139
51
    case RefVal::Released:
140
51
      if (CurrV.getIvarAccessHistory() ==
141
51
              RefVal::IvarAccessHistory::ReleasedAfterDirectAccess &&
142
51
          
CurrV.getIvarAccessHistory() != PrevV.getIvarAccessHistory()0
) {
143
0
        os << "Strong instance variable relinquished. ";
144
0
      }
145
51
      os << "Object released.";
146
51
      return true;
147
148
0
    case RefVal::ReturnedOwned:
149
      // Autoreleases can be applied after marking a node ReturnedOwned.
150
0
      if (CurrV.getAutoreleaseCount())
151
0
        return false;
152
153
0
      os << "Object returned to caller as an owning reference (single "
154
0
            "retain count transferred to caller)";
155
0
      return true;
156
157
0
    case RefVal::ReturnedNotOwned:
158
0
      os << "Object returned to caller with a +0 retain count";
159
0
      return true;
160
161
0
    default:
162
0
      return false;
163
292
    }
164
10.5k
  return true;
165
10.8k
}
166
167
/// Finds argument index of the out paramter in the call @c S
168
/// corresponding to the symbol @c Sym.
169
/// If none found, returns std::nullopt.
170
static std::optional<unsigned>
171
findArgIdxOfSymbol(ProgramStateRef CurrSt, const LocationContext *LCtx,
172
505
                   SymbolRef &Sym, std::optional<CallEventRef<>> CE) {
173
505
  if (!CE)
174
0
    return std::nullopt;
175
176
1.02k
  
for (unsigned Idx = 0; 505
Idx < (*CE)->getNumArgs();
Idx++517
)
177
534
    if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion())
178
238
      if (const auto *TR = dyn_cast<TypedValueRegion>(MR))
179
101
        if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymbol() == Sym)
180
17
          return Idx;
181
182
488
  return std::nullopt;
183
505
}
184
185
28
static std::optional<std::string> findMetaClassAlloc(const Expr *Callee) {
186
28
  if (const auto *ME = dyn_cast<MemberExpr>(Callee)) {
187
3
    if (ME->getMemberDecl()->getNameAsString() != "alloc")
188
2
      return std::nullopt;
189
1
    const Expr *This = ME->getBase()->IgnoreParenImpCasts();
190
1
    if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) {
191
1
      const ValueDecl *VD = DRE->getDecl();
192
1
      if (VD->getNameAsString() != "metaClass")
193
0
        return std::nullopt;
194
195
1
      if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext()))
196
1
        return RD->getNameAsString();
197
198
1
    }
199
1
  }
200
25
  return std::nullopt;
201
28
}
202
203
37
static std::string findAllocatedObjectName(const Stmt *S, QualType QT) {
204
37
  if (const auto *CE = dyn_cast<CallExpr>(S))
205
28
    if (auto Out = findMetaClassAlloc(CE->getCallee()))
206
1
      return *Out;
207
36
  return getPrettyTypeName(QT);
208
37
}
209
210
static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
211
                                           const LocationContext *LCtx,
212
                                           const RefVal &CurrV, SymbolRef &Sym,
213
                                           const Stmt *S,
214
505
                                           llvm::raw_string_ostream &os) {
215
505
  CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager();
216
505
  if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
217
    // Get the name of the callee (if it is available)
218
    // from the tracked SVal.
219
258
    SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);
220
258
    const FunctionDecl *FD = X.getAsFunctionDecl();
221
222
    // If failed, try to get it from AST.
223
258
    if (!FD)
224
3
      FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
225
226
258
    if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) {
227
17
      os << "Call to method '" << MD->getQualifiedNameAsString() << '\'';
228
241
    } else if (FD) {
229
241
      os << "Call to function '" << FD->getQualifiedNameAsString() << '\'';
230
241
    } else {
231
0
      os << "function call";
232
0
    }
233
258
  } else 
if (247
isa<CXXNewExpr>(S)247
) {
234
9
    os << "Operator 'new'";
235
238
  } else {
236
238
    assert(isa<ObjCMessageExpr>(S));
237
238
    CallEventRef<ObjCMethodCall> Call = Mgr.getObjCMethodCall(
238
238
        cast<ObjCMessageExpr>(S), CurrSt, LCtx, {nullptr, 0});
239
240
238
    switch (Call->getMessageKind()) {
241
226
    case OCM_Message:
242
226
      os << "Method";
243
226
      break;
244
6
    case OCM_PropertyAccess:
245
6
      os << "Property";
246
6
      break;
247
6
    case OCM_Subscript:
248
6
      os << "Subscript";
249
6
      break;
250
238
    }
251
238
  }
252
253
505
  std::optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx, {nullptr, 0});
254
505
  auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE);
255
256
  // If index is not found, we assume that the symbol was returned.
257
505
  if (!Idx) {
258
488
    os << " returns ";
259
488
  } else {
260
17
    os << " writes ";
261
17
  }
262
263
505
  if (CurrV.getObjKind() == ObjKind::CF) {
264
237
    os << "a Core Foundation object of type '" << Sym->getType() << "' with a ";
265
268
  } else if (CurrV.getObjKind() == ObjKind::OS) {
266
37
    os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType())
267
37
       << "' with a ";
268
231
  } else if (CurrV.getObjKind() == ObjKind::Generalized) {
269
3
    os << "an object of type '" << Sym->getType() << "' with a ";
270
228
  } else {
271
228
    assert(CurrV.getObjKind() == ObjKind::ObjC);
272
228
    QualType T = Sym->getType();
273
228
    if (!isa<ObjCObjectPointerType>(T)) {
274
63
      os << "an Objective-C object with a ";
275
165
    } else {
276
165
      const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T);
277
165
      os << "an instance of " << PT->getPointeeType() << " with a ";
278
165
    }
279
228
  }
280
281
505
  if (CurrV.isOwned()) {
282
426
    os << "+1 retain count";
283
426
  } else {
284
79
    assert(CurrV.isNotOwned());
285
79
    os << "+0 retain count";
286
79
  }
287
288
505
  if (Idx) {
289
17
    os << " into an out parameter '";
290
17
    const ParmVarDecl *PVD = (*CE)->parameters()[*Idx];
291
17
    PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
292
17
                              /*Qualified=*/false);
293
17
    os << "'";
294
295
17
    QualType RT = (*CE)->getResultType();
296
17
    if (!RT.isNull() && !RT->isVoidType()) {
297
7
      SVal RV = (*CE)->getReturnValue();
298
7
      if (CurrSt->isNull(RV).isConstrainedTrue()) {
299
2
        os << " (assuming the call returns zero)";
300
5
      } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) {
301
5
        os << " (assuming the call returns non-zero)";
302
5
      }
303
304
7
    }
305
17
  }
306
505
}
307
308
namespace clang {
309
namespace ento {
310
namespace retaincountchecker {
311
312
class RefCountReportVisitor : public BugReporterVisitor {
313
protected:
314
  SymbolRef Sym;
315
316
public:
317
607
  RefCountReportVisitor(SymbolRef sym) : Sym(sym) {}
318
319
607
  void Profile(llvm::FoldingSetNodeID &ID) const override {
320
607
    static int x = 0;
321
607
    ID.AddPointer(&x);
322
607
    ID.AddPointer(Sym);
323
607
  }
324
325
  PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
326
                                   BugReporterContext &BRC,
327
                                   PathSensitiveBugReport &BR) override;
328
329
  PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,
330
                                    const ExplodedNode *N,
331
                                    PathSensitiveBugReport &BR) override;
332
};
333
334
class RefLeakReportVisitor : public RefCountReportVisitor {
335
public:
336
  RefLeakReportVisitor(SymbolRef Sym, const MemRegion *LastBinding)
337
439
      : RefCountReportVisitor(Sym), LastBinding(LastBinding) {}
338
339
  PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,
340
                                    const ExplodedNode *N,
341
                                    PathSensitiveBugReport &BR) override;
342
343
private:
344
  const MemRegion *LastBinding;
345
};
346
347
} // end namespace retaincountchecker
348
} // end namespace ento
349
} // end namespace clang
350
351
352
/// Find the first node with the parent stack frame.
353
135
static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) {
354
135
  const StackFrameContext *SC = Pred->getStackFrame();
355
135
  if (SC->inTopFrame())
356
0
    return nullptr;
357
135
  const StackFrameContext *PC = SC->getParent()->getStackFrame();
358
135
  if (!PC)
359
0
    return nullptr;
360
361
135
  const ExplodedNode *N = Pred;
362
2.88k
  while (N && N->getStackFrame() != PC) {
363
2.74k
    N = N->getFirstPred();
364
2.74k
  }
365
135
  return N;
366
135
}
367
368
369
/// Insert a diagnostic piece at function exit
370
/// if a function parameter is annotated as "os_consumed",
371
/// but it does not actually consume the reference.
372
static std::shared_ptr<PathDiagnosticEventPiece>
373
annotateConsumedSummaryMismatch(const ExplodedNode *N,
374
                                CallExitBegin &CallExitLoc,
375
                                const SourceManager &SM,
376
135
                                CallEventManager &CEMgr) {
377
378
135
  const ExplodedNode *CN = getCalleeNode(N);
379
135
  if (!CN)
380
0
    return nullptr;
381
382
135
  CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState());
383
384
135
  std::string sbuf;
385
135
  llvm::raw_string_ostream os(sbuf);
386
135
  ArrayRef<const ParmVarDecl *> Parameters = Call->parameters();
387
192
  for (unsigned I=0; I < Call->getNumArgs() && 
I < Parameters.size()57
;
++I57
) {
388
57
    const ParmVarDecl *PVD = Parameters[I];
389
390
57
    if (!PVD->hasAttr<OSConsumedAttr>())
391
55
      continue;
392
393
2
    if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) {
394
2
      const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR);
395
2
      const RefVal *CountAtExit = getRefBinding(N->getState(), SR);
396
397
2
      if (!CountBeforeCall || !CountAtExit)
398
0
        continue;
399
400
2
      unsigned CountBefore = CountBeforeCall->getCount();
401
2
      unsigned CountAfter = CountAtExit->getCount();
402
403
2
      bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1;
404
2
      if (!AsExpected) {
405
2
        os << "Parameter '";
406
2
        PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
407
2
                                  /*Qualified=*/false);
408
2
        os << "' is marked as consuming, but the function did not consume "
409
2
           << "the reference\n";
410
2
      }
411
2
    }
412
2
  }
413
414
135
  if (os.str().empty())
415
133
    return nullptr;
416
417
2
  PathDiagnosticLocation L = PathDiagnosticLocation::create(CallExitLoc, SM);
418
2
  return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
419
135
}
420
421
/// Annotate the parameter at the analysis entry point.
422
static std::shared_ptr<PathDiagnosticEventPiece>
423
annotateStartParameter(const ExplodedNode *N, SymbolRef Sym,
424
26.3k
                       const SourceManager &SM) {
425
26.3k
  auto PP = N->getLocationAs<BlockEdge>();
426
26.3k
  if (!PP)
427
25.5k
    return nullptr;
428
429
797
  const CFGBlock *Src = PP->getSrc();
430
797
  const RefVal *CurrT = getRefBinding(N->getState(), Sym);
431
432
797
  if (&Src->getParent()->getEntry() != Src || 
!CurrT188
||
433
797
      
getRefBinding(N->getFirstPred()->getState(), Sym)95
)
434
784
    return nullptr;
435
436
13
  const auto *VR = cast<VarRegion>(cast<SymbolRegionValue>(Sym)->getRegion());
437
13
  const auto *PVD = cast<ParmVarDecl>(VR->getDecl());
438
13
  PathDiagnosticLocation L = PathDiagnosticLocation(PVD, SM);
439
440
13
  std::string s;
441
13
  llvm::raw_string_ostream os(s);
442
13
  os << "Parameter '" << PVD->getDeclName() << "' starts at +";
443
13
  if (CurrT->getCount() == 1) {
444
5
    os << "1, as it is marked as consuming";
445
8
  } else {
446
8
    assert(CurrT->getCount() == 0);
447
8
    os << "0";
448
8
  }
449
13
  return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
450
13
}
451
452
PathDiagnosticPieceRef
453
RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
454
26.3k
                                 PathSensitiveBugReport &BR) {
455
456
26.3k
  const auto &BT = static_cast<const RefCountBug&>(BR.getBugType());
457
458
26.3k
  bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned ||
459
26.3k
                       
BT.getBugType() == RefCountBug::DeallocNotOwned26.2k
;
460
461
26.3k
  const SourceManager &SM = BRC.getSourceManager();
462
26.3k
  CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager();
463
26.3k
  if (auto CE = N->getLocationAs<CallExitBegin>())
464
135
    if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr))
465
2
      return PD;
466
467
26.3k
  if (auto PD = annotateStartParameter(N, Sym, SM))
468
13
    return PD;
469
470
  // FIXME: We will eventually need to handle non-statement-based events
471
  // (__attribute__((cleanup))).
472
26.2k
  if (!N->getLocation().getAs<StmtPoint>())
473
2.20k
    return nullptr;
474
475
  // Check if the type state has changed.
476
24.0k
  const ExplodedNode *PrevNode = N->getFirstPred();
477
24.0k
  ProgramStateRef PrevSt = PrevNode->getState();
478
24.0k
  ProgramStateRef CurrSt = N->getState();
479
24.0k
  const LocationContext *LCtx = N->getLocationContext();
480
481
24.0k
  const RefVal* CurrT = getRefBinding(CurrSt, Sym);
482
24.0k
  if (!CurrT)
483
12.6k
    return nullptr;
484
485
11.4k
  const RefVal &CurrV = *CurrT;
486
11.4k
  const RefVal *PrevT = getRefBinding(PrevSt, Sym);
487
488
  // Create a string buffer to constain all the useful things we want
489
  // to tell the user.
490
11.4k
  std::string sbuf;
491
11.4k
  llvm::raw_string_ostream os(sbuf);
492
493
11.4k
  if (PrevT && 
IsFreeUnowned10.8k
&&
CurrV.isNotOwned()32
&&
PrevT->isOwned()9
) {
494
1
    os << "Object is now not exclusively owned";
495
1
    auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM);
496
1
    return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
497
1
  }
498
499
  // This is the allocation site since the previous node had no bindings
500
  // for this symbol.
501
11.4k
  if (!PrevT) {
502
541
    const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
503
504
541
    if (isa<ObjCIvarRefExpr>(S) &&
505
541
        
isSynthesizedAccessor(LCtx->getStackFrame())6
) {
506
6
      S = LCtx->getStackFrame()->getCallSite();
507
6
    }
508
509
541
    if (isa<ObjCArrayLiteral>(S)) {
510
11
      os << "NSArray literal is an object with a +0 retain count";
511
530
    } else if (isa<ObjCDictionaryLiteral>(S)) {
512
5
      os << "NSDictionary literal is an object with a +0 retain count";
513
525
    } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
514
20
      if (isNumericLiteralExpression(BL->getSubExpr()))
515
8
        os << "NSNumber literal is an object with a +0 retain count";
516
12
      else {
517
12
        const ObjCInterfaceDecl *BoxClass = nullptr;
518
12
        if (const ObjCMethodDecl *Method = BL->getBoxingMethod())
519
12
          BoxClass = Method->getClassInterface();
520
521
        // We should always be able to find the boxing class interface,
522
        // but consider this future-proofing.
523
12
        if (BoxClass) {
524
12
          os << *BoxClass << " b";
525
12
        } else {
526
0
          os << "B";
527
0
        }
528
529
12
        os << "oxed expression produces an object with a +0 retain count";
530
12
      }
531
505
    } else if (isa<ObjCIvarRefExpr>(S)) {
532
0
      os << "Object loaded from instance variable";
533
505
    } else {
534
505
      generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os);
535
505
    }
536
537
541
    PathDiagnosticLocation Pos(S, SM, N->getLocationContext());
538
541
    return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
539
541
  }
540
541
  // Gather up the effects that were performed on the object at this
542
  // program point
543
10.8k
  bool DeallocSent = false;
544
545
10.8k
  const ProgramPointTag *Tag = N->getLocation().getTag();
546
547
10.8k
  if (Tag == &RetainCountChecker::getCastFailTag()) {
548
2
    os << "Assuming dynamic cast returns null due to type mismatch";
549
2
  }
550
551
10.8k
  if (Tag == &RetainCountChecker::getDeallocSentTag()) {
552
    // We only have summaries attached to nodes after evaluating CallExpr and
553
    // ObjCMessageExprs.
554
5
    const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
555
556
5
    if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
557
      // Iterate through the parameter expressions and see if the symbol
558
      // was ever passed as an argument.
559
0
      unsigned i = 0;
560
561
0
      for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) {
562
563
        // Retrieve the value of the argument.  Is it the symbol
564
        // we are interested in?
565
0
        if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym)
566
0
          continue;
567
568
        // We have an argument.  Get the effect!
569
0
        DeallocSent = true;
570
0
      }
571
5
    } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
572
5
      if (const Expr *receiver = ME->getInstanceReceiver()) {
573
5
        if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx)
574
5
              .getAsLocSymbol() == Sym) {
575
          // The symbol we are tracking is the receiver.
576
5
          DeallocSent = true;
577
5
        }
578
5
      }
579
5
    }
580
5
  }
581
582
10.8k
  if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent))
583
26
    return nullptr;
584
585
10.8k
  if (os.str().empty())
586
10.5k
    return nullptr; // We have nothing to say!
587
588
273
  const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
589
273
  PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
590
273
                                N->getLocationContext());
591
273
  auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
592
593
  // Add the range by scanning the children of the statement for any bindings
594
  // to Sym.
595
273
  for (const Stmt *Child : S->children())
596
380
    if (const Expr *Exp = dyn_cast_or_null<Expr>(Child))
597
380
      if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) {
598
258
        P->addRange(Exp->getSourceRange());
599
258
        break;
600
258
      }
601
602
273
  return std::move(P);
603
10.8k
}
604
605
828
static std::optional<std::string> describeRegion(const MemRegion *MR) {
606
828
  if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))
607
577
    return std::string(VR->getDecl()->getName());
608
  // Once we support more storage locations for bindings,
609
  // this would need to be improved.
610
251
  return std::nullopt;
611
828
}
612
613
using Bindings = llvm::SmallVector<std::pair<const MemRegion *, SVal>, 4>;
614
615
namespace {
616
class VarBindingsCollector : public StoreManager::BindingsHandler {
617
  SymbolRef Sym;
618
  Bindings &Result;
619
620
public:
621
  VarBindingsCollector(SymbolRef Sym, Bindings &ToFill)
622
169
      : Sym(Sym), Result(ToFill) {}
623
624
  bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *R,
625
1.90k
                     SVal Val) override {
626
1.90k
    SymbolRef SymV = Val.getAsLocSymbol();
627
1.90k
    if (!SymV || 
SymV != Sym1.34k
)
628
1.51k
      return true;
629
630
384
    if (isa<NonParamVarRegion>(R))
631
167
      Result.emplace_back(R, Val);
632
633
384
    return true;
634
1.90k
  }
635
};
636
} // namespace
637
638
Bindings getAllVarBindingsForSymbol(ProgramStateManager &Manager,
639
169
                                    const ExplodedNode *Node, SymbolRef Sym) {
640
169
  Bindings Result;
641
169
  VarBindingsCollector Collector{Sym, Result};
642
1.95k
  while (Result.empty() && 
Node1.78k
) {
643
1.78k
    Manager.iterBindings(Node->getState(), Collector);
644
1.78k
    Node = Node->getFirstPred();
645
1.78k
  }
646
647
169
  return Result;
648
169
}
649
650
namespace {
651
// Find the first node in the current function context that referred to the
652
// tracked symbol and the memory location that value was stored to. Note, the
653
// value is only reported if the allocation occurred in the same function as
654
// the leak. The function can also return a location context, which should be
655
// treated as interesting.
656
struct AllocationInfo {
657
  const ExplodedNode* N;
658
  const MemRegion *R;
659
  const LocationContext *InterestingMethodContext;
660
  AllocationInfo(const ExplodedNode *InN,
661
                 const MemRegion *InR,
662
                 const LocationContext *InInterestingMethodContext) :
663
439
    N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {}
664
};
665
} // end anonymous namespace
666
667
static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
668
439
                                        const ExplodedNode *N, SymbolRef Sym) {
669
439
  const ExplodedNode *AllocationNode = N;
670
439
  const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;
671
439
  const MemRegion *FirstBinding = nullptr;
672
439
  const LocationContext *LeakContext = N->getLocationContext();
673
674
  // The location context of the init method called on the leaked object, if
675
  // available.
676
439
  const LocationContext *InitMethodContext = nullptr;
677
678
11.0k
  while (N) {
679
11.0k
    ProgramStateRef St = N->getState();
680
11.0k
    const LocationContext *NContext = N->getLocationContext();
681
682
11.0k
    if (!getRefBinding(St, Sym))
683
439
      break;
684
685
10.6k
    StoreManager::FindUniqueBinding FB(Sym);
686
10.6k
    StateMgr.iterBindings(St, FB);
687
688
10.6k
    if (FB) {
689
5.87k
      const MemRegion *R = FB.getRegion();
690
      // Do not show local variables belonging to a function other than
691
      // where the error is reported.
692
5.87k
      if (auto MR = dyn_cast<StackSpaceRegion>(R->getMemorySpace()))
693
5.87k
        if (MR->getStackFrame() == LeakContext->getStackFrame())
694
5.20k
          FirstBinding = R;
695
5.87k
    }
696
697
    // AllocationNode is the last node in which the symbol was tracked.
698
10.6k
    AllocationNode = N;
699
700
    // AllocationNodeInCurrentContext, is the last node in the current or
701
    // parent context in which the symbol was tracked.
702
    //
703
    // Note that the allocation site might be in the parent context. For example,
704
    // the case where an allocation happens in a block that captures a reference
705
    // to it and that reference is overwritten/dropped by another call to
706
    // the block.
707
10.6k
    if (NContext == LeakContext || 
NContext->isParentOf(LeakContext)1.90k
)
708
8.82k
      AllocationNodeInCurrentOrParentContext = N;
709
710
    // Find the last init that was called on the given symbol and store the
711
    // init method's location context.
712
10.6k
    if (!InitMethodContext)
713
10.6k
      if (auto CEP = N->getLocation().getAs<CallEnter>()) {
714
66
        const Stmt *CE = CEP->getCallExpr();
715
66
        if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
716
23
          const Stmt *RecExpr = ME->getInstanceReceiver();
717
23
          if (RecExpr) {
718
23
            SVal RecV = St->getSVal(RecExpr, NContext);
719
23
            if (ME->getMethodFamily() == OMF_init && 
RecV.getAsSymbol() == Sym18
)
720
8
              InitMethodContext = CEP->getCalleeContext();
721
23
          }
722
23
        }
723
66
      }
724
725
10.6k
    N = N->getFirstPred();
726
10.6k
  }
727
728
  // If we are reporting a leak of the object that was allocated with alloc,
729
  // mark its init method as interesting.
730
439
  const LocationContext *InterestingMethodContext = nullptr;
731
439
  if (InitMethodContext) {
732
8
    const ProgramPoint AllocPP = AllocationNode->getLocation();
733
8
    if (std::optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
734
8
      if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
735
8
        if (ME->getMethodFamily() == OMF_alloc)
736
8
          InterestingMethodContext = InitMethodContext;
737
8
  }
738
739
  // If allocation happened in a function different from the leak node context,
740
  // do not report the binding.
741
439
  assert(N && "Could not find allocation node");
742
743
439
  if (AllocationNodeInCurrentOrParentContext &&
744
439
      AllocationNodeInCurrentOrParentContext->getLocationContext() !=
745
439
      LeakContext)
746
2
    FirstBinding = nullptr;
747
748
439
  return AllocationInfo(AllocationNodeInCurrentOrParentContext, FirstBinding,
749
439
                        InterestingMethodContext);
750
439
}
751
752
PathDiagnosticPieceRef
753
RefCountReportVisitor::getEndPath(BugReporterContext &BRC,
754
                                  const ExplodedNode *EndN,
755
165
                                  PathSensitiveBugReport &BR) {
756
165
  BR.markInteresting(Sym);
757
165
  return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
758
165
}
759
760
PathDiagnosticPieceRef
761
RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
762
                                 const ExplodedNode *EndN,
763
389
                                 PathSensitiveBugReport &BR) {
764
765
  // Tell the BugReporterContext to report cases when the tracked symbol is
766
  // assigned to different variables, etc.
767
389
  BR.markInteresting(Sym);
768
769
389
  PathDiagnosticLocation L = cast<RefLeakReport>(BR).getEndOfPath();
770
771
389
  std::string sbuf;
772
389
  llvm::raw_string_ostream os(sbuf);
773
774
389
  os << "Object leaked: ";
775
776
389
  std::optional<std::string> RegionDescription = describeRegion(LastBinding);
777
389
  if (RegionDescription) {
778
264
    os << "object allocated and stored into '" << *RegionDescription << '\'';
779
264
  } else {
780
125
    os << "allocated object of type '" << getPrettyTypeName(Sym->getType())
781
125
       << "'";
782
125
  }
783
784
  // Get the retain count.
785
389
  const RefVal *RV = getRefBinding(EndN->getState(), Sym);
786
389
  assert(RV);
787
788
389
  if (RV->getKind() == RefVal::ErrorLeakReturned) {
789
53
    const Decl *D = &EndN->getCodeDecl();
790
791
53
    os << (isa<ObjCMethodDecl>(D) ? 
" is returned from a method "32
792
53
                                  : 
" is returned from a function "21
);
793
794
53
    if (D->hasAttr<CFReturnsNotRetainedAttr>()) {
795
1
      os << "that is annotated as CF_RETURNS_NOT_RETAINED";
796
52
    } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) {
797
3
      os << "that is annotated as NS_RETURNS_NOT_RETAINED";
798
49
    } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) {
799
0
      os << "that is annotated as OS_RETURNS_NOT_RETAINED";
800
49
    } else {
801
49
      if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
802
29
        if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) {
803
1
          os << "managed by Automatic Reference Counting";
804
28
        } else {
805
28
          os << "whose name ('" << MD->getSelector().getAsString()
806
28
             << "') does not start with "
807
28
                "'copy', 'mutableCopy', 'alloc' or 'new'."
808
28
                "  This violates the naming convention rules"
809
28
                " given in the Memory Management Guide for Cocoa";
810
28
        }
811
29
      } else {
812
20
        const FunctionDecl *FD = cast<FunctionDecl>(D);
813
20
        ObjKind K = RV->getObjKind();
814
20
        if (K == ObjKind::ObjC || K == ObjKind::CF) {
815
19
          os << "whose name ('" << *FD
816
19
             << "') does not contain 'Copy' or 'Create'.  This violates the "
817
19
                "naming"
818
19
                " convention rules given in the Memory Management Guide for "
819
19
                "Core"
820
19
                " Foundation";
821
19
        } else 
if (1
RV->getObjKind() == ObjKind::OS1
) {
822
1
          std::string FuncName = FD->getNameAsString();
823
1
          os << "whose name ('" << FuncName << "') starts with '"
824
1
             << StringRef(FuncName).substr(0, 3) << "'";
825
1
        }
826
20
      }
827
49
    }
828
336
  } else {
829
336
    os << " is not referenced later in this execution path and has a retain "
830
336
          "count of +"
831
336
       << RV->getCount();
832
336
  }
833
834
389
  return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
835
389
}
836
837
RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
838
                               ExplodedNode *n, SymbolRef sym, bool isLeak)
839
575
    : PathSensitiveBugReport(D, D.getDescription(), n), Sym(sym),
840
575
      isLeak(isLeak) {
841
575
  if (!isLeak)
842
136
    addVisitor<RefCountReportVisitor>(sym);
843
575
}
844
845
RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
846
                               ExplodedNode *n, SymbolRef sym,
847
                               StringRef endText)
848
32
    : PathSensitiveBugReport(D, D.getDescription(), endText, n) {
849
850
32
  addVisitor<RefCountReportVisitor>(sym);
851
32
}
852
853
123
void RefLeakReport::deriveParamLocation(CheckerContext &Ctx) {
854
123
  const SourceManager &SMgr = Ctx.getSourceManager();
855
856
123
  if (!Sym->getOriginRegion())
857
115
    return;
858
859
8
  auto *Region = dyn_cast<DeclRegion>(Sym->getOriginRegion());
860
8
  if (Region) {
861
8
    const Decl *PDecl = Region->getDecl();
862
8
    if (isa_and_nonnull<ParmVarDecl>(PDecl)) {
863
8
      PathDiagnosticLocation ParamLocation =
864
8
          PathDiagnosticLocation::create(PDecl, SMgr);
865
8
      Location = ParamLocation;
866
8
      UniqueingLocation = ParamLocation;
867
8
      UniqueingDecl = Ctx.getLocationContext()->getDecl();
868
8
    }
869
8
  }
870
8
}
871
872
439
void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx) {
873
  // Most bug reports are cached at the location where they occurred.
874
  // With leaks, we want to unique them by the location where they were
875
  // allocated, and only report a single path.  To do this, we need to find
876
  // the allocation site of a piece of tracked memory, which we do via a
877
  // call to GetAllocationSite.  This will walk the ExplodedGraph backwards.
878
  // Note that this is *not* the trimmed graph; we are guaranteed, however,
879
  // that all ancestor nodes that represent the allocation site have the
880
  // same SourceLocation.
881
439
  const ExplodedNode *AllocNode = nullptr;
882
883
439
  const SourceManager &SMgr = Ctx.getSourceManager();
884
885
439
  AllocationInfo AllocI =
886
439
      GetAllocationSite(Ctx.getStateManager(), getErrorNode(), Sym);
887
888
439
  AllocNode = AllocI.N;
889
439
  AllocFirstBinding = AllocI.R;
890
439
  markInteresting(AllocI.InterestingMethodContext);
891
892
  // Get the SourceLocation for the allocation site.
893
  // FIXME: This will crash the analyzer if an allocation comes from an
894
  // implicit call (ex: a destructor call).
895
  // (Currently there are no such allocations in Cocoa, though.)
896
439
  AllocStmt = AllocNode->getStmtForDiagnostics();
897
898
439
  if (!AllocStmt) {
899
8
    AllocFirstBinding = nullptr;
900
8
    return;
901
8
  }
902
903
431
  PathDiagnosticLocation AllocLocation = PathDiagnosticLocation::createBegin(
904
431
      AllocStmt, SMgr, AllocNode->getLocationContext());
905
431
  Location = AllocLocation;
906
907
  // Set uniqieing info, which will be used for unique the bug reports. The
908
  // leaks should be uniqued on the allocation site.
909
431
  UniqueingLocation = AllocLocation;
910
431
  UniqueingDecl = AllocNode->getLocationContext()->getDecl();
911
431
}
912
913
439
void RefLeakReport::createDescription(CheckerContext &Ctx) {
914
439
  assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
915
439
  Description.clear();
916
439
  llvm::raw_string_ostream os(Description);
917
439
  os << "Potential leak of an object";
918
919
439
  std::optional<std::string> RegionDescription =
920
439
      describeRegion(AllocBindingToReport);
921
439
  if (RegionDescription) {
922
313
    os << " stored into '" << *RegionDescription << '\'';
923
313
  } else {
924
925
    // If we can't figure out the name, just supply the type information.
926
126
    os << " of type '" << getPrettyTypeName(Sym->getType()) << "'";
927
126
  }
928
439
}
929
930
void RefLeakReport::findBindingToReport(CheckerContext &Ctx,
931
439
                                        ExplodedNode *Node) {
932
439
  if (!AllocFirstBinding)
933
    // If we don't have any bindings, we won't be able to find any
934
    // better binding to report.
935
123
    return;
936
937
  // If the original region still contains the leaking symbol...
938
316
  if (Node->getState()->getSVal(AllocFirstBinding).getAsSymbol() == Sym) {
939
    // ...it is the best binding to report.
940
147
    AllocBindingToReport = AllocFirstBinding;
941
147
    return;
942
147
  }
943
944
  // At this point, we know that the original region doesn't contain the leaking
945
  // when the actual leak happens.  It means that it can be confusing for the
946
  // user to see such description in the message.
947
  //
948
  // Let's consider the following example:
949
  //   Object *Original = allocate(...);
950
  //   Object *New = Original;
951
  //   Original = allocate(...);
952
  //   Original->release();
953
  //
954
  // Complaining about a leaking object "stored into Original" might cause a
955
  // rightful confusion because 'Original' is actually released.
956
  // We should complain about 'New' instead.
957
169
  Bindings AllVarBindings =
958
169
      getAllVarBindingsForSymbol(Ctx.getStateManager(), Node, Sym);
959
960
  // While looking for the last var bindings, we can still find
961
  // `AllocFirstBinding` to be one of them.  In situations like this,
962
  // it would still be the easiest case to explain to our users.
963
169
  if (!AllVarBindings.empty() &&
964
169
      llvm::count_if(AllVarBindings,
965
167
                     [this](const std::pair<const MemRegion *, SVal> Binding) {
966
167
                       return Binding.first == AllocFirstBinding;
967
167
                     }) == 0) {
968
    // Let's pick one of them at random (if there is something to pick from).
969
19
    AllocBindingToReport = AllVarBindings[0].first;
970
971
    // Because 'AllocBindingToReport' is not the same as
972
    // 'AllocFirstBinding', we need to explain how the leaking object
973
    // got from one to another.
974
    //
975
    // NOTE: We use the actual SVal stored in AllocBindingToReport here because
976
    //       trackStoredValue compares SVal's and it can get trickier for
977
    //       something like derived regions if we want to construct SVal from
978
    //       Sym. Instead, we take the value that is definitely stored in that
979
    //       region, thus guaranteeing that trackStoredValue will work.
980
19
    bugreporter::trackStoredValue(AllVarBindings[0].second.castAs<KnownSVal>(),
981
19
                                  AllocBindingToReport, *this);
982
150
  } else {
983
150
    AllocBindingToReport = AllocFirstBinding;
984
150
  }
985
169
}
986
987
RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts,
988
                             ExplodedNode *N, SymbolRef Sym,
989
                             CheckerContext &Ctx)
990
439
    : RefCountReport(D, LOpts, N, Sym, /*isLeak=*/true) {
991
992
439
  deriveAllocLocation(Ctx);
993
439
  findBindingToReport(Ctx, N);
994
995
439
  if (!AllocFirstBinding)
996
123
    deriveParamLocation(Ctx);
997
998
439
  createDescription(Ctx);
999
1000
439
  addVisitor<RefLeakReportVisitor>(Sym, AllocBindingToReport);
1001
439
}