Coverage Report

Created: 2023-09-30 09:22

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
Line
Count
Source (jump to first uncovered line)
1
//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- 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 BasicObjCFoundationChecks, a class that encapsulates
10
//  a set of simple checks to run on Objective-C code using Apple's Foundation
11
//  classes.
12
//
13
//===----------------------------------------------------------------------===//
14
15
#include "clang/AST/ASTContext.h"
16
#include "clang/AST/DeclObjC.h"
17
#include "clang/AST/Expr.h"
18
#include "clang/AST/ExprObjC.h"
19
#include "clang/AST/StmtObjC.h"
20
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
21
#include "clang/Analysis/SelectorExtras.h"
22
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
23
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
24
#include "clang/StaticAnalyzer/Core/Checker.h"
25
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
26
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
27
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
28
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
29
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
30
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
31
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
32
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
33
#include "llvm/ADT/STLExtras.h"
34
#include "llvm/ADT/SmallString.h"
35
#include "llvm/ADT/StringMap.h"
36
#include "llvm/Support/raw_ostream.h"
37
#include <optional>
38
39
using namespace clang;
40
using namespace ento;
41
using namespace llvm;
42
43
namespace {
44
class APIMisuse : public BugType {
45
public:
46
  APIMisuse(const CheckerBase *checker, const char *name)
47
85
      : BugType(checker, name, "API Misuse (Apple)") {}
48
};
49
} // end anonymous namespace
50
51
//===----------------------------------------------------------------------===//
52
// Utility functions.
53
//===----------------------------------------------------------------------===//
54
55
46
static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) {
56
46
  if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
57
46
    return ID->getIdentifier()->getName();
58
0
  return StringRef();
59
46
}
60
61
enum FoundationClass {
62
  FC_None,
63
  FC_NSArray,
64
  FC_NSDictionary,
65
  FC_NSEnumerator,
66
  FC_NSNull,
67
  FC_NSOrderedSet,
68
  FC_NSSet,
69
  FC_NSString
70
};
71
72
static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID,
73
1.50k
                                      bool IncludeSuperclasses = true) {
74
1.50k
  static llvm::StringMap<FoundationClass> Classes;
75
1.50k
  if (Classes.empty()) {
76
15
    Classes["NSArray"] = FC_NSArray;
77
15
    Classes["NSDictionary"] = FC_NSDictionary;
78
15
    Classes["NSEnumerator"] = FC_NSEnumerator;
79
15
    Classes["NSNull"] = FC_NSNull;
80
15
    Classes["NSOrderedSet"] = FC_NSOrderedSet;
81
15
    Classes["NSSet"] = FC_NSSet;
82
15
    Classes["NSString"] = FC_NSString;
83
15
  }
84
85
  // FIXME: Should we cache this at all?
86
1.50k
  FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());
87
1.50k
  if (result == FC_None && 
IncludeSuperclasses801
)
88
702
    if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
89
449
      return findKnownClass(Super);
90
91
1.05k
  return result;
