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