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