92
1.50k
}
93
94
//===----------------------------------------------------------------------===//
95
// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
96
//===----------------------------------------------------------------------===//
97
98
namespace {
99
class NilArgChecker : public Checker<check::PreObjCMessage,
100
                                     check::PostStmt<ObjCDictionaryLiteral>,
101
                                     check::PostStmt<ObjCArrayLiteral>,
102
                                     EventDispatcher<ImplicitNullDerefEvent>> {
103
  mutable std::unique_ptr<APIMisuse> BT;
104
105
  mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors;
106
  mutable Selector ArrayWithObjectSel;
107
  mutable Selector AddObjectSel;
108
  mutable Selector InsertObjectAtIndexSel;
109
  mutable Selector ReplaceObjectAtIndexWithObjectSel;
110
  mutable Selector SetObjectAtIndexedSubscriptSel;
111
  mutable Selector ArrayByAddingObjectSel;
112
  mutable Selector DictionaryWithObjectForKeySel;
113
  mutable Selector SetObjectForKeySel;
114
  mutable Selector SetObjectForKeyedSubscriptSel;
115
  mutable Selector RemoveObjectForKeySel;
116
117
  void warnIfNilExpr(const Expr *E, const char *Msg, CheckerContext &C) const;
118
119
  void warnIfNilArg(CheckerContext &C, const ObjCMethodCall &msg, unsigned Arg,
120
                    FoundationClass Class, bool CanBeSubscript = false) const;
121
122
  void generateBugReport(ExplodedNode *N, StringRef Msg, SourceRange Range,
123
                         const Expr *Expr, CheckerContext &C) const;
124
125
public:
126
  void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
127
  void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const;
128
  void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const;
129
};
130
} // end anonymous namespace
131
132
void NilArgChecker::warnIfNilExpr(const Expr *E,
133
                                  const char *Msg,
134
55
                                  CheckerContext &C) const {
135
55
  auto Location = C.getSVal(E).getAs<Loc>();
136
55
  if (!Location)
137
0
    return;
138
139
55
  auto [NonNull, Null] = C.getState()->assume(*Location);
140
141
  // If it's known to be null.
142
55
  if (!NonNull && 
Null9
) {
143
9
    if (ExplodedNode *N = C.generateErrorNode()) {
144
8
      generateBugReport(N, Msg, E->getSourceRange(), E, C);
145
8
      return;
146
8
    }
147
9
  }
148
149
  // If it might be null, assume that it cannot after this operation.
150
47
  if (Null) {
151
    // One needs to make sure the pointer is non-null to be used here.
152
9
    if (ExplodedNode *N = C.generateSink(Null, C.getPredecessor())) {
153
8
      dispatchEvent({*Location, /*IsLoad=*/false, N, &C.getBugReporter(),
154
8
                     /*IsDirectDereference=*/false});
155
8
    }
156
9
    C.addTransition(NonNull);
157
9
  }
158
47
}
159
160
void NilArgChecker::warnIfNilArg(CheckerContext &C,
161
                                 const ObjCMethodCall &msg,
162
                                 unsigned int Arg,
163
                                 FoundationClass Class,
164
65
                                 bool CanBeSubscript) const {
165
  // Check if the argument is nil.
166
65
  ProgramStateRef State = C.getState();
167
65
  if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
168
20
      return;
169
170
  // NOTE: We cannot throw non-fatal errors from warnIfNilExpr,
171
  // because it's called multiple times from some callers, so it'd cause
172
  // an unwanted state split if two or more non-fatal errors are thrown
173
  // within the same checker callback. For now we don't want to, but
174
  // it'll need to be fixed if we ever want to.
175
45
  if (ExplodedNode *N = C.generateErrorNode()) {
176
44
    SmallString<128> sbuf;
177
44
    llvm::raw_svector_ostream os(sbuf);
178
179
44
    if (CanBeSubscript && 
msg.getMessageKind() == OCM_Subscript5
) {
180
181
4
      if (Class == FC_NSArray) {
182
1
        os << "Array element cannot be nil";
183
3
      } else if (Class == FC_NSDictionary) {
184
3
        if (Arg == 0) {
185
0
          os << "Value stored into '";
186
0
          os << GetReceiverInterfaceName(msg) << "' cannot be nil";
187
3
        } else {
188
3
          assert(Arg == 1);
189
3
          os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil";
190
3
        }
191
3
      } else
192
0
        llvm_unreachable("Missing foundation class for the subscript expr");
193
194
40
    } else {
195
40
      if (Class == FC_NSDictionary) {
196
7
        if (Arg == 0)
197
3
          os << "Value argument ";
198
4
        else {
199
4
          assert(Arg == 1);
200
4
          os << "Key argument ";
201
4
        }
202
7
        os << "to '";
203
7
        msg.getSelector().print(os);
204
7
        os << "' cannot be nil";
205
33
      } else {
206
33
        os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '";
207
33
        msg.getSelector().print(os);
208
33
        os << "' cannot be nil";
209
33
      }
210
40
    }
211
212
44
    generateBugReport(N, os.str(), msg.getArgSourceRange(Arg),
213
44
                      msg.getArgExpr(Arg), C);
214
44
  }
215
45
}
216
217
void NilArgChecker::generateBugReport(ExplodedNode *N,
218
                                      StringRef Msg,
219
                                      SourceRange Range,
220
                                      const Expr *E,
221
52
                                      CheckerContext &C) const {
222
52
  if (!BT)
223
8
    BT.reset(new APIMisuse(this, "nil argument"));
224
225
52
  auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
226
52
  R->addRange(Range);
227
52
  bugreporter::trackExpressionValue(N, E, *R);
228
52
  C.emitReport(std::move(R));
229
52
}
230
231
void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
232
491
                                        CheckerContext &C) const {
233
491
  const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
234
491
  if (!ID)
235
8
    return;
236
237
483
  FoundationClass Class = findKnownClass(ID);
238
239
483
  static const unsigned InvalidArgIndex = UINT_MAX;
240
483
  unsigned Arg = InvalidArgIndex;
241
483
  bool CanBeSubscript = false;
242
243
483
  if (Class == FC_NSString) {
244
329
    Selector S = msg.getSelector();
245
246
329
    if (S.isUnarySelector())
247
293
      return;
248
249
36
    if (StringSelectors.empty()) {
250
4
      ASTContext &Ctx = C.getASTContext();
251
4
      Selector Sels[] = {
252
4
          getKeywordSelector(Ctx, "caseInsensitiveCompare"),
253
4
          getKeywordSelector(Ctx, "compare"),
254
4
          getKeywordSelector(Ctx, "compare", "options"),
255
4
          getKeywordSelector(Ctx, "compare", "options", "range"),
256
4
          getKeywordSelector(Ctx, "compare", "options", "range", "locale"),
257
4
          getKeywordSelector(Ctx, "componentsSeparatedByCharactersInSet"),
258
4
          getKeywordSelector(Ctx, "initWithFormat"),
259
4
          getKeywordSelector(Ctx, "localizedCaseInsensitiveCompare"),
260
4
          getKeywordSelector(Ctx, "localizedCompare"),
261
4
          getKeywordSelector(Ctx, "localizedStandardCompare"),
262
4
      };
263
4
      for (Selector KnownSel : Sels)
264
40
        StringSelectors[KnownSel] = 0;
265
4
    }
266
36
    auto I = StringSelectors.find(S);
267
36
    if (I == StringSelectors.end())
268
8
      return;
269
28
    Arg = I->second;
270
154
  } else if (Class == FC_NSArray) {
271
33
    Selector S = msg.getSelector();
272
273
33
    if (S.isUnarySelector())
274
17
      return;
275
276
16
    if (ArrayWithObjectSel.isNull()) {
277
5
      ASTContext &Ctx = C.getASTContext();
278
5
      ArrayWithObjectSel = getKeywordSelector(Ctx, "arrayWithObject");
279
5
      AddObjectSel = getKeywordSelector(Ctx, "addObject");
280
5
      InsertObjectAtIndexSel =
281
5
          getKeywordSelector(Ctx, "insertObject", "atIndex");
282
5
      ReplaceObjectAtIndexWithObjectSel =
283
5
          getKeywordSelector(Ctx, "replaceObjectAtIndex", "withObject");
284
5
      SetObjectAtIndexedSubscriptSel =
285
5
          getKeywordSelector(Ctx, "setObject", "atIndexedSubscript");
286
5
      ArrayByAddingObjectSel = getKeywordSelector(Ctx, "arrayByAddingObject");
287
5
    }
288
289
16
    if (S == ArrayWithObjectSel || S == AddObjectSel ||
290
16
        
S == InsertObjectAtIndexSel10
||
S == ArrayByAddingObjectSel9
) {
291
8
      Arg = 0;
292
8
    } else if (S == SetObjectAtIndexedSubscriptSel) {
293
2
      Arg = 0;
294
2
      CanBeSubscript = true;
295
6
    } else if (S == ReplaceObjectAtIndexWithObjectSel) {
296
1
      Arg = 1;
297
1
    }
298
121
  } else if (Class == FC_NSDictionary) {
299
21
    Selector S = msg.getSelector();
300
301
21
    if (S.isUnarySelector())
302
1
      return;
303
304
20
    if (DictionaryWithObjectForKeySel.isNull()) {
305
2
      ASTContext &Ctx = C.getASTContext();
306
2
      DictionaryWithObjectForKeySel =
307
2
          getKeywordSelector(Ctx, "dictionaryWithObject", "forKey");
308
2
      SetObjectForKeySel = getKeywordSelector(Ctx, "setObject", "forKey");
309
2
      SetObjectForKeyedSubscriptSel =
310
2
          getKeywordSelector(Ctx, "setObject", "forKeyedSubscript");
311
2
      RemoveObjectForKeySel = getKeywordSelector(Ctx, "removeObjectForKey");
312
2
    }
313
314
20
    if (S == DictionaryWithObjectForKeySel || 
S == SetObjectForKeySel18
) {
315
7
      Arg = 0;
316
7
      warnIfNilArg(C, msg, /* Arg */1, Class);
317
13
    } else if (S == SetObjectForKeyedSubscriptSel) {
318
8
      CanBeSubscript = true;
319
8
      Arg = 1;
320
8
    } else 
if (5
S == RemoveObjectForKeySel5
) {
321
4
      Arg = 0;
322
4
    }
323
20
  }
324
325
  // If argument is '0', report a warning.
326
164
  if ((Arg != InvalidArgIndex))
327
58
    warnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
328
164
}
329
330
void NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL,
331
10
                                  CheckerContext &C) const {
332
10
  unsigned NumOfElements = AL->getNumElements();
333
31
  for (unsigned i = 0; i < NumOfElements; 
++i21
) {
334
21
    warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C);
335
21
  }
