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