BasicObjCFoundationChecks.cpp revision 2ffcd18b845d4f855074ff7011c46e20616e08fd
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 789// The map from container symbol to the container count symbol. 790// We currently will remember the last countainer count symbol encountered. 791REGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap, SymbolRef, SymbolRef) 792 793namespace { 794class ObjCLoopChecker 795 : public Checker<check::PostStmt<ObjCForCollectionStmt>, 796 check::PostObjCMessage, 797 check::DeadSymbols, 798 check::PointerEscape > { 799 mutable IdentifierInfo *CountSelectorII; 800 801 bool isCollectionCountMethod(const ObjCMethodCall &M, 802 CheckerContext &C) const; 803 804public: 805 ObjCLoopChecker() : CountSelectorII(0) {} 806 void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const; 807 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 808 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 809 ProgramStateRef checkPointerEscape(ProgramStateRef State, 810 const InvalidatedSymbols &Escaped, 811 const CallEvent *Call, 812 PointerEscapeKind Kind) const; 813}; 814} 815 816static bool isKnownNonNilCollectionType(QualType T) { 817 const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); 818 if (!PT) 819 return false; 820 821 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); 822 if (!ID) 823 return false; 824 825 switch (findKnownClass(ID)) { 826 case FC_NSArray: 827 case FC_NSDictionary: 828 case FC_NSEnumerator: 829 case FC_NSOrderedSet: 830 case FC_NSSet: 831 return true; 832 default: 833 return false; 834 } 835} 836 837/// Assumes that the collection is non-nil. 838/// 839/// If the collection is known to be nil, returns NULL to indicate an infeasible 840/// path. 841static ProgramStateRef checkCollectionNonNil(CheckerContext &C, 842 ProgramStateRef State, 843 const ObjCForCollectionStmt *FCS) { 844 if (!State) 845 return NULL; 846 847 SVal CollectionVal = C.getSVal(FCS->getCollection()); 848 Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>(); 849 if (!KnownCollection) 850 return State; 851 852 ProgramStateRef StNonNil, StNil; 853 llvm::tie(StNonNil, StNil) = State->assume(*KnownCollection); 854 if (StNil && !StNonNil) { 855 // The collection is nil. This path is infeasible. 856 return NULL; 857 } 858 859 return StNonNil; 860} 861 862/// Assumes that the collection elements are non-nil. 863/// 864/// This only applies if the collection is one of those known not to contain 865/// nil values. 866static ProgramStateRef checkElementNonNil(CheckerContext &C, 867 ProgramStateRef State, 868 const ObjCForCollectionStmt *FCS) { 869 if (!State) 870 return NULL; 871 872 // See if the collection is one where we /know/ the elements are non-nil. 873 if (!isKnownNonNilCollectionType(FCS->getCollection()->getType())) 874 return State; 875 876 const LocationContext *LCtx = C.getLocationContext(); 877 const Stmt *Element = FCS->getElement(); 878 879 // FIXME: Copied from ExprEngineObjC. 880 Optional<Loc> ElementLoc; 881 if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { 882 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); 883 assert(ElemDecl->getInit() == 0); 884 ElementLoc = State->getLValue(ElemDecl, LCtx); 885 } else { 886 ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>(); 887 } 888 889 if (!ElementLoc) 890 return State; 891 892 // Go ahead and assume the value is non-nil. 893 SVal Val = State->getSVal(*ElementLoc); 894 return State->assume(Val.castAs<DefinedOrUnknownSVal>(), true); 895} 896 897/// Returns NULL state if the collection is known to contain elements 898/// (or is known not to contain elements if the Assumption parameter is false.) 899static ProgramStateRef assumeCollectionNonEmpty(CheckerContext &C, 900 ProgramStateRef State, 901 const ObjCForCollectionStmt *FCS, 902 bool Assumption = false) { 903 if (!State) 904 return NULL; 905 906 SymbolRef CollectionS = C.getSVal(FCS->getCollection()).getAsSymbol(); 907 if (!CollectionS) 908 return State; 909 const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS); 910 if (!CountS) 911 return State; 912 913 SValBuilder &SvalBuilder = C.getSValBuilder(); 914 SVal CountGreaterThanZeroVal = 915 SvalBuilder.evalBinOp(State, BO_GT, 916 nonloc::SymbolVal(*CountS), 917 SvalBuilder.makeIntVal(0, (*CountS)->getType()), 918 SvalBuilder.getConditionType()); 919 Optional<DefinedSVal> CountGreaterThanZero = 920 CountGreaterThanZeroVal.getAs<DefinedSVal>(); 921 if (!CountGreaterThanZero) { 922 // The SValBuilder cannot construct a valid SVal for this condition. 923 // This means we cannot properly reason about it. 924 return State; 925 } 926 927 return State->assume(*CountGreaterThanZero, Assumption); 928} 929 930/// If the fist block edge is a back edge, we are reentering the loop. 931static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N, 932 const ObjCForCollectionStmt *FCS) { 933 if (!N) 934 return false; 935 936 ProgramPoint P = N->getLocation(); 937 if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { 938 if (BE->getSrc()->getLoopTarget() == FCS) 939 return true; 940 return false; 941 } 942 943 // Keep looking for a block edge. 944 for (ExplodedNode::const_pred_iterator I = N->pred_begin(), 945 E = N->pred_end(); I != E; ++I) { 946 if (alreadyExecutedAtLeastOneLoopIteration(*I, FCS)) 947 return true; 948 } 949 950 return false; 951} 952 953void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, 954 CheckerContext &C) const { 955 ProgramStateRef State = C.getState(); 956 957 // Check if this is the branch for the end of the loop. 958 SVal CollectionSentinel = C.getSVal(FCS); 959 if (CollectionSentinel.isZeroConstant()) { 960 if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS)) 961 State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false); 962 963 // Otherwise, this is a branch that goes through the loop body. 964 } else { 965 State = checkCollectionNonNil(C, State, FCS); 966 State = checkElementNonNil(C, State, FCS); 967 State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true); 968 } 969 970 if (!State) 971 C.generateSink(); 972 else if (State != C.getState()) 973 C.addTransition(State); 974} 975 976bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M, 977 CheckerContext &C) const { 978 Selector S = M.getSelector(); 979 // Initialize the identifiers on first use. 980 if (!CountSelectorII) 981 CountSelectorII = &C.getASTContext().Idents.get("count"); 982 983 // If the method returns collection count, record the value. 984 if (S.isUnarySelector() && 985 (S.getIdentifierInfoForSlot(0) == CountSelectorII)) 986 return true; 987 988 return false; 989} 990 991void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M, 992 CheckerContext &C) const { 993 if (!M.isInstanceMessage()) 994 return; 995 996 const ObjCInterfaceDecl *ClassID = M.getReceiverInterface(); 997 if (!ClassID) 998 return; 999 1000 FoundationClass Class = findKnownClass(ClassID); 1001 if (Class != FC_NSDictionary && 1002 Class != FC_NSArray && 1003 Class != FC_NSSet) 1004 return; 1005 1006 SymbolRef ContainerS = M.getReceiverSVal().getAsSymbol(); 1007 if (!ContainerS) 1008 return; 1009 1010 // If we are processing a call to "count", get the symbolic value returned by 1011 // a call to "count" and add it to the map. 1012 if (!isCollectionCountMethod(M, C)) 1013 return; 1014 1015 const Expr *MsgExpr = M.getOriginExpr(); 1016 SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol(); 1017 if (CountS) { 1018 ProgramStateRef State = C.getState(); 1019 C.getSymbolManager().addSymbolDependency(ContainerS, CountS); 1020 State = State->set<ContainerCountMap>(ContainerS, CountS); 1021 C.addTransition(State); 1022 } 1023 return; 1024} 1025 1026ProgramStateRef 1027ObjCLoopChecker::checkPointerEscape(ProgramStateRef State, 1028 const InvalidatedSymbols &Escaped, 1029 const CallEvent *Call, 1030 PointerEscapeKind Kind) const { 1031 // TODO: If we know that the call cannot change the collection count, there 1032 // is nothing to do, just return. 1033 1034 // Remove the invalidated symbols form the collection count map. 1035 for (InvalidatedSymbols::const_iterator I = Escaped.begin(), 1036 E = Escaped.end(); 1037 I != E; ++I) { 1038 SymbolRef Sym = *I; 1039 1040 // The symbol escaped. Pessimistically, assume that the count could have 1041 // changed. 1042 State = State->remove<ContainerCountMap>(Sym); 1043 } 1044 return State; 1045} 1046 1047void ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper, 1048 CheckerContext &C) const { 1049 ProgramStateRef State = C.getState(); 1050 1051 // Remove the dead symbols from the collection count map. 1052 ContainerCountMapTy Tracked = State->get<ContainerCountMap>(); 1053 for (ContainerCountMapTy::iterator I = Tracked.begin(), 1054 E = Tracked.end(); I != E; ++I) { 1055 SymbolRef Sym = I->first; 1056 if (SymReaper.isDead(Sym)) 1057 State = State->remove<ContainerCountMap>(Sym); 1058 } 1059 1060 C.addTransition(State); 1061} 1062 1063namespace { 1064/// \class ObjCNonNilReturnValueChecker 1065/// \brief The checker restricts the return values of APIs known to 1066/// never (or almost never) return 'nil'. 1067class ObjCNonNilReturnValueChecker 1068 : public Checker<check::PostObjCMessage> { 1069 mutable bool Initialized; 1070 mutable Selector ObjectAtIndex; 1071 mutable Selector ObjectAtIndexedSubscript; 1072 mutable Selector NullSelector; 1073 1074public: 1075 ObjCNonNilReturnValueChecker() : Initialized(false) {} 1076 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 1077}; 1078} 1079 1080static ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr, 1081 ProgramStateRef State, 1082 CheckerContext &C) { 1083 SVal Val = State->getSVal(NonNullExpr, C.getLocationContext()); 1084 if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>()) 1085 return State->assume(*DV, true); 1086 return State; 1087} 1088 1089void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M, 1090 CheckerContext &C) 1091 const { 1092 ProgramStateRef State = C.getState(); 1093 1094 if (!Initialized) { 1095 ASTContext &Ctx = C.getASTContext(); 1096 ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx); 1097 ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx); 1098 NullSelector = GetNullarySelector("null", Ctx); 1099 } 1100 1101 // Check the receiver type. 1102 if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) { 1103 1104 // Assume that object returned from '[self init]' or '[super init]' is not 1105 // 'nil' if we are processing an inlined function/method. 1106 // 1107 // A defensive callee will (and should) check if the object returned by 1108 // '[super init]' is 'nil' before doing it's own initialization. However, 1109 // since 'nil' is rarely returned in practice, we should not warn when the 1110 // caller to the defensive constructor uses the object in contexts where 1111 // 'nil' is not accepted. 1112 if (!C.inTopFrame() && M.getDecl() && 1113 M.getDecl()->getMethodFamily() == OMF_init && 1114 M.isReceiverSelfOrSuper()) { 1115 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 1116 } 1117 1118 FoundationClass Cl = findKnownClass(Interface); 1119 1120 // Objects returned from 1121 // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript] 1122 // are never 'nil'. 1123 if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) { 1124 Selector Sel = M.getSelector(); 1125 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) { 1126 // Go ahead and assume the value is non-nil. 1127 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 1128 } 1129 } 1130 1131 // Objects returned from [NSNull null] are not nil. 1132 if (Cl == FC_NSNull) { 1133 if (M.getSelector() == NullSelector) { 1134 // Go ahead and assume the value is non-nil. 1135 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 1136 } 1137 } 1138 } 1139 C.addTransition(State); 1140} 1141 1142//===----------------------------------------------------------------------===// 1143// Check registration. 1144//===----------------------------------------------------------------------===// 1145 1146void ento::registerNilArgChecker(CheckerManager &mgr) { 1147 mgr.registerChecker<NilArgChecker>(); 1148} 1149 1150void ento::registerCFNumberCreateChecker(CheckerManager &mgr) { 1151 mgr.registerChecker<CFNumberCreateChecker>(); 1152} 1153 1154void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { 1155 mgr.registerChecker<CFRetainReleaseChecker>(); 1156} 1157 1158void ento::registerClassReleaseChecker(CheckerManager &mgr) { 1159 mgr.registerChecker<ClassReleaseChecker>(); 1160} 1161 1162void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { 1163 mgr.registerChecker<VariadicMethodTypeChecker>(); 1164} 1165 1166void ento::registerObjCLoopChecker(CheckerManager &mgr) { 1167 mgr.registerChecker<ObjCLoopChecker>(); 1168} 1169 1170void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) { 1171 mgr.registerChecker<ObjCNonNilReturnValueChecker>(); 1172} 1173