336
10
}
337
338
void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
339
12
                                  CheckerContext &C) const {
340
12
  unsigned NumOfElements = DL->getNumElements();
341
29
  for (unsigned i = 0; i < NumOfElements; 
++i17
) {
342
17
    ObjCDictionaryElement Element = DL->getKeyValueElement(i);
343
17
    warnIfNilExpr(Element.Key, "Dictionary key cannot be nil", C);
344
17
    warnIfNilExpr(Element.Value, "Dictionary value cannot be nil", C);
345
17
  }
346
12
}
347
348
//===----------------------------------------------------------------------===//
349
// Checking for mismatched types passed to CFNumberCreate/CFNumberGetValue.
350
//===----------------------------------------------------------------------===//
351
352
namespace {
353
class CFNumberChecker : public Checker< check::PreStmt<CallExpr> > {
354
  mutable std::unique_ptr<APIMisuse> BT;
355
  mutable IdentifierInfo *ICreate = nullptr, *IGetValue = nullptr;
356
public:
357
47
  CFNumberChecker() = default;
358
359
  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
360
};
361
} // end anonymous namespace
362
363
enum CFNumberType {
364
  kCFNumberSInt8Type = 1,
365
  kCFNumberSInt16Type = 2,
366
  kCFNumberSInt32Type = 3,
367
  kCFNumberSInt64Type = 4,
368
  kCFNumberFloat32Type = 5,
369
  kCFNumberFloat64Type = 6,
370
  kCFNumberCharType = 7,
371
  kCFNumberShortType = 8,
372
  kCFNumberIntType = 9,
373
  kCFNumberLongType = 10,
374
  kCFNumberLongLongType = 11,
375
  kCFNumberFloatType = 12,
376
  kCFNumberDoubleType = 13,
377
  kCFNumberCFIndexType = 14,
378
  kCFNumberNSIntegerType = 15,
379
  kCFNumberCGFloatType = 16
380
};
381
382
8
static std::optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
383
8
  static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
384
385
8
  if (i < kCFNumberCharType)
386
7
    return FixedSize[i-1];
387
388
1
  QualType T;
389
390
1
  switch (i) {
391
0
    case kCFNumberCharType:     T = Ctx.CharTy;     break;
392
0
    case kCFNumberShortType:    T = Ctx.ShortTy;    break;
393
0
    case kCFNumberIntType:      T = Ctx.IntTy;      break;
394
1
    case kCFNumberLongType:     T = Ctx.LongTy;     break;
395
0
    case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
396
0
    case kCFNumberFloatType:    T = Ctx.FloatTy;    break;
397
0
    case kCFNumberDoubleType:   T = Ctx.DoubleTy;   break;
398
0
    case kCFNumberCFIndexType:
399
0
    case kCFNumberNSIntegerType:
400
0
    case kCFNumberCGFloatType:
401
      // FIXME: We need a way to map from names to Type*.
402
0
    default:
403
0
      return std::nullopt;
404
1
  }
405
406
1
  return Ctx.getTypeSize(T);
407
1
}
408
409
#if 0
410
static const char* GetCFNumberTypeStr(uint64_t i) {
411
  static const char* Names[] = {
412
    "kCFNumberSInt8Type",
413
    "kCFNumberSInt16Type",
414
    "kCFNumberSInt32Type",
415
    "kCFNumberSInt64Type",
416
    "kCFNumberFloat32Type",
417
    "kCFNumberFloat64Type",
418
    "kCFNumberCharType",
419
    "kCFNumberShortType",
420
    "kCFNumberIntType",
421
    "kCFNumberLongType",
422
    "kCFNumberLongLongType",
423
    "kCFNumberFloatType",
424
    "kCFNumberDoubleType",
425
    "kCFNumberCFIndexType",
426
    "kCFNumberNSIntegerType",
427
    "kCFNumberCGFloatType"
428
  };
429
430
  return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
431
}
432
#endif
433
434
void CFNumberChecker::checkPreStmt(const CallExpr *CE,
435
305
                                         CheckerContext &C) const {
436
305
  ProgramStateRef state = C.getState();
437
305
  const FunctionDecl *FD = C.getCalleeDecl(CE);
438
305
  if (!FD)
439
3
    return;
440
441
302
  ASTContext &Ctx = C.getASTContext();
442
302
  if (!ICreate) {
443
19
    ICreate = &Ctx.Idents.get("CFNumberCreate");
444
19
    IGetValue = &Ctx.Idents.get("CFNumberGetValue");
445
19
  }
446
302
  if (!(FD->getIdentifier() == ICreate || 
FD->getIdentifier() == IGetValue296
) ||
447
302
      
CE->getNumArgs() != 38
)
448
294
    return;
449
450
  // Get the value of the "theType" argument.
451
8
  SVal TheTypeVal = C.getSVal(CE->getArg(1));
452
453
  // FIXME: We really should allow ranges of valid theType values, and
454
  //   bifurcate the state appropriately.
455
8
  std::optional<nonloc::ConcreteInt> V =
456
8
      dyn_cast<nonloc::ConcreteInt>(TheTypeVal);
457
8
  if (!V)
458
0
    return;
459
460
8
  uint64_t NumberKind = V->getValue().getLimitedValue();
461
8
  std::optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind);
462
463
  // FIXME: In some cases we can emit an error.
464
8
  if (!OptCFNumberSize)
465
0
    return;
466
467
8
  uint64_t CFNumberSize = *OptCFNumberSize;
468
469
  // Look at the value of the integer being passed by reference.  Essentially
470
  // we want to catch cases where the value passed in is not equal to the
471
  // size of the type being created.
472
8
  SVal TheValueExpr = C.getSVal(CE->getArg(2));
473
474
  // FIXME: Eventually we should handle arbitrary locations.  We can do this
