BasicObjCFoundationChecks.cpp revision 928c415d5dde89b7c01e41f0dfa8a782cbfa8e7d
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/Analysis/DomainSpecific/CocoaConventions.h" 18#include "clang/StaticAnalyzer/Core/Checker.h" 19#include "clang/StaticAnalyzer/Core/CheckerManager.h" 20#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 21#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 22#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 23#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" 24#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 25#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" 26#include "clang/AST/DeclObjC.h" 27#include "clang/AST/Expr.h" 28#include "clang/AST/ExprObjC.h" 29#include "clang/AST/ASTContext.h" 30 31using namespace clang; 32using namespace ento; 33 34namespace { 35class APIMisuse : public BugType { 36public: 37 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} 38}; 39} // end anonymous namespace 40 41//===----------------------------------------------------------------------===// 42// Utility functions. 43//===----------------------------------------------------------------------===// 44 45static const char* GetReceiverNameType(const ObjCMessage &msg) { 46 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) 47 return ID->getIdentifier()->getNameStart(); 48 return 0; 49} 50 51static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID, 52 llvm::StringRef ClassName) { 53 if (ID->getIdentifier()->getName() == ClassName) 54 return true; 55 56 if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) 57 return isReceiverClassOrSuperclass(Super, ClassName); 58 59 return false; 60} 61 62static inline bool isNil(SVal X) { 63 return isa<loc::ConcreteInt>(X); 64} 65 66//===----------------------------------------------------------------------===// 67// NilArgChecker - Check for prohibited nil arguments to ObjC method calls. 68//===----------------------------------------------------------------------===// 69 70namespace { 71 class NilArgChecker : public Checker<check::PreObjCMessage> { 72 mutable llvm::OwningPtr<APIMisuse> BT; 73 74 void WarnNilArg(CheckerContext &C, 75 const ObjCMessage &msg, unsigned Arg) const; 76 77 public: 78 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; 79 }; 80} 81 82void NilArgChecker::WarnNilArg(CheckerContext &C, 83 const ObjCMessage &msg, 84 unsigned int Arg) const 85{ 86 if (!BT) 87 BT.reset(new APIMisuse("nil argument")); 88 89 if (ExplodedNode *N = C.generateSink()) { 90 llvm::SmallString<128> sbuf; 91 llvm::raw_svector_ostream os(sbuf); 92 os << "Argument to '" << GetReceiverNameType(msg) << "' method '" 93 << msg.getSelector().getAsString() << "' cannot be nil"; 94 95 RangedBugReport *R = new RangedBugReport(*BT, os.str(), N); 96 R->addRange(msg.getArgSourceRange(Arg)); 97 C.EmitReport(R); 98 } 99} 100 101void NilArgChecker::checkPreObjCMessage(ObjCMessage msg, 102 CheckerContext &C) const { 103 const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); 104 if (!ID) 105 return; 106 107 if (isReceiverClassOrSuperclass(ID, "NSString")) { 108 Selector S = msg.getSelector(); 109 110 if (S.isUnarySelector()) 111 return; 112 113 // FIXME: This is going to be really slow doing these checks with 114 // lexical comparisons. 115 116 std::string NameStr = S.getAsString(); 117 llvm::StringRef Name(NameStr); 118 assert(!Name.empty()); 119 120 // FIXME: Checking for initWithFormat: will not work in most cases 121 // yet because [NSString alloc] returns id, not NSString*. We will 122 // need support for tracking expected-type information in the analyzer 123 // to find these errors. 124 if (Name == "caseInsensitiveCompare:" || 125 Name == "compare:" || 126 Name == "compare:options:" || 127 Name == "compare:options:range:" || 128 Name == "compare:options:range:locale:" || 129 Name == "componentsSeparatedByCharactersInSet:" || 130 Name == "initWithFormat:") { 131 if (isNil(msg.getArgSVal(0, C.getState()))) 132 WarnNilArg(C, msg, 0); 133 } 134 } 135} 136 137//===----------------------------------------------------------------------===// 138// Error reporting. 139//===----------------------------------------------------------------------===// 140 141namespace { 142class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > { 143 mutable llvm::OwningPtr<APIMisuse> BT; 144 mutable IdentifierInfo* II; 145public: 146 CFNumberCreateChecker() : II(0) {} 147 148 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 149 150private: 151 void EmitError(const TypedRegion* R, const Expr* Ex, 152 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); 153}; 154} // end anonymous namespace 155 156enum CFNumberType { 157 kCFNumberSInt8Type = 1, 158 kCFNumberSInt16Type = 2, 159 kCFNumberSInt32Type = 3, 160 kCFNumberSInt64Type = 4, 161 kCFNumberFloat32Type = 5, 162 kCFNumberFloat64Type = 6, 163 kCFNumberCharType = 7, 164 kCFNumberShortType = 8, 165 kCFNumberIntType = 9, 166 kCFNumberLongType = 10, 167 kCFNumberLongLongType = 11, 168 kCFNumberFloatType = 12, 169 kCFNumberDoubleType = 13, 170 kCFNumberCFIndexType = 14, 171 kCFNumberNSIntegerType = 15, 172 kCFNumberCGFloatType = 16 173}; 174 175namespace { 176 template<typename T> 177 class Optional { 178 bool IsKnown; 179 T Val; 180 public: 181 Optional() : IsKnown(false), Val(0) {} 182 Optional(const T& val) : IsKnown(true), Val(val) {} 183 184 bool isKnown() const { return IsKnown; } 185 186 const T& getValue() const { 187 assert (isKnown()); 188 return Val; 189 } 190 191 operator const T&() const { 192 return getValue(); 193 } 194 }; 195} 196 197static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) { 198 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; 199 200 if (i < kCFNumberCharType) 201 return FixedSize[i-1]; 202 203 QualType T; 204 205 switch (i) { 206 case kCFNumberCharType: T = Ctx.CharTy; break; 207 case kCFNumberShortType: T = Ctx.ShortTy; break; 208 case kCFNumberIntType: T = Ctx.IntTy; break; 209 case kCFNumberLongType: T = Ctx.LongTy; break; 210 case kCFNumberLongLongType: T = Ctx.LongLongTy; break; 211 case kCFNumberFloatType: T = Ctx.FloatTy; break; 212 case kCFNumberDoubleType: T = Ctx.DoubleTy; break; 213 case kCFNumberCFIndexType: 214 case kCFNumberNSIntegerType: 215 case kCFNumberCGFloatType: 216 // FIXME: We need a way to map from names to Type*. 217 default: 218 return Optional<uint64_t>(); 219 } 220 221 return Ctx.getTypeSize(T); 222} 223 224#if 0 225static const char* GetCFNumberTypeStr(uint64_t i) { 226 static const char* Names[] = { 227 "kCFNumberSInt8Type", 228 "kCFNumberSInt16Type", 229 "kCFNumberSInt32Type", 230 "kCFNumberSInt64Type", 231 "kCFNumberFloat32Type", 232 "kCFNumberFloat64Type", 233 "kCFNumberCharType", 234 "kCFNumberShortType", 235 "kCFNumberIntType", 236 "kCFNumberLongType", 237 "kCFNumberLongLongType", 238 "kCFNumberFloatType", 239 "kCFNumberDoubleType", 240 "kCFNumberCFIndexType", 241 "kCFNumberNSIntegerType", 242 "kCFNumberCGFloatType" 243 }; 244 245 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType"; 246} 247#endif 248 249void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, 250 CheckerContext &C) const { 251 const Expr* Callee = CE->getCallee(); 252 const GRState *state = C.getState(); 253 SVal CallV = state->getSVal(Callee); 254 const FunctionDecl* FD = CallV.getAsFunctionDecl(); 255 256 if (!FD) 257 return; 258 259 ASTContext &Ctx = C.getASTContext(); 260 if (!II) 261 II = &Ctx.Idents.get("CFNumberCreate"); 262 263 if (FD->getIdentifier() != II || CE->getNumArgs() != 3) 264 return; 265 266 // Get the value of the "theType" argument. 267 SVal TheTypeVal = state->getSVal(CE->getArg(1)); 268 269 // FIXME: We really should allow ranges of valid theType values, and 270 // bifurcate the state appropriately. 271 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal); 272 if (!V) 273 return; 274 275 uint64_t NumberKind = V->getValue().getLimitedValue(); 276 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind); 277 278 // FIXME: In some cases we can emit an error. 279 if (!TargetSize.isKnown()) 280 return; 281 282 // Look at the value of the integer being passed by reference. Essentially 283 // we want to catch cases where the value passed in is not equal to the 284 // size of the type being created. 285 SVal TheValueExpr = state->getSVal(CE->getArg(2)); 286 287 // FIXME: Eventually we should handle arbitrary locations. We can do this 288 // by having an enhanced memory model that does low-level typing. 289 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr); 290 if (!LV) 291 return; 292 293 const TypedRegion* R = dyn_cast<TypedRegion>(LV->stripCasts()); 294 if (!R) 295 return; 296 297 QualType T = Ctx.getCanonicalType(R->getValueType()); 298 299 // FIXME: If the pointee isn't an integer type, should we flag a warning? 300 // People can do weird stuff with pointers. 301 302 if (!T->isIntegerType()) 303 return; 304 305 uint64_t SourceSize = Ctx.getTypeSize(T); 306 307 // CHECK: is SourceSize == TargetSize 308 if (SourceSize == TargetSize) 309 return; 310 311 // Generate an error. Only generate a sink if 'SourceSize < TargetSize'; 312 // otherwise generate a regular node. 313 // 314 // FIXME: We can actually create an abstract "CFNumber" object that has 315 // the bits initialized to the provided values. 316 // 317 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink() 318 : C.generateNode()) { 319 llvm::SmallString<128> sbuf; 320 llvm::raw_svector_ostream os(sbuf); 321 322 os << (SourceSize == 8 ? "An " : "A ") 323 << SourceSize << " bit integer is used to initialize a CFNumber " 324 "object that represents " 325 << (TargetSize == 8 ? "an " : "a ") 326 << TargetSize << " bit integer. "; 327 328 if (SourceSize < TargetSize) 329 os << (TargetSize - SourceSize) 330 << " bits of the CFNumber value will be garbage." ; 331 else 332 os << (SourceSize - TargetSize) 333 << " bits of the input integer will be lost."; 334 335 if (!BT) 336 BT.reset(new APIMisuse("Bad use of CFNumberCreate")); 337 338 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); 339 report->addRange(CE->getArg(2)->getSourceRange()); 340 C.EmitReport(report); 341 } 342} 343 344//===----------------------------------------------------------------------===// 345// CFRetain/CFRelease checking for null arguments. 346//===----------------------------------------------------------------------===// 347 348namespace { 349class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > { 350 mutable llvm::OwningPtr<APIMisuse> BT; 351 mutable IdentifierInfo *Retain, *Release; 352public: 353 CFRetainReleaseChecker(): Retain(0), Release(0) {} 354 void checkPreStmt(const CallExpr* CE, CheckerContext& C) const; 355}; 356} // end anonymous namespace 357 358 359void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE, 360 CheckerContext& C) const { 361 // If the CallExpr doesn't have exactly 1 argument just give up checking. 362 if (CE->getNumArgs() != 1) 363 return; 364 365 // Get the function declaration of the callee. 366 const GRState* state = C.getState(); 367 SVal X = state->getSVal(CE->getCallee()); 368 const FunctionDecl* FD = X.getAsFunctionDecl(); 369 370 if (!FD) 371 return; 372 373 if (!BT) { 374 ASTContext &Ctx = C.getASTContext(); 375 Retain = &Ctx.Idents.get("CFRetain"); 376 Release = &Ctx.Idents.get("CFRelease"); 377 BT.reset(new APIMisuse("null passed to CFRetain/CFRelease")); 378 } 379 380 // Check if we called CFRetain/CFRelease. 381 const IdentifierInfo *FuncII = FD->getIdentifier(); 382 if (!(FuncII == Retain || FuncII == Release)) 383 return; 384 385 // FIXME: The rest of this just checks that the argument is non-null. 386 // It should probably be refactored and combined with AttrNonNullChecker. 387 388 // Get the argument's value. 389 const Expr *Arg = CE->getArg(0); 390 SVal ArgVal = state->getSVal(Arg); 391 DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal); 392 if (!DefArgVal) 393 return; 394 395 // Get a NULL value. 396 SValBuilder &svalBuilder = C.getSValBuilder(); 397 DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType())); 398 399 // Make an expression asserting that they're equal. 400 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); 401 402 // Are they equal? 403 const GRState *stateTrue, *stateFalse; 404 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); 405 406 if (stateTrue && !stateFalse) { 407 ExplodedNode *N = C.generateSink(stateTrue); 408 if (!N) 409 return; 410 411 const char *description = (FuncII == Retain) 412 ? "Null pointer argument in call to CFRetain" 413 : "Null pointer argument in call to CFRelease"; 414 415 EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N); 416 report->addRange(Arg->getSourceRange()); 417 report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg); 418 C.EmitReport(report); 419 return; 420 } 421 422 // From here on, we know the argument is non-null. 423 C.addTransition(stateFalse); 424} 425 426//===----------------------------------------------------------------------===// 427// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. 428//===----------------------------------------------------------------------===// 429 430namespace { 431class ClassReleaseChecker : public Checker<check::PreObjCMessage> { 432 mutable Selector releaseS; 433 mutable Selector retainS; 434 mutable Selector autoreleaseS; 435 mutable Selector drainS; 436 mutable llvm::OwningPtr<BugType> BT; 437 438public: 439 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; 440}; 441} 442 443void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg, 444 CheckerContext &C) const { 445 446 if (!BT) { 447 BT.reset(new APIMisuse("message incorrectly sent to class instead of class " 448 "instance")); 449 450 ASTContext &Ctx = C.getASTContext(); 451 releaseS = GetNullarySelector("release", Ctx); 452 retainS = GetNullarySelector("retain", Ctx); 453 autoreleaseS = GetNullarySelector("autorelease", Ctx); 454 drainS = GetNullarySelector("drain", Ctx); 455 } 456 457 if (msg.isInstanceMessage()) 458 return; 459 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 460 assert(Class); 461 462 Selector S = msg.getSelector(); 463 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) 464 return; 465 466 if (ExplodedNode *N = C.generateNode()) { 467 llvm::SmallString<200> buf; 468 llvm::raw_svector_ostream os(buf); 469 470 os << "The '" << S.getAsString() << "' message should be sent to instances " 471 "of class '" << Class->getName() 472 << "' and not the class directly"; 473 474 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); 475 report->addRange(msg.getSourceRange()); 476 C.EmitReport(report); 477 } 478} 479 480//===----------------------------------------------------------------------===// 481// Check for passing non-Objective-C types to variadic methods that expect 482// only Objective-C types. 483//===----------------------------------------------------------------------===// 484 485namespace { 486class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> { 487 mutable Selector arrayWithObjectsS; 488 mutable Selector dictionaryWithObjectsAndKeysS; 489 mutable Selector setWithObjectsS; 490 mutable Selector initWithObjectsS; 491 mutable Selector initWithObjectsAndKeysS; 492 mutable llvm::OwningPtr<BugType> BT; 493 494 bool isVariadicMessage(const ObjCMessage &msg) const; 495 496public: 497 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; 498}; 499} 500 501/// isVariadicMessage - Returns whether the given message is a variadic message, 502/// where all arguments must be Objective-C types. 503bool 504VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const { 505 const ObjCMethodDecl *MD = msg.getMethodDecl(); 506 if (!MD || !MD->isVariadic()) 507 return false; 508 509 Selector S = msg.getSelector(); 510 511 if (msg.isInstanceMessage()) { 512 // FIXME: Ideally we'd look at the receiver interface here, but that's not 513 // useful for init, because alloc returns 'id'. In theory, this could lead 514 // to false positives, for example if there existed a class that had an 515 // initWithObjects: implementation that does accept non-Objective-C pointer 516 // types, but the chance of that happening is pretty small compared to the 517 // gains that this analysis gives. 518 const ObjCInterfaceDecl *Class = MD->getClassInterface(); 519 520 // -[NSArray initWithObjects:] 521 if (isReceiverClassOrSuperclass(Class, "NSArray") && 522 S == initWithObjectsS) 523 return true; 524 525 // -[NSDictionary initWithObjectsAndKeys:] 526 if (isReceiverClassOrSuperclass(Class, "NSDictionary") && 527 S == initWithObjectsAndKeysS) 528 return true; 529 530 // -[NSSet initWithObjects:] 531 if (isReceiverClassOrSuperclass(Class, "NSSet") && 532 S == initWithObjectsS) 533 return true; 534 } else { 535 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 536 537 // -[NSArray arrayWithObjects:] 538 if (isReceiverClassOrSuperclass(Class, "NSArray") && 539 S == arrayWithObjectsS) 540 return true; 541 542 // -[NSDictionary dictionaryWithObjectsAndKeys:] 543 if (isReceiverClassOrSuperclass(Class, "NSDictionary") && 544 S == dictionaryWithObjectsAndKeysS) 545 return true; 546 547 // -[NSSet setWithObjects:] 548 if (isReceiverClassOrSuperclass(Class, "NSSet") && 549 S == setWithObjectsS) 550 return true; 551 } 552 553 return false; 554} 555 556void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, 557 CheckerContext &C) const { 558 if (!BT) { 559 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all " 560 "Objective-C pointer types")); 561 562 ASTContext &Ctx = C.getASTContext(); 563 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); 564 dictionaryWithObjectsAndKeysS = 565 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); 566 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); 567 568 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); 569 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); 570 } 571 572 if (!isVariadicMessage(msg)) 573 return; 574 575 // We are not interested in the selector arguments since they have 576 // well-defined types, so the compiler will issue a warning for them. 577 unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); 578 579 // We're not interested in the last argument since it has to be nil or the 580 // compiler would have issued a warning for it elsewhere. 581 unsigned variadicArgsEnd = msg.getNumArgs() - 1; 582 583 if (variadicArgsEnd <= variadicArgsBegin) 584 return; 585 586 // Verify that all arguments have Objective-C types. 587 llvm::Optional<ExplodedNode*> errorNode; 588 const GRState *state = C.getState(); 589 590 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { 591 QualType ArgTy = msg.getArgType(I); 592 if (ArgTy->isObjCObjectPointerType()) 593 continue; 594 595 // Ignore pointer constants. 596 if (isa<loc::ConcreteInt>(msg.getArgSVal(I, state))) 597 continue; 598 599 // Ignore CF references, which can be toll-free bridged. 600 if (cocoa::isCFObjectRef(ArgTy)) 601 continue; 602 603 // Generate only one error node to use for all bug reports. 604 if (!errorNode.hasValue()) { 605 errorNode = C.generateNode(); 606 } 607 608 if (!errorNode.getValue()) 609 continue; 610 611 llvm::SmallString<128> sbuf; 612 llvm::raw_svector_ostream os(sbuf); 613 614 if (const char *TypeName = GetReceiverNameType(msg)) 615 os << "Argument to '" << TypeName << "' method '"; 616 else 617 os << "Argument to method '"; 618 619 os << msg.getSelector().getAsString() 620 << "' should be an Objective-C pointer type, not '" 621 << ArgTy.getAsString() << "'"; 622 623 RangedBugReport *R = new RangedBugReport(*BT, os.str(), 624 errorNode.getValue()); 625 R->addRange(msg.getArgSourceRange(I)); 626 C.EmitReport(R); 627 } 628} 629 630//===----------------------------------------------------------------------===// 631// Check registration. 632//===----------------------------------------------------------------------===// 633 634void ento::registerNilArgChecker(CheckerManager &mgr) { 635 mgr.registerChecker<NilArgChecker>(); 636} 637 638void ento::registerCFNumberCreateChecker(CheckerManager &mgr) { 639 mgr.registerChecker<CFNumberCreateChecker>(); 640} 641 642void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { 643 mgr.registerChecker<CFRetainReleaseChecker>(); 644} 645 646void ento::registerClassReleaseChecker(CheckerManager &mgr) { 647 mgr.registerChecker<ClassReleaseChecker>(); 648} 649 650void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { 651 mgr.registerChecker<VariadicMethodTypeChecker>(); 652} 653