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/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, categories::AppleAPIMisuse) {} 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 = nullptr, *IGetValue = nullptr; 355 public: 356 CFNumberChecker() = default; 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 {CDM::CLibrary, {"CFRetain"}, 1}, 545 {CDM::CLibrary, {"CFRelease"}, 1}, 546 {CDM::CLibrary, {"CFMakeCollectable"}, 1}, 547 {CDM::CLibrary, {"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 // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease. 558 if (!ModelledCalls.contains(Call)) 559 return; 560 561 // Get the argument's value. 562 SVal ArgVal = Call.getArgSVal(0); 563 std::optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); 564 if (!DefArgVal) 565 return; 566 567 // Is it null? 568 ProgramStateRef state = C.getState(); 569 ProgramStateRef stateNonNull, stateNull; 570 std::tie(stateNonNull, stateNull) = state->assume(*DefArgVal); 571 572 if (!stateNonNull) { 573 ExplodedNode *N = C.generateErrorNode(stateNull); 574 if (!N) 575 return; 576 577 SmallString<64> Str; 578 raw_svector_ostream OS(Str); 579 OS << "Null pointer argument in call to " 580 << cast<FunctionDecl>(Call.getDecl())->getName(); 581 582 auto report = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N); 583 report->addRange(Call.getArgSourceRange(0)); 584 bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *report); 585 C.emitReport(std::move(report)); 586 return; 587 } 588 589 // From here on, we know the argument is non-null. 590 C.addTransition(stateNonNull); 591 } 592 593 //===----------------------------------------------------------------------===// 594 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class. 595 //===----------------------------------------------------------------------===// 596 597 namespace { 598 class ClassReleaseChecker : public Checker<check::PreObjCMessage> { 599 mutable Selector releaseS; 600 mutable Selector retainS; 601 mutable Selector autoreleaseS; 602 mutable Selector drainS; 603 mutable std::unique_ptr<BugType> BT; 604 605 public: 606 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 607 }; 608 } // end anonymous namespace 609 610 void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 611 CheckerContext &C) const { 612 if (!BT) { 613 BT.reset(new APIMisuse( 614 this, "message incorrectly sent to class instead of class instance")); 615 616 ASTContext &Ctx = C.getASTContext(); 617 releaseS = GetNullarySelector("release", Ctx); 618 retainS = GetNullarySelector("retain", Ctx); 619 autoreleaseS = GetNullarySelector("autorelease", Ctx); 620 drainS = GetNullarySelector("drain", Ctx); 621 } 622 623 if (msg.isInstanceMessage()) 624 return; 625 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 626 assert(Class); 627 628 Selector S = msg.getSelector(); 629 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) 630 return; 631 632 if (ExplodedNode *N = C.generateNonFatalErrorNode()) { 633 SmallString<200> buf; 634 llvm::raw_svector_ostream os(buf); 635 636 os << "The '"; 637 S.print(os); 638 os << "' message should be sent to instances " 639 "of class '" << Class->getName() 640 << "' and not the class directly"; 641 642 auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); 643 report->addRange(msg.getSourceRange()); 644 C.emitReport(std::move(report)); 645 } 646 } 647 648 //===----------------------------------------------------------------------===// 649 // Check for passing non-Objective-C types to variadic methods that expect 650 // only Objective-C types. 651 //===----------------------------------------------------------------------===// 652 653 namespace { 654 class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> { 655 mutable Selector arrayWithObjectsS; 656 mutable Selector dictionaryWithObjectsAndKeysS; 657 mutable Selector setWithObjectsS; 658 mutable Selector orderedSetWithObjectsS; 659 mutable Selector initWithObjectsS; 660 mutable Selector initWithObjectsAndKeysS; 661 mutable std::unique_ptr<BugType> BT; 662 663 bool isVariadicMessage(const ObjCMethodCall &msg) const; 664 665 public: 666 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 667 }; 668 } // end anonymous namespace 669 670 /// isVariadicMessage - Returns whether the given message is a variadic message, 671 /// where all arguments must be Objective-C types. 672 bool 673 VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const { 674 const ObjCMethodDecl *MD = msg.getDecl(); 675 676 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext())) 677 return false; 678 679 Selector S = msg.getSelector(); 680 681 if (msg.isInstanceMessage()) { 682 // FIXME: Ideally we'd look at the receiver interface here, but that's not 683 // useful for init, because alloc returns 'id'. In theory, this could lead 684 // to false positives, for example if there existed a class that had an 685 // initWithObjects: implementation that does accept non-Objective-C pointer 686 // types, but the chance of that happening is pretty small compared to the 687 // gains that this analysis gives. 688 const ObjCInterfaceDecl *Class = MD->getClassInterface(); 689 690 switch (findKnownClass(Class)) { 691 case FC_NSArray: 692 case FC_NSOrderedSet: 693 case FC_NSSet: 694 return S == initWithObjectsS; 695 case FC_NSDictionary: 696 return S == initWithObjectsAndKeysS; 697 default: 698 return false; 699 } 700 } else { 701 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 702 703 switch (findKnownClass(Class)) { 704 case FC_NSArray: 705 return S == arrayWithObjectsS; 706 case FC_NSOrderedSet: 707 return S == orderedSetWithObjectsS; 708 case FC_NSSet: 709 return S == setWithObjectsS; 710 case FC_NSDictionary: 711 return S == dictionaryWithObjectsAndKeysS; 712 default: 713 return false; 714 } 715 } 716 } 717 718 void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 719 CheckerContext &C) const { 720 if (!BT) { 721 BT.reset(new APIMisuse(this, 722 "Arguments passed to variadic method aren't all " 723 "Objective-C pointer types")); 724 725 ASTContext &Ctx = C.getASTContext(); 726 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); 727 dictionaryWithObjectsAndKeysS = 728 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); 729 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); 730 orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx); 731 732 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); 733 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); 734 } 735 736 if (!isVariadicMessage(msg)) 737 return; 738 739 // We are not interested in the selector arguments since they have 740 // well-defined types, so the compiler will issue a warning for them. 741 unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); 742 743 // We're not interested in the last argument since it has to be nil or the 744 // compiler would have issued a warning for it elsewhere. 745 unsigned variadicArgsEnd = msg.getNumArgs() - 1; 746 747 if (variadicArgsEnd <= variadicArgsBegin) 748 return; 749 750 // Verify that all arguments have Objective-C types. 751 std::optional<ExplodedNode *> errorNode; 752 753 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { 754 QualType ArgTy = msg.getArgExpr(I)->getType(); 755 if (ArgTy->isObjCObjectPointerType()) 756 continue; 757 758 // Block pointers are treaded as Objective-C pointers. 759 if (ArgTy->isBlockPointerType()) 760 continue; 761 762 // Ignore pointer constants. 763 if (isa<loc::ConcreteInt>(msg.getArgSVal(I))) 764 continue; 765 766 // Ignore pointer types annotated with 'NSObject' attribute. 767 if (C.getASTContext().isObjCNSObjectType(ArgTy)) 768 continue; 769 770 // Ignore CF references, which can be toll-free bridged. 771 if (coreFoundation::isCFObjectRef(ArgTy)) 772 continue; 773 774 // Generate only one error node to use for all bug reports. 775 if (!errorNode) 776 errorNode = C.generateNonFatalErrorNode(); 777 778 if (!*errorNode) 779 continue; 780 781 SmallString<128> sbuf; 782 llvm::raw_svector_ostream os(sbuf); 783 784 StringRef TypeName = GetReceiverInterfaceName(msg); 785 if (!TypeName.empty()) 786 os << "Argument to '" << TypeName << "' method '"; 787 else 788 os << "Argument to method '"; 789 790 msg.getSelector().print(os); 791 os << "' should be an Objective-C pointer type, not '"; 792 ArgTy.print(os, C.getLangOpts()); 793 os << "'"; 794 795 auto R = 796 std::make_unique<PathSensitiveBugReport>(*BT, os.str(), *errorNode); 797 R->addRange(msg.getArgSourceRange(I)); 798 C.emitReport(std::move(R)); 799 } 800 } 801 802 //===----------------------------------------------------------------------===// 803 // Improves the modeling of loops over Cocoa collections. 804 //===----------------------------------------------------------------------===// 805 806 // The map from container symbol to the container count symbol. 807 // We currently will remember the last container count symbol encountered. 808 REGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap, SymbolRef, SymbolRef) 809 REGISTER_MAP_WITH_PROGRAMSTATE(ContainerNonEmptyMap, SymbolRef, bool) 810 811 namespace { 812 class ObjCLoopChecker 813 : public Checker<check::PostStmt<ObjCForCollectionStmt>, 814 check::PostObjCMessage, 815 check::DeadSymbols, 816 check::PointerEscape > { 817 mutable IdentifierInfo *CountSelectorII = nullptr; 818 819 bool isCollectionCountMethod(const ObjCMethodCall &M, 820 CheckerContext &C) const; 821 822 public: 823 ObjCLoopChecker() = default; 824 void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const; 825 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 826 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 827 ProgramStateRef checkPointerEscape(ProgramStateRef State, 828 const InvalidatedSymbols &Escaped, 829 const CallEvent *Call, 830 PointerEscapeKind Kind) const; 831 }; 832 } // end anonymous namespace 833 834 static bool isKnownNonNilCollectionType(QualType T) { 835 const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); 836 if (!PT) 837 return false; 838 839 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); 840 if (!ID) 841 return false; 842 843 switch (findKnownClass(ID)) { 844 case FC_NSArray: 845 case FC_NSDictionary: 846 case FC_NSEnumerator: 847 case FC_NSOrderedSet: 848 case FC_NSSet: 849 return true; 850 default: 851 return false; 852 } 853 } 854 855 /// Assumes that the collection is non-nil. 856 /// 857 /// If the collection is known to be nil, returns NULL to indicate an infeasible 858 /// path. 859 static ProgramStateRef checkCollectionNonNil(CheckerContext &C, 860 ProgramStateRef State, 861 const ObjCForCollectionStmt *FCS) { 862 if (!State) 863 return nullptr; 864 865 SVal CollectionVal = C.getSVal(FCS->getCollection()); 866 std::optional<DefinedSVal> KnownCollection = 867 CollectionVal.getAs<DefinedSVal>(); 868 if (!KnownCollection) 869 return State; 870 871 ProgramStateRef StNonNil, StNil; 872 std::tie(StNonNil, StNil) = State->assume(*KnownCollection); 873 if (StNil && !StNonNil) { 874 // The collection is nil. This path is infeasible. 875 return nullptr; 876 } 877 878 return StNonNil; 879 } 880 881 /// Assumes that the collection elements are non-nil. 882 /// 883 /// This only applies if the collection is one of those known not to contain 884 /// nil values. 885 static ProgramStateRef checkElementNonNil(CheckerContext &C, 886 ProgramStateRef State, 887 const ObjCForCollectionStmt *FCS) { 888 if (!State) 889 return nullptr; 890 891 // See if the collection is one where we /know/ the elements are non-nil. 892 if (!isKnownNonNilCollectionType(FCS->getCollection()->getType())) 893 return State; 894 895 const LocationContext *LCtx = C.getLocationContext(); 896 const Stmt *Element = FCS->getElement(); 897 898 // FIXME: Copied from ExprEngineObjC. 899 std::optional<Loc> ElementLoc; 900 if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { 901 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); 902 assert(ElemDecl->getInit() == nullptr); 903 ElementLoc = State->getLValue(ElemDecl, LCtx); 904 } else { 905 ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>(); 906 } 907 908 if (!ElementLoc) 909 return State; 910 911 // Go ahead and assume the value is non-nil. 912 SVal Val = State->getSVal(*ElementLoc); 913 return State->assume(cast<DefinedOrUnknownSVal>(Val), true); 914 } 915 916 /// Returns NULL state if the collection is known to contain elements 917 /// (or is known not to contain elements if the Assumption parameter is false.) 918 static ProgramStateRef 919 assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, 920 SymbolRef CollectionS, bool Assumption) { 921 if (!State || !CollectionS) 922 return State; 923 924 const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS); 925 if (!CountS) { 926 const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS); 927 if (!KnownNonEmpty) 928 return State->set<ContainerNonEmptyMap>(CollectionS, Assumption); 929 return (Assumption == *KnownNonEmpty) ? State : nullptr; 930 } 931 932 SValBuilder &SvalBuilder = C.getSValBuilder(); 933 SVal CountGreaterThanZeroVal = 934 SvalBuilder.evalBinOp(State, BO_GT, 935 nonloc::SymbolVal(*CountS), 936 SvalBuilder.makeIntVal(0, (*CountS)->getType()), 937 SvalBuilder.getConditionType()); 938 std::optional<DefinedSVal> CountGreaterThanZero = 939 CountGreaterThanZeroVal.getAs<DefinedSVal>(); 940 if (!CountGreaterThanZero) { 941 // The SValBuilder cannot construct a valid SVal for this condition. 942 // This means we cannot properly reason about it. 943 return State; 944 } 945 946 return State->assume(*CountGreaterThanZero, Assumption); 947 } 948 949 static ProgramStateRef 950 assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, 951 const ObjCForCollectionStmt *FCS, 952 bool Assumption) { 953 if (!State) 954 return nullptr; 955 956 SymbolRef CollectionS = C.getSVal(FCS->getCollection()).getAsSymbol(); 957 return assumeCollectionNonEmpty(C, State, CollectionS, Assumption); 958 } 959 960 /// If the fist block edge is a back edge, we are reentering the loop. 961 static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N, 962 const ObjCForCollectionStmt *FCS) { 963 if (!N) 964 return false; 965 966 ProgramPoint P = N->getLocation(); 967 if (std::optional<BlockEdge> BE = P.getAs<BlockEdge>()) { 968 return BE->getSrc()->getLoopTarget() == FCS; 969 } 970 971 // Keep looking for a block edge. 972 for (const ExplodedNode *N : N->preds()) { 973 if (alreadyExecutedAtLeastOneLoopIteration(N, FCS)) 974 return true; 975 } 976 977 return false; 978 } 979 980 void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, 981 CheckerContext &C) const { 982 ProgramStateRef State = C.getState(); 983 984 // Check if this is the branch for the end of the loop. 985 if (!ExprEngine::hasMoreIteration(State, FCS, C.getLocationContext())) { 986 if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS)) 987 State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false); 988 989 // Otherwise, this is a branch that goes through the loop body. 990 } else { 991 State = checkCollectionNonNil(C, State, FCS); 992 State = checkElementNonNil(C, State, FCS); 993 State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true); 994 } 995 996 if (!State) 997 C.generateSink(C.getState(), C.getPredecessor()); 998 else if (State != C.getState()) 999 C.addTransition(State); 1000 } 1001 1002 bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M, 1003 CheckerContext &C) const { 1004 Selector S = M.getSelector(); 1005 // Initialize the identifiers on first use. 1006 if (!CountSelectorII) 1007 CountSelectorII = &C.getASTContext().Idents.get("count"); 1008 1009 // If the method returns collection count, record the value. 1010 return S.isUnarySelector() && 1011 (S.getIdentifierInfoForSlot(0) == CountSelectorII); 1012 } 1013 1014 void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M, 1015 CheckerContext &C) const { 1016 if (!M.isInstanceMessage()) 1017 return; 1018 1019 const ObjCInterfaceDecl *ClassID = M.getReceiverInterface(); 1020 if (!ClassID) 1021 return; 1022 1023 FoundationClass Class = findKnownClass(ClassID); 1024 if (Class != FC_NSDictionary && 1025 Class != FC_NSArray && 1026 Class != FC_NSSet && 1027 Class != FC_NSOrderedSet) 1028 return; 1029 1030 SymbolRef ContainerS = M.getReceiverSVal().getAsSymbol(); 1031 if (!ContainerS) 1032 return; 1033 1034 // If we are processing a call to "count", get the symbolic value returned by 1035 // a call to "count" and add it to the map. 1036 if (!isCollectionCountMethod(M, C)) 1037 return; 1038 1039 const Expr *MsgExpr = M.getOriginExpr(); 1040 SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol(); 1041 if (CountS) { 1042 ProgramStateRef State = C.getState(); 1043 1044 C.getSymbolManager().addSymbolDependency(ContainerS, CountS); 1045 State = State->set<ContainerCountMap>(ContainerS, CountS); 1046 1047 if (const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) { 1048 State = State->remove<ContainerNonEmptyMap>(ContainerS); 1049 State = assumeCollectionNonEmpty(C, State, ContainerS, *NonEmpty); 1050 } 1051 1052 C.addTransition(State); 1053 } 1054 } 1055 1056 static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call) { 1057 const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call); 1058 if (!Message) 1059 return nullptr; 1060 1061 const ObjCMethodDecl *MD = Message->getDecl(); 1062 if (!MD) 1063 return nullptr; 1064 1065 const ObjCInterfaceDecl *StaticClass; 1066 if (isa<ObjCProtocolDecl>(MD->getDeclContext())) { 1067 // We can't find out where the method was declared without doing more work. 1068 // Instead, see if the receiver is statically typed as a known immutable 1069 // collection. 1070 StaticClass = Message->getOriginExpr()->getReceiverInterface(); 1071 } else { 1072 StaticClass = MD->getClassInterface(); 1073 } 1074 1075 if (!StaticClass) 1076 return nullptr; 1077 1078 switch (findKnownClass(StaticClass, /*IncludeSuper=*/false)) { 1079 case FC_None: 1080 return nullptr; 1081 case FC_NSArray: 1082 case FC_NSDictionary: 1083 case FC_NSEnumerator: 1084 case FC_NSNull: 1085 case FC_NSOrderedSet: 1086 case FC_NSSet: 1087 case FC_NSString: 1088 break; 1089 } 1090 1091 return Message->getReceiverSVal().getAsSymbol(); 1092 } 1093 1094 ProgramStateRef 1095 ObjCLoopChecker::checkPointerEscape(ProgramStateRef State, 1096 const InvalidatedSymbols &Escaped, 1097 const CallEvent *Call, 1098 PointerEscapeKind Kind) const { 1099 SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call); 1100 1101 // Remove the invalidated symbols from the collection count map. 1102 for (SymbolRef Sym : Escaped) { 1103 // Don't invalidate this symbol's count if we know the method being called 1104 // is declared on an immutable class. This isn't completely correct if the 1105 // receiver is also passed as an argument, but in most uses of NSArray, 1106 // NSDictionary, etc. this isn't likely to happen in a dangerous way. 1107 if (Sym == ImmutableReceiver) 1108 continue; 1109 1110 // The symbol escaped. Pessimistically, assume that the count could have 1111 // changed. 1112 State = State->remove<ContainerCountMap>(Sym); 1113 State = State->remove<ContainerNonEmptyMap>(Sym); 1114 } 1115 return State; 1116 } 1117 1118 void ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper, 1119 CheckerContext &C) const { 1120 ProgramStateRef State = C.getState(); 1121 1122 // Remove the dead symbols from the collection count map. 1123 ContainerCountMapTy Tracked = State->get<ContainerCountMap>(); 1124 for (SymbolRef Sym : llvm::make_first_range(Tracked)) { 1125 if (SymReaper.isDead(Sym)) { 1126 State = State->remove<ContainerCountMap>(Sym); 1127 State = State->remove<ContainerNonEmptyMap>(Sym); 1128 } 1129 } 1130 1131 C.addTransition(State); 1132 } 1133 1134 namespace { 1135 /// \class ObjCNonNilReturnValueChecker 1136 /// The checker restricts the return values of APIs known to 1137 /// never (or almost never) return 'nil'. 1138 class ObjCNonNilReturnValueChecker 1139 : public Checker<check::PostObjCMessage, 1140 check::PostStmt<ObjCArrayLiteral>, 1141 check::PostStmt<ObjCDictionaryLiteral>, 1142 check::PostStmt<ObjCBoxedExpr> > { 1143 mutable bool Initialized = false; 1144 mutable Selector ObjectAtIndex; 1145 mutable Selector ObjectAtIndexedSubscript; 1146 mutable Selector NullSelector; 1147 1148 public: 1149 ObjCNonNilReturnValueChecker() = default; 1150 1151 ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr, 1152 ProgramStateRef State, 1153 CheckerContext &C) const; 1154 void assumeExprIsNonNull(const Expr *E, CheckerContext &C) const { 1155 C.addTransition(assumeExprIsNonNull(E, C.getState(), C)); 1156 } 1157 1158 void checkPostStmt(const ObjCArrayLiteral *E, CheckerContext &C) const { 1159 assumeExprIsNonNull(E, C); 1160 } 1161 void checkPostStmt(const ObjCDictionaryLiteral *E, CheckerContext &C) const { 1162 assumeExprIsNonNull(E, C); 1163 } 1164 void checkPostStmt(const ObjCBoxedExpr *E, CheckerContext &C) const { 1165 assumeExprIsNonNull(E, C); 1166 } 1167 1168 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 1169 }; 1170 } // end anonymous namespace 1171 1172 ProgramStateRef 1173 ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr, 1174 ProgramStateRef State, 1175 CheckerContext &C) const { 1176 SVal Val = C.getSVal(NonNullExpr); 1177 if (std::optional<DefinedOrUnknownSVal> DV = 1178 Val.getAs<DefinedOrUnknownSVal>()) 1179 return State->assume(*DV, true); 1180 return State; 1181 } 1182 1183 void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M, 1184 CheckerContext &C) 1185 const { 1186 ProgramStateRef State = C.getState(); 1187 1188 if (!Initialized) { 1189 ASTContext &Ctx = C.getASTContext(); 1190 ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx); 1191 ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx); 1192 NullSelector = GetNullarySelector("null", Ctx); 1193 } 1194 1195 // Check the receiver type. 1196 if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) { 1197 1198 // Assume that object returned from '[self init]' or '[super init]' is not 1199 // 'nil' if we are processing an inlined function/method. 1200 // 1201 // A defensive callee will (and should) check if the object returned by 1202 // '[super init]' is 'nil' before doing it's own initialization. However, 1203 // since 'nil' is rarely returned in practice, we should not warn when the 1204 // caller to the defensive constructor uses the object in contexts where 1205 // 'nil' is not accepted. 1206 if (!C.inTopFrame() && M.getDecl() && 1207 M.getDecl()->getMethodFamily() == OMF_init && 1208 M.isReceiverSelfOrSuper()) { 1209 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 1210 } 1211 1212 FoundationClass Cl = findKnownClass(Interface); 1213 1214 // Objects returned from 1215 // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript] 1216 // are never 'nil'. 1217 if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) { 1218 Selector Sel = M.getSelector(); 1219 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) { 1220 // Go ahead and assume the value is non-nil. 1221 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 1222 } 1223 } 1224 1225 // Objects returned from [NSNull null] are not nil. 1226 if (Cl == FC_NSNull) { 1227 if (M.getSelector() == NullSelector) { 1228 // Go ahead and assume the value is non-nil. 1229 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 1230 } 1231 } 1232 } 1233 C.addTransition(State); 1234 } 1235 1236 //===----------------------------------------------------------------------===// 1237 // Check registration. 1238 //===----------------------------------------------------------------------===// 1239 1240 void ento::registerNilArgChecker(CheckerManager &mgr) { 1241 mgr.registerChecker<NilArgChecker>(); 1242 } 1243 1244 bool ento::shouldRegisterNilArgChecker(const CheckerManager &mgr) { 1245 return true; 1246 } 1247 1248 void ento::registerCFNumberChecker(CheckerManager &mgr) { 1249 mgr.registerChecker<CFNumberChecker>(); 1250 } 1251 1252 bool ento::shouldRegisterCFNumberChecker(const CheckerManager &mgr) { 1253 return true; 1254 } 1255 1256 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { 1257 mgr.registerChecker<CFRetainReleaseChecker>(); 1258 } 1259 1260 bool ento::shouldRegisterCFRetainReleaseChecker(const CheckerManager &mgr) { 1261 return true; 1262 } 1263 1264 void ento::registerClassReleaseChecker(CheckerManager &mgr) { 1265 mgr.registerChecker<ClassReleaseChecker>(); 1266 } 1267 1268 bool ento::shouldRegisterClassReleaseChecker(const CheckerManager &mgr) { 1269 return true; 1270 } 1271 1272 void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { 1273 mgr.registerChecker<VariadicMethodTypeChecker>(); 1274 } 1275 1276 bool ento::shouldRegisterVariadicMethodTypeChecker(const CheckerManager &mgr) { 1277 return true; 1278 } 1279 1280 void ento::registerObjCLoopChecker(CheckerManager &mgr) { 1281 mgr.registerChecker<ObjCLoopChecker>(); 1282 } 1283 1284 bool ento::shouldRegisterObjCLoopChecker(const CheckerManager &mgr) { 1285 return true; 1286 } 1287 1288 void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) { 1289 mgr.registerChecker<ObjCNonNilReturnValueChecker>(); 1290 } 1291 1292 bool ento::shouldRegisterObjCNonNilReturnValueChecker(const CheckerManager &mgr) { 1293 return true; 1294 } 1295