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