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