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