475
  //  by having an enhanced memory model that does low-level typing.
476
8
  std::optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
477
8
  if (!LV)
478
0
    return;
479
480
8
  const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
481
8
  if (!R)
482
0
    return;
483
484
8
  QualType T = Ctx.getCanonicalType(R->getValueType());
485
486
  // FIXME: If the pointee isn't an integer type, should we flag a warning?
487
  //  People can do weird stuff with pointers.
488
489
8
  if (!T->isIntegralOrEnumerationType())
490
0
    return;
491
492
8
  uint64_t PrimitiveTypeSize = Ctx.getTypeSize(T);
493
494
8
  if (PrimitiveTypeSize == CFNumberSize)
495
3
    return;
496
497
  // FIXME: We can actually create an abstract "CFNumber" object that has
498
  //  the bits initialized to the provided values.
499
5
  ExplodedNode *N = C.generateNonFatalErrorNode();
500
5
  if (N) {
501
5
    SmallString<128> sbuf;
502
5
    llvm::raw_svector_ostream os(sbuf);
503
5
    bool isCreate = (FD->getIdentifier() == ICreate);
504
505
5
    if (isCreate) {
506
3
      os << (PrimitiveTypeSize == 8 ? 
"An "1
:
"A "2
)
507
3
         << PrimitiveTypeSize << "-bit integer is used to initialize a "
508
3
         << "CFNumber object that represents "
509
3
         << (CFNumberSize == 8 ? 
"an "1
:
"a "2
)
510
3
         << CFNumberSize << "-bit integer; ";
511
3
    } else {
512
2
      os << "A CFNumber object that represents "
513
2
         << (CFNumberSize == 8 ? 
"an "1
:
"a "1
)
514
2
         << CFNumberSize << "-bit integer is used to initialize "
515
2
         << (PrimitiveTypeSize == 8 ? 
"an "1
:
"a "1
)
516
2
         << PrimitiveTypeSize << "-bit integer; ";
517
2
    }
518
519
5
    if (PrimitiveTypeSize < CFNumberSize)
520
3
      os << (CFNumberSize - PrimitiveTypeSize)
521
3
      << " bits of the CFNumber value will "
522
3
      << (isCreate ? 
"be garbage."2
:
"overwrite adjacent storage."1
);
523
2
    else
524
2
      os << (PrimitiveTypeSize - CFNumberSize)
525
2
      << " bits of the integer value will be "
526
2
      << (isCreate ? 
"lost."1
:
"garbage."1
);
527
528
5
    if (!BT)
529
1
      BT.reset(new APIMisuse(this, "Bad use of CFNumber APIs"));
530
531
5
    auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
532
5
    report->addRange(CE->getArg(2)->getSourceRange());
533
5
    C.emitReport(std::move(report));
534
5
  }
535
5
}
536
537
//===----------------------------------------------------------------------===//
538
// CFRetain/CFRelease/CFMakeCollectable/CFAutorelease checking for null arguments.
539
//===----------------------------------------------------------------------===//
540
541
namespace {
542
class CFRetainReleaseChecker : public Checker<check::PreCall> {
543
  mutable APIMisuse BT{this, "null passed to CF memory management function"};
544
  const CallDescriptionSet ModelledCalls = {
545
      {{"CFRetain"}, 1},
546
      {{"CFRelease"}, 1},
547
      {{"CFMakeCollectable"}, 1},
548
      {{"CFAutorelease"}, 1},
549
  };
550
551
public:
552
  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
553
};
554
} // end anonymous namespace
555
556
void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call,
557
2.71k
                                          CheckerContext &C) const {
558
  // TODO: Make this check part of CallDescription.
559
2.71k
  if (!Call.isGlobalCFunction())
560
1.58k
    return;
561
562
  // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease.
563
1.13k
  if (!ModelledCalls.contains(Call))
564
893
    return;
565
566
  // Get the argument's value.
567
239
  SVal ArgVal = Call.getArgSVal(0);
568
239
  std::optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
569
239
  if (!DefArgVal)
570
0
    return;
571
572
  // Is it null?
573
239
  ProgramStateRef state = C.getState();
574
239
  ProgramStateRef stateNonNull, stateNull;
575
239
  std::tie(stateNonNull, stateNull) = state->assume(*DefArgVal);
576
577
239
  if (!stateNonNull) {
578
14
    ExplodedNode *N = C.generateErrorNode(stateNull);
579
14
    if (!N)
580
0
      return;
581
582
14
    SmallString<64> Str;
583
14
    raw_svector_ostream OS(Str);
584
14
    OS << "Null pointer argument in call to "
585
14
       << cast<FunctionDecl>(Call.getDecl())->getName();
586
587
14
    auto report = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N);
588
14
    report->addRange(Call.getArgSourceRange(0));
589
14
    bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *report);
590
14
    C.emitReport(std::move(report));
591
14
    return;
592
14
  }
593
594
  // From here on, we know the argument is non-null.
595
225
  C.addTransition(stateNonNull);
596
225
}
597
598
//===----------------------------------------------------------------------===//
599
// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
600
//===----------------------------------------------------------------------===//
601
602
namespace {
603
class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
604
  mutable Selector releaseS;
605
  mutable Selector retainS;
606
  mutable Selector autoreleaseS;
607
  mutable Selector drainS;
608
  mutable std::unique_ptr<BugType> BT;
609
610
public:
611
  void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
612
};
613
} // end anonymous namespace
614
615
void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
616
1.28k
                                              CheckerContext &C) const {
617
1.28k
  if (!BT) {
618
14
    BT.reset(new APIMisuse(
619
14
        this, "message incorrectly sent to class instead of class instance"));
620
621
14
    ASTContext &Ctx = C.getASTContext();
622
14
    releaseS = GetNullarySelector("release", Ctx);
623
14
    retainS = GetNullarySelector("retain", Ctx);
624
14
    autoreleaseS = GetNullarySelector("autorelease", Ctx);
625
14
    drainS = GetNullarySelector("drain", Ctx);
626
14
  }
627
628
1.28k
  if (msg.isInstanceMessage())
629
924
    return;
630
362
  const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
631
362
  assert(Class);
632
633
362
  Selector S = msg.getSelector();
634
362
  if (!(S == releaseS || 
S == retainS359
||
S == autoreleaseS356
||
S == drainS353
))
635
350
    return;
636
637
12
  if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
638
12
    SmallString<200> buf;
639
12
    llvm::raw_svector_ostream os(buf);
640
641
12
    os << "The '";
642
12
    S.print(os);
643
12
    os << "' message should be sent to instances "
644
12
          "of class '" << Class->getName()
645
12
       << "' and not the class directly";
646
647
12
    auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
648
12
    report->addRange(msg.getSourceRange());
649
12
    C.emitReport(std::move(report));
650
12
  }
