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