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