651
12
}
652
653
//===----------------------------------------------------------------------===//
654
// Check for passing non-Objective-C types to variadic methods that expect
655
// only Objective-C types.
656
//===----------------------------------------------------------------------===//
657
658
namespace {
659
class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
660
  mutable Selector arrayWithObjectsS;
661
  mutable Selector dictionaryWithObjectsAndKeysS;
662
  mutable Selector setWithObjectsS;
663
  mutable Selector orderedSetWithObjectsS;
664
  mutable Selector initWithObjectsS;
665
  mutable Selector initWithObjectsAndKeysS;
666
  mutable std::unique_ptr<BugType> BT;
667
668
  bool isVariadicMessage(const ObjCMethodCall &msg) const;
669
670
public:
671
  void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
672
};
673
} // end anonymous namespace
674
675
/// isVariadicMessage - Returns whether the given message is a variadic message,
676
/// where all arguments must be Objective-C types.
677
bool
678
96
VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
679
96
  const ObjCMethodDecl *MD = msg.getDecl();
680
681
96
  if (!MD || 
!MD->isVariadic()95
||
isa<ObjCProtocolDecl>(MD->getDeclContext())15
)
682
82
    return false;
683
684
14
  Selector S = msg.getSelector();
685
686
14
  if (msg.isInstanceMessage()) {
687
    // FIXME: Ideally we'd look at the receiver interface here, but that's not
688
    // useful for init, because alloc returns 'id'. In theory, this could lead
689
    // to false positives, for example if there existed a class that had an
690
    // initWithObjects: implementation that does accept non-Objective-C pointer
691
    // types, but the chance of that happening is pretty small compared to the
692
    // gains that this analysis gives.
693
8
    const ObjCInterfaceDecl *Class = MD->getClassInterface();
694
695
8
    switch (findKnownClass(Class)) {
696
1
    case FC_NSArray:
697
2
    case FC_NSOrderedSet:
698
3
    case FC_NSSet:
699
3
      return S == initWithObjectsS;
700
5
    case FC_NSDictionary:
701
5
      return S == initWithObjectsAndKeysS;
702
0
    default:
703
0
      return false;
704
8
    }
705
8
  } else {
706
6
    const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
707
708
6
    switch (findKnownClass(Class)) {
709
3
      case FC_NSArray:
710
3
        return S == arrayWithObjectsS;
711
1
      case FC_NSOrderedSet:
712
1
        return S == orderedSetWithObjectsS;
713
1
      case FC_NSSet:
714
1
        return S == setWithObjectsS;
715
1
      case FC_NSDictionary:
716
1
        return S == dictionaryWithObjectsAndKeysS;
717
0
      default:
718
0
        return false;
719
6
    }
720
6
  }
721
14
}
722
723
void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
724
96
                                                    CheckerContext &C) const {
725
96
  if (!BT) {
726
5
    BT.reset(new APIMisuse(this,
727
5
                           "Arguments passed to variadic method aren't all "
728
5
                           "Objective-C pointer types"));
729
730
5
    ASTContext &Ctx = C.getASTContext();
731
5
    arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
732
5
    dictionaryWithObjectsAndKeysS =
733
5
      GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
734
5
    setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
735
5
    orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
736
737
5
    initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
738
5
    initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
739
5
  }
740
741
96
  if (!isVariadicMessage(msg))
742
82
      return;
743
744
  // We are not interested in the selector arguments since they have
745
  // well-defined types, so the compiler will issue a warning for them.
746
14
  unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
747
748
  // We're not interested in the last argument since it has to be nil or the
749
  // compiler would have issued a warning for it elsewhere.
750
14
  unsigned variadicArgsEnd = msg.getNumArgs() - 1;
751
752
14
  if (variadicArgsEnd <= variadicArgsBegin)
753
0
    return;
754
755
  // Verify that all arguments have Objective-C types.
756
14
  std::optional<ExplodedNode *> errorNode;
757
758
32
  for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; 
++I18
) {
759
18
    QualType ArgTy = msg.getArgExpr(I)->getType();
760
18
    if (ArgTy->isObjCObjectPointerType())
761
4
      continue;
762
763
    // Block pointers are treaded as Objective-C pointers.
764
14
    if (ArgTy->isBlockPointerType())
765
1
      continue;
766
767
    // Ignore pointer constants.
768
13
    if (isa<loc::ConcreteInt>(msg.getArgSVal(I)))
769
1
      continue;
770
771
    // Ignore pointer types annotated with 'NSObject' attribute.
772
12
    if (C.getASTContext().isObjCNSObjectType(ArgTy))
773
1
      continue;
774
775
    // Ignore CF references, which can be toll-free bridged.
776
11
    if (coreFoundation::isCFObjectRef(ArgTy))
777
1
      continue;
778
779
    // Generate only one error node to use for all bug reports.
780
10
    if (!errorNode)
781
9
      errorNode = C.generateNonFatalErrorNode();
782
783
10
    if (!*errorNode)
784
0
      continue;
785
786
10
    SmallString<128> sbuf;
787
10
    llvm::raw_svector_ostream os(sbuf);
788
789
10
    StringRef TypeName = GetReceiverInterfaceName(msg);
790
10
    if (!TypeName.empty())
791
10
      os << "Argument to '" << TypeName << "' method '";
792
0
    else
793
0
      os << "Argument to method '";
794
795
10
    msg.getSelector().print(os);
796
10
    os << "' should be an Objective-C pointer type, not '";
797
10
    ArgTy.print(os, C.getLangOpts());
798
10
    os << "'";
799
800
10
    auto R =
801
10
        std::make_unique<PathSensitiveBugReport>(*BT, os.str(), *errorNode);
802
10
    R->addRange(msg.getArgSourceRange(I));
803
10
    C.emitReport(std::move(R));
804
10
  }
