1 //== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*-- 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file defines BasicObjCFoundationChecks, a class that encapsulates 11 // a set of simple checks to run on Objective-C code using Apple's Foundation 12 // classes. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #include "ClangSACheckers.h" 17 #include "clang/AST/ASTContext.h" 18 #include "clang/AST/DeclObjC.h" 19 #include "clang/AST/Expr.h" 20 #include "clang/AST/ExprObjC.h" 21 #include "clang/AST/StmtObjC.h" 22 #include "clang/Analysis/DomainSpecific/CocoaConventions.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/CallEvent.h" 27 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 28 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 29 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 30 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" 31 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 32 #include "llvm/ADT/SmallString.h" 33 #include "llvm/ADT/StringMap.h" 34 #include "llvm/Support/raw_ostream.h" 35 36 using namespace clang; 37 using namespace ento; 38 39 namespace { 40 class APIMisuse : public BugType { 41 public: 42 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} 43 }; 44 } // end anonymous namespace 45 46 //===----------------------------------------------------------------------===// 47 // Utility functions. 48 //===----------------------------------------------------------------------===// 49 50 static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) { 51 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) 52 return ID->getIdentifier()->getName(); 53 return StringRef(); 54 } 55 56 enum FoundationClass { 57 FC_None, 58 FC_NSArray, 59 FC_NSDictionary, 60 FC_NSEnumerator, 61 FC_NSOrderedSet, 62 FC_NSSet, 63 FC_NSString 64 }; 65 66 static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID) { 67 static llvm::StringMap<FoundationClass> Classes; 68 if (Classes.empty()) { 69 Classes["NSArray"] = FC_NSArray; 70 Classes["NSDictionary"] = FC_NSDictionary; 71 Classes["NSEnumerator"] = FC_NSEnumerator; 72 Classes["NSOrderedSet"] = FC_NSOrderedSet; 73 Classes["NSSet"] = FC_NSSet; 74 Classes["NSString"] = FC_NSString; 75 } 76 77 // FIXME: Should we cache this at all? 78 FoundationClass result = Classes.lookup(ID->getIdentifier()->getName()); 79 if (result == FC_None) 80 if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) 81 return findKnownClass(Super); 82 83 return result; 84 } 85 86 static inline bool isNil(SVal X) { 87 return X.getAs<loc::ConcreteInt>().hasValue(); 88 } 89 90 //===----------------------------------------------------------------------===// 91 // NilArgChecker - Check for prohibited nil arguments to ObjC method calls. 92 //===----------------------------------------------------------------------===// 93 94 namespace { 95 class NilArgChecker : public Checker<check::PreObjCMessage> { 96 mutable OwningPtr<APIMisuse> BT; 97 98 void WarnNilArg(CheckerContext &C, 99 const ObjCMethodCall &msg, unsigned Arg) const; 100 101 public: 102 void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 103 }; 104 } 105 106 void NilArgChecker::WarnNilArg(CheckerContext &C, 107 const ObjCMethodCall &msg, 108 unsigned int Arg) const 109 { 110 if (!BT) 111 BT.reset(new APIMisuse("nil argument")); 112 113 if (ExplodedNode *N = C.generateSink()) { 114 SmallString<128> sbuf; 115 llvm::raw_svector_ostream os(sbuf); 116 os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '" 117 << msg.getSelector().getAsString() << "' cannot be nil"; 118 119 BugReport *R = new BugReport(*BT, os.str(), N); 120 R->addRange(msg.getArgSourceRange(Arg)); 121 C.emitReport(R); 122 } 123 } 124 125 void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 126 CheckerContext &C) const { 127 const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); 128 if (!ID) 129 return; 130 131 FoundationClass Class = findKnownClass(ID); 132 133 static const unsigned InvalidArgIndex = UINT_MAX; 134 unsigned Arg = InvalidArgIndex; 135 136 if (Class == FC_NSString) { 137 Selector S = msg.getSelector(); 138 139 if (S.isUnarySelector()) 140 return; 141 142 // FIXME: This is going to be really slow doing these checks with 143 // lexical comparisons. 144 145 std::string NameStr = S.getAsString(); 146 StringRef Name(NameStr); 147 assert(!Name.empty()); 148 149 // FIXME: Checking for initWithFormat: will not work in most cases 150 // yet because [NSString alloc] returns id, not NSString*. We will 151 // need support for tracking expected-type information in the analyzer 152 // to find these errors. 153 if (Name == "caseInsensitiveCompare:" || 154 Name == "compare:" || 155 Name == "compare:options:" || 156 Name == "compare:options:range:" || 157 Name == "compare:options:range:locale:" || 158 Name == "componentsSeparatedByCharactersInSet:" || 159 Name == "initWithFormat:") { 160 Arg = 0; 161 } 162 } else if (Class == FC_NSArray) { 163 Selector S = msg.getSelector(); 164 165 if (S.isUnarySelector()) 166 return; 167 168 if (S.getNameForSlot(0).equals("addObject")) { 169 Arg = 0; 170 } else if (S.getNameForSlot(0).equals("insertObject") && 171 S.getNameForSlot(1).equals("atIndex")) { 172 Arg = 0; 173 } else if (S.getNameForSlot(0).equals("replaceObjectAtIndex") && 174 S.getNameForSlot(1).equals("withObject")) { 175 Arg = 1; 176 } else if (S.getNameForSlot(0).equals("setObject") && 177 S.getNameForSlot(1).equals("atIndexedSubscript")) { 178 Arg = 0; 179 } else if (S.getNameForSlot(0).equals("arrayByAddingObject")) { 180 Arg = 0; 181 } 182 } 183 184 // If argument is '0', report a warning. 185 if ((Arg != InvalidArgIndex) && isNil(msg.getArgSVal(Arg))) 186 WarnNilArg(C, msg, Arg); 187 188 } 189 190 //===----------------------------------------------------------------------===// 191 // Error reporting. 192 //===----------------------------------------------------------------------===// 193 194 namespace { 195 class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > { 196 mutable OwningPtr<APIMisuse> BT; 197 mutable IdentifierInfo* II; 198 public: 199 CFNumberCreateChecker() : II(0) {} 200 201 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 202 203 private: 204 void EmitError(const TypedRegion* R, const Expr *Ex, 205 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); 206 }; 207 } // end anonymous namespace 208 209 enum CFNumberType { 210 kCFNumberSInt8Type = 1, 211 kCFNumberSInt16Type = 2, 212 kCFNumberSInt32Type = 3, 213 kCFNumberSInt64Type = 4, 214 kCFNumberFloat32Type = 5, 215 kCFNumberFloat64Type = 6, 216 kCFNumberCharType = 7, 217 kCFNumberShortType = 8, 218 kCFNumberIntType = 9, 219 kCFNumberLongType = 10, 220 kCFNumberLongLongType = 11, 221 kCFNumberFloatType = 12, 222 kCFNumberDoubleType = 13, 223 kCFNumberCFIndexType = 14, 224 kCFNumberNSIntegerType = 15, 225 kCFNumberCGFloatType = 16 226 }; 227 228 static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { 229 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; 230 231 if (i < kCFNumberCharType) 232 return FixedSize[i-1]; 233 234 QualType T; 235 236 switch (i) { 237 case kCFNumberCharType: T = Ctx.CharTy; break; 238 case kCFNumberShortType: T = Ctx.ShortTy; break; 239 case kCFNumberIntType: T = Ctx.IntTy; break; 240 case kCFNumberLongType: T = Ctx.LongTy; break; 241 case kCFNumberLongLongType: T = Ctx.LongLongTy; break; 242 case kCFNumberFloatType: T = Ctx.FloatTy; break; 243 case kCFNumberDoubleType: T = Ctx.DoubleTy; break; 244 case kCFNumberCFIndexType: 245 case kCFNumberNSIntegerType: 246 case kCFNumberCGFloatType: 247 // FIXME: We need a way to map from names to Type*. 248 default: 249 return None; 250 } 251 252 return Ctx.getTypeSize(T); 253 } 254 255 #if 0 256 static const char* GetCFNumberTypeStr(uint64_t i) { 257 static const char* Names[] = { 258 "kCFNumberSInt8Type", 259 "kCFNumberSInt16Type", 260 "kCFNumberSInt32Type", 261 "kCFNumberSInt64Type", 262 "kCFNumberFloat32Type", 263 "kCFNumberFloat64Type", 264 "kCFNumberCharType", 265 "kCFNumberShortType", 266 "kCFNumberIntType", 267 "kCFNumberLongType", 268 "kCFNumberLongLongType", 269 "kCFNumberFloatType", 270 "kCFNumberDoubleType", 271 "kCFNumberCFIndexType", 272 "kCFNumberNSIntegerType", 273 "kCFNumberCGFloatType" 274 }; 275 276 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType"; 277 } 278 #endif 279 280 void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, 281 CheckerContext &C) const { 282 ProgramStateRef state = C.getState(); 283 const FunctionDecl *FD = C.getCalleeDecl(CE); 284 if (!FD) 285 return; 286 287 ASTContext &Ctx = C.getASTContext(); 288 if (!II) 289 II = &Ctx.Idents.get("CFNumberCreate"); 290 291 if (FD->getIdentifier() != II || CE->getNumArgs() != 3) 292 return; 293 294 // Get the value of the "theType" argument. 295 const LocationContext *LCtx = C.getLocationContext(); 296 SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx); 297 298 // FIXME: We really should allow ranges of valid theType values, and 299 // bifurcate the state appropriately. 300 Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>(); 301 if (!V) 302 return; 303 304 uint64_t NumberKind = V->getValue().getLimitedValue(); 305 Optional<uint64_t> OptTargetSize = GetCFNumberSize(Ctx, NumberKind); 306 307 // FIXME: In some cases we can emit an error. 308 if (!OptTargetSize) 309 return; 310 311 uint64_t TargetSize = *OptTargetSize; 312 313 // Look at the value of the integer being passed by reference. Essentially 314 // we want to catch cases where the value passed in is not equal to the 315 // size of the type being created. 316 SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx); 317 318 // FIXME: Eventually we should handle arbitrary locations. We can do this 319 // by having an enhanced memory model that does low-level typing. 320 Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>(); 321 if (!LV) 322 return; 323 324 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts()); 325 if (!R) 326 return; 327 328 QualType T = Ctx.getCanonicalType(R->getValueType()); 329 330 // FIXME: If the pointee isn't an integer type, should we flag a warning? 331 // People can do weird stuff with pointers. 332 333 if (!T->isIntegerType()) 334 return; 335 336 uint64_t SourceSize = Ctx.getTypeSize(T); 337 338 // CHECK: is SourceSize == TargetSize 339 if (SourceSize == TargetSize) 340 return; 341 342 // Generate an error. Only generate a sink if 'SourceSize < TargetSize'; 343 // otherwise generate a regular node. 344 // 345 // FIXME: We can actually create an abstract "CFNumber" object that has 346 // the bits initialized to the provided values. 347 // 348 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink() 349 : C.addTransition()) { 350 SmallString<128> sbuf; 351 llvm::raw_svector_ostream os(sbuf); 352 353 os << (SourceSize == 8 ? "An " : "A ") 354 << SourceSize << " bit integer is used to initialize a CFNumber " 355 "object that represents " 356 << (TargetSize == 8 ? "an " : "a ") 357 << TargetSize << " bit integer. "; 358 359 if (SourceSize < TargetSize) 360 os << (TargetSize - SourceSize) 361 << " bits of the CFNumber value will be garbage." ; 362 else 363 os << (SourceSize - TargetSize) 364 << " bits of the input integer will be lost."; 365 366 if (!BT) 367 BT.reset(new APIMisuse("Bad use of CFNumberCreate")); 368 369 BugReport *report = new BugReport(*BT, os.str(), N); 370 report->addRange(CE->getArg(2)->getSourceRange()); 371 C.emitReport(report); 372 } 373 } 374 375 //===----------------------------------------------------------------------===// 376 // CFRetain/CFRelease/CFMakeCollectable checking for null arguments. 377 //===----------------------------------------------------------------------===// 378 379 namespace { 380 class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > { 381 mutable OwningPtr<APIMisuse> BT; 382 mutable IdentifierInfo *Retain, *Release, *MakeCollectable; 383 public: 384 CFRetainReleaseChecker(): Retain(0), Release(0), MakeCollectable(0) {} 385 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 386 }; 387 } // end anonymous namespace 388 389 390 void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, 391 CheckerContext &C) const { 392 // If the CallExpr doesn't have exactly 1 argument just give up checking. 393 if (CE->getNumArgs() != 1) 394 return; 395 396 ProgramStateRef state = C.getState(); 397 const FunctionDecl *FD = C.getCalleeDecl(CE); 398 if (!FD) 399 return; 400 401 if (!BT) { 402 ASTContext &Ctx = C.getASTContext(); 403 Retain = &Ctx.Idents.get("CFRetain"); 404 Release = &Ctx.Idents.get("CFRelease"); 405 MakeCollectable = &Ctx.Idents.get("CFMakeCollectable"); 406 BT.reset( 407 new APIMisuse("null passed to CFRetain/CFRelease/CFMakeCollectable")); 408 } 409 410 // Check if we called CFRetain/CFRelease/CFMakeCollectable. 411 const IdentifierInfo *FuncII = FD->getIdentifier(); 412 if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable)) 413 return; 414 415 // FIXME: The rest of this just checks that the argument is non-null. 416 // It should probably be refactored and combined with NonNullParamChecker. 417 418 // Get the argument's value. 419 const Expr *Arg = CE->getArg(0); 420 SVal ArgVal = state->getSVal(Arg, C.getLocationContext()); 421 Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); 422 if (!DefArgVal) 423 return; 424 425 // Get a NULL value. 426 SValBuilder &svalBuilder = C.getSValBuilder(); 427 DefinedSVal zero = 428 svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>(); 429 430 // Make an expression asserting that they're equal. 431 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); 432 433 // Are they equal? 434 ProgramStateRef stateTrue, stateFalse; 435 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); 436 437 if (stateTrue && !stateFalse) { 438 ExplodedNode *N = C.generateSink(stateTrue); 439 if (!N) 440 return; 441 442 const char *description; 443 if (FuncII == Retain) 444 description = "Null pointer argument in call to CFRetain"; 445 else if (FuncII == Release) 446 description = "Null pointer argument in call to CFRelease"; 447 else if (FuncII == MakeCollectable) 448 description = "Null pointer argument in call to CFMakeCollectable"; 449 else 450 llvm_unreachable("impossible case"); 451 452 BugReport *report = new BugReport(*BT, description, N); 453 report->addRange(Arg->getSourceRange()); 454 bugreporter::trackNullOrUndefValue(N, Arg, *report); 455 C.emitReport(report); 456 return; 457 } 458 459 // From here on, we know the argument is non-null. 460 C.addTransition(stateFalse); 461 } 462 463 //===----------------------------------------------------------------------===// 464 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class. 465 //===----------------------------------------------------------------------===// 466 467 namespace { 468 class ClassReleaseChecker : public Checker<check::PreObjCMessage> { 469 mutable Selector releaseS; 470 mutable Selector retainS; 471 mutable Selector autoreleaseS; 472 mutable Selector drainS; 473 mutable OwningPtr<BugType> BT; 474 475 public: 476 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 477 }; 478 } 479 480 void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 481 CheckerContext &C) const { 482 483 if (!BT) { 484 BT.reset(new APIMisuse("message incorrectly sent to class instead of class " 485 "instance")); 486 487 ASTContext &Ctx = C.getASTContext(); 488 releaseS = GetNullarySelector("release", Ctx); 489 retainS = GetNullarySelector("retain", Ctx); 490 autoreleaseS = GetNullarySelector("autorelease", Ctx); 491 drainS = GetNullarySelector("drain", Ctx); 492 } 493 494 if (msg.isInstanceMessage()) 495 return; 496 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 497 assert(Class); 498 499 Selector S = msg.getSelector(); 500 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) 501 return; 502 503 if (ExplodedNode *N = C.addTransition()) { 504 SmallString<200> buf; 505 llvm::raw_svector_ostream os(buf); 506 507 os << "The '" << S.getAsString() << "' message should be sent to instances " 508 "of class '" << Class->getName() 509 << "' and not the class directly"; 510 511 BugReport *report = new BugReport(*BT, os.str(), N); 512 report->addRange(msg.getSourceRange()); 513 C.emitReport(report); 514 } 515 } 516 517 //===----------------------------------------------------------------------===// 518 // Check for passing non-Objective-C types to variadic methods that expect 519 // only Objective-C types. 520 //===----------------------------------------------------------------------===// 521 522 namespace { 523 class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> { 524 mutable Selector arrayWithObjectsS; 525 mutable Selector dictionaryWithObjectsAndKeysS; 526 mutable Selector setWithObjectsS; 527 mutable Selector orderedSetWithObjectsS; 528 mutable Selector initWithObjectsS; 529 mutable Selector initWithObjectsAndKeysS; 530 mutable OwningPtr<BugType> BT; 531 532 bool isVariadicMessage(const ObjCMethodCall &msg) const; 533 534 public: 535 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 536 }; 537 } 538 539 /// isVariadicMessage - Returns whether the given message is a variadic message, 540 /// where all arguments must be Objective-C types. 541 bool 542 VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const { 543 const ObjCMethodDecl *MD = msg.getDecl(); 544 545 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext())) 546 return false; 547 548 Selector S = msg.getSelector(); 549 550 if (msg.isInstanceMessage()) { 551 // FIXME: Ideally we'd look at the receiver interface here, but that's not 552 // useful for init, because alloc returns 'id'. In theory, this could lead 553 // to false positives, for example if there existed a class that had an 554 // initWithObjects: implementation that does accept non-Objective-C pointer 555 // types, but the chance of that happening is pretty small compared to the 556 // gains that this analysis gives. 557 const ObjCInterfaceDecl *Class = MD->getClassInterface(); 558 559 switch (findKnownClass(Class)) { 560 case FC_NSArray: 561 case FC_NSOrderedSet: 562 case FC_NSSet: 563 return S == initWithObjectsS; 564 case FC_NSDictionary: 565 return S == initWithObjectsAndKeysS; 566 default: 567 return false; 568 } 569 } else { 570 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 571 572 switch (findKnownClass(Class)) { 573 case FC_NSArray: 574 return S == arrayWithObjectsS; 575 case FC_NSOrderedSet: 576 return S == orderedSetWithObjectsS; 577 case FC_NSSet: 578 return S == setWithObjectsS; 579 case FC_NSDictionary: 580 return S == dictionaryWithObjectsAndKeysS; 581 default: 582 return false; 583 } 584 } 585 } 586 587 void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 588 CheckerContext &C) const { 589 if (!BT) { 590 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all " 591 "Objective-C pointer types")); 592 593 ASTContext &Ctx = C.getASTContext(); 594 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); 595 dictionaryWithObjectsAndKeysS = 596 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); 597 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); 598 orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx); 599 600 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); 601 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); 602 } 603 604 if (!isVariadicMessage(msg)) 605 return; 606 607 // We are not interested in the selector arguments since they have 608 // well-defined types, so the compiler will issue a warning for them. 609 unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); 610 611 // We're not interested in the last argument since it has to be nil or the 612 // compiler would have issued a warning for it elsewhere. 613 unsigned variadicArgsEnd = msg.getNumArgs() - 1; 614 615 if (variadicArgsEnd <= variadicArgsBegin) 616 return; 617 618 // Verify that all arguments have Objective-C types. 619 Optional<ExplodedNode*> errorNode; 620 ProgramStateRef state = C.getState(); 621 622 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { 623 QualType ArgTy = msg.getArgExpr(I)->getType(); 624 if (ArgTy->isObjCObjectPointerType()) 625 continue; 626 627 // Block pointers are treaded as Objective-C pointers. 628 if (ArgTy->isBlockPointerType()) 629 continue; 630 631 // Ignore pointer constants. 632 if (msg.getArgSVal(I).getAs<loc::ConcreteInt>()) 633 continue; 634 635 // Ignore pointer types annotated with 'NSObject' attribute. 636 if (C.getASTContext().isObjCNSObjectType(ArgTy)) 637 continue; 638 639 // Ignore CF references, which can be toll-free bridged. 640 if (coreFoundation::isCFObjectRef(ArgTy)) 641 continue; 642 643 // Generate only one error node to use for all bug reports. 644 if (!errorNode.hasValue()) 645 errorNode = C.addTransition(); 646 647 if (!errorNode.getValue()) 648 continue; 649 650 SmallString<128> sbuf; 651 llvm::raw_svector_ostream os(sbuf); 652 653 StringRef TypeName = GetReceiverInterfaceName(msg); 654 if (!TypeName.empty()) 655 os << "Argument to '" << TypeName << "' method '"; 656 else 657 os << "Argument to method '"; 658 659 os << msg.getSelector().getAsString() 660 << "' should be an Objective-C pointer type, not '"; 661 ArgTy.print(os, C.getLangOpts()); 662 os << "'"; 663 664 BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue()); 665 R->addRange(msg.getArgSourceRange(I)); 666 C.emitReport(R); 667 } 668 } 669 670 //===----------------------------------------------------------------------===// 671 // Improves the modeling of loops over Cocoa collections. 672 //===----------------------------------------------------------------------===// 673 674 namespace { 675 class ObjCLoopChecker 676 : public Checker<check::PostStmt<ObjCForCollectionStmt> > { 677 678 public: 679 void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const; 680 }; 681 } 682 683 static bool isKnownNonNilCollectionType(QualType T) { 684 const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); 685 if (!PT) 686 return false; 687 688 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); 689 if (!ID) 690 return false; 691 692 switch (findKnownClass(ID)) { 693 case FC_NSArray: 694 case FC_NSDictionary: 695 case FC_NSEnumerator: 696 case FC_NSOrderedSet: 697 case FC_NSSet: 698 return true; 699 default: 700 return false; 701 } 702 } 703 704 void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, 705 CheckerContext &C) const { 706 ProgramStateRef State = C.getState(); 707 708 // Check if this is the branch for the end of the loop. 709 SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext()); 710 if (CollectionSentinel.isZeroConstant()) 711 return; 712 713 // See if the collection is one where we /know/ the elements are non-nil. 714 const Expr *Collection = FCS->getCollection(); 715 if (!isKnownNonNilCollectionType(Collection->getType())) 716 return; 717 718 // FIXME: Copied from ExprEngineObjC. 719 const Stmt *Element = FCS->getElement(); 720 SVal ElementVar; 721 if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { 722 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); 723 assert(ElemDecl->getInit() == 0); 724 ElementVar = State->getLValue(ElemDecl, C.getLocationContext()); 725 } else { 726 ElementVar = State->getSVal(Element, C.getLocationContext()); 727 } 728 729 if (!ElementVar.getAs<Loc>()) 730 return; 731 732 // Go ahead and assume the value is non-nil. 733 SVal Val = State->getSVal(ElementVar.castAs<Loc>()); 734 State = State->assume(Val.castAs<DefinedOrUnknownSVal>(), true); 735 C.addTransition(State); 736 } 737 738 namespace { 739 /// \class ObjCNonNilReturnValueChecker 740 /// \brief The checker restricts the return values of APIs known to 741 /// never (or almost never) return 'nil'. 742 class ObjCNonNilReturnValueChecker 743 : public Checker<check::PostObjCMessage> { 744 mutable bool Initialized; 745 mutable Selector ObjectAtIndex; 746 mutable Selector ObjectAtIndexedSubscript; 747 748 public: 749 ObjCNonNilReturnValueChecker() : Initialized(false) {} 750 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 751 }; 752 } 753 754 static ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr, 755 ProgramStateRef State, 756 CheckerContext &C) { 757 SVal Val = State->getSVal(NonNullExpr, C.getLocationContext()); 758 if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>()) 759 return State->assume(*DV, true); 760 return State; 761 } 762 763 void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M, 764 CheckerContext &C) 765 const { 766 ProgramStateRef State = C.getState(); 767 768 if (!Initialized) { 769 ASTContext &Ctx = C.getASTContext(); 770 ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx); 771 ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx); 772 } 773 774 // Check the receiver type. 775 if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) { 776 777 // Assume that object returned from '[self init]' or '[super init]' is not 778 // 'nil' if we are processing an inlined function/method. 779 // 780 // A defensive callee will (and should) check if the object returned by 781 // '[super init]' is 'nil' before doing it's own initialization. However, 782 // since 'nil' is rarely returned in practice, we should not warn when the 783 // caller to the defensive constructor uses the object in contexts where 784 // 'nil' is not accepted. 785 if (!C.inTopFrame() && M.getDecl() && 786 M.getDecl()->getMethodFamily() == OMF_init && 787 M.isReceiverSelfOrSuper()) { 788 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 789 } 790 791 // Objects returned from 792 // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript] 793 // are never 'nil'. 794 FoundationClass Cl = findKnownClass(Interface); 795 if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) { 796 Selector Sel = M.getSelector(); 797 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) { 798 // Go ahead and assume the value is non-nil. 799 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 800 } 801 } 802 } 803 C.addTransition(State); 804 } 805 806 //===----------------------------------------------------------------------===// 807 // Check registration. 808 //===----------------------------------------------------------------------===// 809 810 void ento::registerNilArgChecker(CheckerManager &mgr) { 811 mgr.registerChecker<NilArgChecker>(); 812 } 813 814 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) { 815 mgr.registerChecker<CFNumberCreateChecker>(); 816 } 817 818 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { 819 mgr.registerChecker<CFRetainReleaseChecker>(); 820 } 821 822 void ento::registerClassReleaseChecker(CheckerManager &mgr) { 823 mgr.registerChecker<ClassReleaseChecker>(); 824 } 825 826 void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { 827 mgr.registerChecker<VariadicMethodTypeChecker>(); 828 } 829 830 void ento::registerObjCLoopChecker(CheckerManager &mgr) { 831 mgr.registerChecker<ObjCLoopChecker>(); 832 } 833 834 void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) { 835 mgr.registerChecker<ObjCNonNilReturnValueChecker>(); 836 } 837