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 30 using namespace clang; 31 using namespace ento; 32 33 namespace { 34 class APIMisuse : public BugType { 35 public: 36 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} 37 }; 38 } // end anonymous namespace 39 40 //===----------------------------------------------------------------------===// 41 // Utility functions. 42 //===----------------------------------------------------------------------===// 43 44 static const char* GetReceiverNameType(const ObjCMessage &msg) { 45 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) 46 return ID->getIdentifier()->getNameStart(); 47 return 0; 48 } 49 50 static 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 61 static 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 69 namespace { 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 81 void 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 100 void 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 140 namespace { 141 class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > { 142 mutable llvm::OwningPtr<APIMisuse> BT; 143 mutable IdentifierInfo* II; 144 public: 145 CFNumberCreateChecker() : II(0) {} 146 147 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 148 149 private: 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 155 enum 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 174 namespace { 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 196 static 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 224 static 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 248 void 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 347 namespace { 348 class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > { 349 mutable llvm::OwningPtr<APIMisuse> BT; 350 mutable IdentifierInfo *Retain, *Release; 351 public: 352 CFRetainReleaseChecker(): Retain(0), Release(0) {} 353 void checkPreStmt(const CallExpr* CE, CheckerContext& C) const; 354 }; 355 } // end anonymous namespace 356 357 358 void 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 429 namespace { 430 class 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 437 public: 438 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; 439 }; 440 } 441 442 void 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 484 namespace { 485 class 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 495 public: 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. 502 bool 503 VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const { 504 const ObjCMethodDecl *MD = msg.getMethodDecl(); 505 if (!MD) 506 return false; 507 508 if (!MD->isVariadic()) 509 return false; 510 511 Selector S = msg.getSelector(); 512 513 if (msg.isInstanceMessage()) { 514 // FIXME: Ideally we'd look at the receiver interface here, but that's not 515 // useful for init, because alloc returns 'id'. In theory, this could lead 516 // to false positives, for example if there existed a class that had an 517 // initWithObjects: implementation that does accept non-Objective-C pointer 518 // types, but the chance of that happening is pretty small compared to the 519 // gains that this analysis gives. 520 const ObjCInterfaceDecl *Class = MD->getClassInterface(); 521 522 // -[NSArray initWithObjects:] 523 if (isReceiverClassOrSuperclass(Class, "NSArray") && 524 S == initWithObjectsS) 525 return true; 526 527 // -[NSDictionary initWithObjectsAndKeys:] 528 if (isReceiverClassOrSuperclass(Class, "NSDictionary") && 529 S == initWithObjectsAndKeysS) 530 return true; 531 532 // -[NSSet initWithObjects:] 533 if (isReceiverClassOrSuperclass(Class, "NSSet") && 534 S == initWithObjectsS) 535 return true; 536 } else { 537 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 538 539 // -[NSArray arrayWithObjects:] 540 if (isReceiverClassOrSuperclass(Class, "NSArray") && 541 S == arrayWithObjectsS) 542 return true; 543 544 // -[NSDictionary dictionaryWithObjectsAndKeys:] 545 if (isReceiverClassOrSuperclass(Class, "NSDictionary") && 546 S == dictionaryWithObjectsAndKeysS) 547 return true; 548 549 // -[NSSet setWithObjects:] 550 if (isReceiverClassOrSuperclass(Class, "NSSet") && 551 S == setWithObjectsS) 552 return true; 553 } 554 555 return false; 556 } 557 558 void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, 559 CheckerContext &C) const { 560 if (!BT) { 561 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all " 562 "Objective-C pointer types")); 563 564 ASTContext &Ctx = C.getASTContext(); 565 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); 566 dictionaryWithObjectsAndKeysS = 567 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); 568 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); 569 570 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); 571 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); 572 } 573 574 if (!isVariadicMessage(msg)) 575 return; 576 577 // We are not interested in the selector arguments since they have 578 // well-defined types, so the compiler will issue a warning for them. 579 unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); 580 581 // We're not interested in the last argument since it has to be nil or the 582 // compiler would have issued a warning for it elsewhere. 583 unsigned variadicArgsEnd = msg.getNumArgs() - 1; 584 585 if (variadicArgsEnd <= variadicArgsBegin) 586 return; 587 588 // Verify that all arguments have Objective-C types. 589 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { 590 QualType ArgTy = msg.getArgType(I); 591 if (ArgTy->isObjCObjectPointerType()) 592 continue; 593 594 ExplodedNode *N = C.generateNode(); 595 if (!N) 596 continue; 597 598 llvm::SmallString<128> sbuf; 599 llvm::raw_svector_ostream os(sbuf); 600 601 if (const char *TypeName = GetReceiverNameType(msg)) 602 os << "Argument to '" << TypeName << "' method '"; 603 else 604 os << "Argument to method '"; 605 606 os << msg.getSelector().getAsString() 607 << "' should be an Objective-C pointer type, not '" 608 << ArgTy.getAsString() << "'"; 609 610 RangedBugReport *R = new RangedBugReport(*BT, os.str(), N); 611 R->addRange(msg.getArgSourceRange(I)); 612 C.EmitReport(R); 613 } 614 } 615 616 //===----------------------------------------------------------------------===// 617 // Check registration. 618 //===----------------------------------------------------------------------===// 619 620 void ento::registerNilArgChecker(CheckerManager &mgr) { 621 mgr.registerChecker<NilArgChecker>(); 622 } 623 624 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) { 625 mgr.registerChecker<CFNumberCreateChecker>(); 626 } 627 628 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { 629 mgr.registerChecker<CFRetainReleaseChecker>(); 630 } 631 632 void ento::registerClassReleaseChecker(CheckerManager &mgr) { 633 mgr.registerChecker<ClassReleaseChecker>(); 634 } 635 636 void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { 637 mgr.registerChecker<VariadicMethodTypeChecker>(); 638 } 639