805
14
}
806
807
//===----------------------------------------------------------------------===//
808
// Improves the modeling of loops over Cocoa collections.
809
//===----------------------------------------------------------------------===//
810
811
// The map from container symbol to the container count symbol.
812
// We currently will remember the last container count symbol encountered.
813
REGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap, SymbolRef, SymbolRef)
814
REGISTER_MAP_WITH_PROGRAMSTATE(ContainerNonEmptyMap, SymbolRef, bool)
815
816
namespace {
817
class ObjCLoopChecker
818
  : public Checker<check::PostStmt<ObjCForCollectionStmt>,
819
                   check::PostObjCMessage,
820
                   check::DeadSymbols,
821
                   check::PointerEscape > {
822
  mutable IdentifierInfo *CountSelectorII = nullptr;
823
824
  bool isCollectionCountMethod(const ObjCMethodCall &M,
825
                               CheckerContext &C) const;
826
827
public:
828
48
  ObjCLoopChecker() = default;
829
  void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
830
  void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
831
  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
832
  ProgramStateRef checkPointerEscape(ProgramStateRef State,
833
                                     const InvalidatedSymbols &Escaped,
834
                                     const CallEvent *Call,
835
                                     PointerEscapeKind Kind) const;
836
};
837
} // end anonymous namespace
838
839
226
static bool isKnownNonNilCollectionType(QualType T) {
840
226
  const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
841
226
  if (!PT)
842
0
    return false;
843
844
226
  const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
845
226
  if (!ID)
846
2
    return false;
847
848
224
  switch (findKnownClass(ID)) {
849
29
  case FC_NSArray:
850
99
  case FC_NSDictionary:
851
131
  case FC_NSEnumerator:
852
134
  case FC_NSOrderedSet:
853
154
  case FC_NSSet:
854
154
    return true;
855
70
  default:
856
70
    return false;
857
224
  }
858
224
}
859
860
/// Assumes that the collection is non-nil.
861
///
862
/// If the collection is known to be nil, returns NULL to indicate an infeasible
863
/// path.
864
static ProgramStateRef checkCollectionNonNil(CheckerContext &C,
865
                                             ProgramStateRef State,
866
226
                                             const ObjCForCollectionStmt *FCS) {
867
226
  if (!State)
868
0
    return nullptr;
869
870
226
  SVal CollectionVal = C.getSVal(FCS->getCollection());
871
226
  std::optional<DefinedSVal> KnownCollection =
872
226
      CollectionVal.getAs<DefinedSVal>();
873
226
  if (!KnownCollection)
874
0
    return State;
875
876
226
  ProgramStateRef StNonNil, StNil;
877
226
  std::tie(StNonNil, StNil) = State->assume(*KnownCollection);
878
226
  if (StNil && 
!StNonNil41
) {
879
    // The collection is nil. This path is infeasible.
880
0
    return nullptr;
881
0
  }
882
883
226
  return StNonNil;
884
226
}
885
886
/// Assumes that the collection elements are non-nil.
887
///
888
/// This only applies if the collection is one of those known not to contain
889
/// nil values.
890
static ProgramStateRef checkElementNonNil(CheckerContext &C,
891
                                          ProgramStateRef State,
892
226
                                          const ObjCForCollectionStmt *FCS) {
893
226
  if (!State)
894
0
    return nullptr;
895
896
  // See if the collection is one where we /know/ the elements are non-nil.
897
226
  if (!isKnownNonNilCollectionType(FCS->getCollection()->getType()))
898
72
    return State;
899
900
154
  const LocationContext *LCtx = C.getLocationContext();
901
154
  const Stmt *Element = FCS->getElement();
902
903
  // FIXME: Copied from ExprEngineObjC.
904
154
  std::optional<Loc> ElementLoc;
905
154
  if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
906
94
    const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
907
94
    assert(ElemDecl->getInit() == nullptr);
908
94
    ElementLoc = State->getLValue(ElemDecl, LCtx);
909
94
  } else {
910
60
    ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>();
911
60
  }
912
913
154
  if (!ElementLoc)
914
0
    return State;
915
916
  // Go ahead and assume the value is non-nil.
917
154
  SVal Val = State->getSVal(*ElementLoc);
918
154
  return State->assume(cast<DefinedOrUnknownSVal>(Val), true);
919
154
}
920
921
/// Returns NULL state if the collection is known to contain elements
922
/// (or is known not to contain elements if the Assumption parameter is false.)
923
static ProgramStateRef
924
assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
925
317
                         SymbolRef CollectionS, bool Assumption) {
926
317
  if (!State || !CollectionS)
927
3
    return State;
928
929
314
  const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS);
930
314
  if (!CountS) {
931
224
    const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS);
932
224
    if (!KnownNonEmpty)
933
94
      return State->set<ContainerNonEmptyMap>(CollectionS, Assumption);
934
130
    return (Assumption == *KnownNonEmpty) ? 
State124
:
nullptr6
;
935
224
  }
936
937
90
  SValBuilder &SvalBuilder = C.getSValBuilder();
938
90
  SVal CountGreaterThanZeroVal =
939
90
    SvalBuilder.evalBinOp(State, BO_GT,
940
90
                          nonloc::SymbolVal(*CountS),
941
90
                          SvalBuilder.makeIntVal(0, (*CountS)->getType()),
942
90
                          SvalBuilder.getConditionType());
943
90
  std::optional<DefinedSVal> CountGreaterThanZero =
944
90
      CountGreaterThanZeroVal.getAs<DefinedSVal>();
945
90
  if (!CountGreaterThanZero) {
946
    // The SValBuilder cannot construct a valid SVal for this condition.
947
    // This means we cannot properly reason about it.
948
0
    return State;
949
0
  }
950
951
90
  return State->assume(*CountGreaterThanZero, Assumption);
952
90
}
953
954
static ProgramStateRef
955
assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
956
                         const ObjCForCollectionStmt *FCS,
957
309
                         bool Assumption) {
958
309
  if (!State)
959
0
    return nullptr;
960
961
309
  SymbolRef CollectionS = C.getSVal(FCS->getCollection()).getAsSymbol();
962
309
  return assumeCollectionNonEmpty(C, State, CollectionS, Assumption);
963
309
}
964
965
/// If the fist block edge is a back edge, we are reentering the loop.
966
static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N,
967
1.27k
                                             const ObjCForCollectionStmt *FCS) {
968
1.27k
  if (!N)
969
0
    return false;
970
971
1.27k
  ProgramPoint P = N->getLocation();
972
1.27k
  if (std::optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
973
197
    return BE->getSrc()->getLoopTarget() == FCS;
974
197
  }
975
976
  // Keep looking for a block edge.
977
1.07k
  for (const ExplodedNode *N : N->preds()) {
978
1.07k
    if (alreadyExecutedAtLeastOneLoopIteration(N, FCS))
979
631
      return true;
980
1.07k
  }
981
982
446
  return false;
983
1.07k
}
984
985
void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
986
423
                                    CheckerContext &C) const {
987
423
  ProgramStateRef State = C.getState();
988
989
  // Check if this is the branch for the end of the loop.
990
423
  if (!ExprEngine::hasMoreIteration(State, FCS, C.getLocationContext())) {
991
197
    if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS))
