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