992
83
      State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false);
993
994
  // Otherwise, this is a branch that goes through the loop body.
995
226
  } else {
996
226
    State = checkCollectionNonNil(C, State, FCS);
997
226
    State = checkElementNonNil(C, State, FCS);
998
226
    State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true);
999
226
  }
1000
1001
423
  if (!State)
1002
31
    C.generateSink(C.getState(), C.getPredecessor());
1003
392
  else if (State != C.getState())
1004
208
    C.addTransition(State);
1005
423
}
1006
1007
bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M,
1008
60
                                              CheckerContext &C) const {
1009
60
  Selector S = M.getSelector();
1010
  // Initialize the identifiers on first use.
1011
60
  if (!CountSelectorII)
1012
2
    CountSelectorII = &C.getASTContext().Idents.get("count");
1013
1014
  // If the method returns collection count, record the value.
1015
60
  return S.isUnarySelector() &&
1016
60
         
(S.getIdentifierInfoForSlot(0) == CountSelectorII)43
;
1017
60
}
1018
1019
void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M,
1020
145
                                           CheckerContext &C) const {
1021
145
  if (!M.isInstanceMessage())
1022
44
    return;
1023
1024
101
  const ObjCInterfaceDecl *ClassID = M.getReceiverInterface();
1025
101
  if (!ClassID)
1026
0
    return;
1027
1028
101
  FoundationClass Class = findKnownClass(ClassID);
1029
101
  if (Class != FC_NSDictionary &&
1030
101
      
Class != FC_NSArray77
&&
1031
101
      
Class != FC_NSSet43
&&
1032
101
      
Class != FC_NSOrderedSet42
)
1033
41
    return;
1034
1035
60
  SymbolRef ContainerS = M.getReceiverSVal().getAsSymbol();
1036
60
  if (!ContainerS)
1037
0
    return;
1038
1039
  // If we are processing a call to "count", get the symbolic value returned by
1040
  // a call to "count" and add it to the map.
1041
60
  if (!isCollectionCountMethod(M, C))
1042
36
    return;
1043
1044
24
  const Expr *MsgExpr = M.getOriginExpr();
1045
24
  SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol();
1046
24
  if (CountS) {
1047
24
    ProgramStateRef State = C.getState();
1048
1049
24
    C.getSymbolManager().addSymbolDependency(ContainerS, CountS);
1050
24
    State = State->set<ContainerCountMap>(ContainerS, CountS);
1051
1052
24
    if (const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) {
1053
8
      State = State->remove<ContainerNonEmptyMap>(ContainerS);
1054
8
      State = assumeCollectionNonEmpty(C, State, ContainerS, *NonEmpty);
1055
8
    }
1056
1057
24
    C.addTransition(State);
1058
24
  }
1059
24
}
1060
1061
410
static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call) {
1062
410
  const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call);
1063
410
  if (!Message)
1064
255
    return nullptr;
1065
1066
155
  const ObjCMethodDecl *MD = Message->getDecl();
1067
155
  if (!MD)
1068
1
    return nullptr;
1069
1070
154
  const ObjCInterfaceDecl *StaticClass;
1071
154
  if (isa<ObjCProtocolDecl>(MD->getDeclContext())) {
1072
    // We can't find out where the method was declared without doing more work.
1073
    // Instead, see if the receiver is statically typed as a known immutable
1074
    // collection.
1075
4
    StaticClass = Message->getOriginExpr()->getReceiverInterface();
1076
150
  } else {
1077
150
    StaticClass = MD->getClassInterface();
1078
150
  }
1079
1080
154
  if (!StaticClass)
1081
0
    return nullptr;
1082
1083
154
  switch (findKnownClass(StaticClass, /*IncludeSuper=*/false)) {
1084
99
  case FC_None:
1085
99
    return nullptr;
1086
35
  case FC_NSArray:
1087
53
  case FC_NSDictionary:
1088
53
  case FC_NSEnumerator:
1089
53
  case FC_NSNull:
1090
54
  case FC_NSOrderedSet:
1091
55
  case FC_NSSet:
1092
55
  case FC_NSString:
1093
55
    break;
1094
154
  }
1095
1096
55
  return Message->getReceiverSVal().getAsSymbol();
1097
154
}
1098
1099
ProgramStateRef
1100
ObjCLoopChecker::checkPointerEscape(ProgramStateRef State,
1101
                                    const InvalidatedSymbols &Escaped,
1102
                                    const CallEvent *Call,
1103
410
                                    PointerEscapeKind Kind) const {
1104
410
  SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call);
1105
1106
  // Remove the invalidated symbols from the collection count map.
1107
599
  for (SymbolRef Sym : Escaped) {
1108
    // Don't invalidate this symbol's count if we know the method being called
1109
    // is declared on an immutable class. This isn't completely correct if the
1110
    // receiver is also passed as an argument, but in most uses of NSArray,
1111
    // NSDictionary, etc. this isn't likely to happen in a dangerous way.
1112
599
    if (Sym == ImmutableReceiver)
1113
37
      continue;
1114
1115
    // The symbol escaped. Pessimistically, assume that the count could have
1116
    // changed.
1117
562
    State = State->remove<ContainerCountMap>(Sym);
1118
562
    State = State->remove<ContainerNonEmptyMap>(Sym);
1119
562
  }
1120
410
  return State;
1121
410
}
1122
1123
void ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1124
2.92k
                                       CheckerContext &C) const {
1125
2.92k
  ProgramStateRef State = C.getState();
1126
1127
  // Remove the dead symbols from the collection count map.
1128
2.92k
  ContainerCountMapTy Tracked = State->get<ContainerCountMap>();
1129
2.92k
  for (SymbolRef Sym : llvm::make_first_range(Tracked)) {
1130
336
    if (SymReaper.isDead(Sym)) {
1131
43
      State = State->remove<ContainerCountMap>(Sym);
1132
43
      State = State->remove<ContainerNonEmptyMap>(Sym);
1133
43
    }
1134
336
  }
1135
1136
2.92k
  C.addTransition(State);
1137
2.92k
}
1138
1139
namespace {
1140
/// \class ObjCNonNilReturnValueChecker
1141
/// The checker restricts the return values of APIs known to
1142
/// never (or almost never) return 'nil'.
1143
class ObjCNonNilReturnValueChecker
1144
  : public Checker<check::PostObjCMessage,
1145
                   check::PostStmt<ObjCArrayLiteral>,
1146
                   check::PostStmt<ObjCDictionaryLiteral>,
1147
                   check::PostStmt<ObjCBoxedExpr> > {
1148
    mutable bool Initialized = false;
1149
    mutable Selector ObjectAtIndex;
1150
    mutable Selector ObjectAtIndexedSubscript;
1151
    mutable Selector NullSelector;
1152
1153
public:
1154
49
  ObjCNonNilReturnValueChecker() = default;
1155
1156
  ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
1157
                                      ProgramStateRef State,
1158
                                      CheckerContext &C) const;
1159
23
  void assumeExprIsNonNull(const Expr *E, CheckerContext &C) const {
1160
23
    C.addTransition(assumeExprIsNonNull(E, C.getState(), C));
1161
23
  }
1162
1163
5
  void checkPostStmt(const ObjCArrayLiteral *E, CheckerContext &C) const {
1164
5
    assumeExprIsNonNull(E, C);
1165
5
  }
1166
8
  void checkPostStmt(const ObjCDictionaryLiteral *E, CheckerContext &C) const {
1167
8
    assumeExprIsNonNull(E, C);
1168
8
  }
1169
10
  void checkPostStmt(const ObjCBoxedExpr *E, CheckerContext &C) const {
1170
10
    assumeExprIsNonNull(E, C);
1171
10
  }
1172
1173
  void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
1174
};
1175
} // end anonymous namespace
1176
1177
ProgramStateRef
1178
ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr,
1179
                                                  ProgramStateRef State,
1180
29
                                                  CheckerContext &C) const {
1181
29
  SVal Val = C.getSVal(NonNullExpr);
1182
29
  if (std::optional<DefinedOrUnknownSVal> DV =
1183
29
          Val.getAs<DefinedOrUnknownSVal>())
1184
29
    return State->assume(*DV, true);
1185
0
  return State;
1186
29
}
1187
1188
void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
1189
                                                        CheckerContext &C)
1190
76
                                                        const {
1191
76
  ProgramStateRef State = C.getState();
1192
1193
76
  if (!Initialized) {
1194
76
    ASTContext &Ctx = C.getASTContext();
1195
76
    ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
1196
76
    ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
1197
76
    NullSelector = GetNullarySelector("null", Ctx);
1198
76
  }
1199
1200
  // Check the receiver type.
1201
76
  if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
1202
1203
    // Assume that object returned from '[self init]' or '[super init]' is not
1204
    // 'nil' if we are processing an inlined function/method.
1205
    //
1206
    // A defensive callee will (and should) check if the object returned by
1207
    // '[super init]' is 'nil' before doing it's own initialization. However,
1208
    // since 'nil' is rarely returned in practice, we should not warn when the
1209
    // caller to the defensive constructor uses the object in contexts where
1210
    // 'nil' is not accepted.
1211
76
    if (!C.inTopFrame() && 
M.getDecl()2
&&
1212
76
        
M.getDecl()->getMethodFamily() == OMF_init1
&&
1213
76
        
M.isReceiverSelfOrSuper()1
) {
1214
1
      State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1215
1
    }
1216
1217
76
    FoundationClass Cl = findKnownClass(Interface);
1218
1219
    // Objects returned from
1220
    // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
1221
    // are never 'nil'.
1222
76
    if (Cl == FC_NSArray || 
Cl == FC_NSOrderedSet58
) {
1223
20
      Selector Sel = M.getSelector();
1224
20
      if (Sel == ObjectAtIndex || 
Sel == ObjectAtIndexedSubscript18
) {
1225
        // Go ahead and assume the value is non-nil.
1226
4
        State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1227
4
      }
1228
20
    }
1229
1230
    // Objects returned from [NSNull null] are not nil.
1231
76
    if (Cl == FC_NSNull) {
1232
1
      if (M.getSelector() == NullSelector) {
1233
        // Go ahead and assume the value is non-nil.
1234
1
        State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1235
1
      }
1236
1
    }
1237
76
  }
1238
76
  C.addTransition(State);
1239
76
}
1240
1241
//===----------------------------------------------------------------------===//
1242
// Check registration.
1243
//===----------------------------------------------------------------------===//
1244
1245
53
void ento::registerNilArgChecker(CheckerManager &mgr) {
1246
53
  mgr.registerChecker<NilArgChecker>();
1247
53
}
1248
1249
106
bool ento::shouldRegisterNilArgChecker(const CheckerManager &mgr) {
1250
106
  return true;
1251
106
}
1252
1253
47
void ento::registerCFNumberChecker(CheckerManager &mgr) {
1254
47
  mgr.registerChecker<CFNumberChecker>();
1255
47
}
1256
1257
94
bool ento::shouldRegisterCFNumberChecker(const CheckerManager &mgr) {
1258
94
  return true;
1259
94
}
1260
1261
57
void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
1262
57
  mgr.registerChecker<CFRetainReleaseChecker>();
1263
57
}
1264
1265
114
bool ento::shouldRegisterCFRetainReleaseChecker(const CheckerManager &mgr) {
1266
114
  return true;
1267
114
}
1268
1269
56
void ento::registerClassReleaseChecker(CheckerManager &mgr) {
1270
56
  mgr.registerChecker<ClassReleaseChecker>();
1271
56
}
1272
1273
112
bool ento::shouldRegisterClassReleaseChecker(const CheckerManager &mgr) {
1274
112
  return true;
1275
112
}
1276
1277
48
void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
1278
48
  mgr.registerChecker<VariadicMethodTypeChecker>();
1279
48
}
1280
1281
96
bool ento::shouldRegisterVariadicMethodTypeChecker(const CheckerManager &mgr) {
1282
96
  return true;
1283
96
}
1284
1285
48
void ento::registerObjCLoopChecker(CheckerManager &mgr) {
1286
48
  mgr.registerChecker<ObjCLoopChecker>();
1287
48
}
1288
1289
96
bool ento::shouldRegisterObjCLoopChecker(const CheckerManager &mgr) {
1290
96
  return true;
1291
96
}
1292
1293
49
void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
1294
49
  mgr.registerChecker<ObjCNonNilReturnValueChecker>();
1295
49
}
1296
1297
98
bool ento::shouldRegisterObjCNonNilReturnValueChecker(const CheckerManager &mgr) {
1298
98
  return true;
1299
98
}