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 "BasicObjCFoundationChecks.h" 17 18 #include "ClangSACheckers.h" 19 #include "clang/StaticAnalyzer/Core/CheckerV2.h" 20 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 22 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" 23 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 24 #include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" 25 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 26 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" 27 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" 28 #include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" 29 #include "clang/AST/DeclObjC.h" 30 #include "clang/AST/Expr.h" 31 #include "clang/AST/ExprObjC.h" 32 #include "clang/AST/ASTContext.h" 33 34 using namespace clang; 35 using namespace ento; 36 37 namespace { 38 class APIMisuse : public BugType { 39 public: 40 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} 41 }; 42 } // end anonymous namespace 43 44 //===----------------------------------------------------------------------===// 45 // Utility functions. 46 //===----------------------------------------------------------------------===// 47 48 static const ObjCInterfaceType* GetReceiverType(const ObjCMessage &msg) { 49 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) 50 return ID->getTypeForDecl()->getAs<ObjCInterfaceType>(); 51 return NULL; 52 } 53 54 static const char* GetReceiverNameType(const ObjCMessage &msg) { 55 if (const ObjCInterfaceType *ReceiverType = GetReceiverType(msg)) 56 return ReceiverType->getDecl()->getIdentifier()->getNameStart(); 57 return NULL; 58 } 59 60 static bool isNSString(llvm::StringRef ClassName) { 61 return ClassName == "NSString" || ClassName == "NSMutableString"; 62 } 63 64 static inline bool isNil(SVal X) { 65 return isa<loc::ConcreteInt>(X); 66 } 67 68 //===----------------------------------------------------------------------===// 69 // NilArgChecker - Check for prohibited nil arguments to ObjC method calls. 70 //===----------------------------------------------------------------------===// 71 72 namespace { 73 class NilArgChecker : public CheckerV2<check::PreObjCMessage> { 74 mutable llvm::OwningPtr<APIMisuse> BT; 75 76 void WarnNilArg(CheckerContext &C, 77 const ObjCMessage &msg, unsigned Arg) const; 78 79 public: 80 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; 81 }; 82 } 83 84 void NilArgChecker::WarnNilArg(CheckerContext &C, 85 const ObjCMessage &msg, 86 unsigned int Arg) const 87 { 88 if (!BT) 89 BT.reset(new APIMisuse("nil argument")); 90 91 if (ExplodedNode *N = C.generateSink()) { 92 llvm::SmallString<128> sbuf; 93 llvm::raw_svector_ostream os(sbuf); 94 os << "Argument to '" << GetReceiverNameType(msg) << "' method '" 95 << msg.getSelector().getAsString() << "' cannot be nil"; 96 97 RangedBugReport *R = new RangedBugReport(*BT, os.str(), N); 98 R->addRange(msg.getArgSourceRange(Arg)); 99 C.EmitReport(R); 100 } 101 } 102 103 void NilArgChecker::checkPreObjCMessage(ObjCMessage msg, 104 CheckerContext &C) const { 105 const ObjCInterfaceType *ReceiverType = GetReceiverType(msg); 106 if (!ReceiverType) 107 return; 108 109 if (isNSString(ReceiverType->getDecl()->getIdentifier()->getName())) { 110 Selector S = msg.getSelector(); 111 112 if (S.isUnarySelector()) 113 return; 114 115 // FIXME: This is going to be really slow doing these checks with 116 // lexical comparisons. 117 118 std::string NameStr = S.getAsString(); 119 llvm::StringRef Name(NameStr); 120 assert(!Name.empty()); 121 122 // FIXME: Checking for initWithFormat: will not work in most cases 123 // yet because [NSString alloc] returns id, not NSString*. We will 124 // need support for tracking expected-type information in the analyzer 125 // to find these errors. 126 if (Name == "caseInsensitiveCompare:" || 127 Name == "compare:" || 128 Name == "compare:options:" || 129 Name == "compare:options:range:" || 130 Name == "compare:options:range:locale:" || 131 Name == "componentsSeparatedByCharactersInSet:" || 132 Name == "initWithFormat:") { 133 if (isNil(msg.getArgSVal(0, C.getState()))) 134 WarnNilArg(C, msg, 0); 135 } 136 } 137 } 138 139 //===----------------------------------------------------------------------===// 140 // Error reporting. 141 //===----------------------------------------------------------------------===// 142 143 namespace { 144 class CFNumberCreateChecker : public CheckerV2< check::PreStmt<CallExpr> > { 145 mutable llvm::OwningPtr<APIMisuse> BT; 146 mutable IdentifierInfo* II; 147 public: 148 CFNumberCreateChecker() : II(0) {} 149 150 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 151 152 private: 153 void EmitError(const TypedRegion* R, const Expr* Ex, 154 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); 155 }; 156 } // end anonymous namespace 157 158 enum CFNumberType { 159 kCFNumberSInt8Type = 1, 160 kCFNumberSInt16Type = 2, 161 kCFNumberSInt32Type = 3, 162 kCFNumberSInt64Type = 4, 163 kCFNumberFloat32Type = 5, 164 kCFNumberFloat64Type = 6, 165 kCFNumberCharType = 7, 166 kCFNumberShortType = 8, 167 kCFNumberIntType = 9, 168 kCFNumberLongType = 10, 169 kCFNumberLongLongType = 11, 170 kCFNumberFloatType = 12, 171 kCFNumberDoubleType = 13, 172 kCFNumberCFIndexType = 14, 173 kCFNumberNSIntegerType = 15, 174 kCFNumberCGFloatType = 16 175 }; 176 177 namespace { 178 template<typename T> 179 class Optional { 180 bool IsKnown; 181 T Val; 182 public: 183 Optional() : IsKnown(false), Val(0) {} 184 Optional(const T& val) : IsKnown(true), Val(val) {} 185 186 bool isKnown() const { return IsKnown; } 187 188 const T& getValue() const { 189 assert (isKnown()); 190 return Val; 191 } 192 193 operator const T&() const { 194 return getValue(); 195 } 196 }; 197 } 198 199 static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) { 200 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; 201 202 if (i < kCFNumberCharType) 203 return FixedSize[i-1]; 204 205 QualType T; 206 207 switch (i) { 208 case kCFNumberCharType: T = Ctx.CharTy; break; 209 case kCFNumberShortType: T = Ctx.ShortTy; break; 210 case kCFNumberIntType: T = Ctx.IntTy; break; 211 case kCFNumberLongType: T = Ctx.LongTy; break; 212 case kCFNumberLongLongType: T = Ctx.LongLongTy; break; 213 case kCFNumberFloatType: T = Ctx.FloatTy; break; 214 case kCFNumberDoubleType: T = Ctx.DoubleTy; break; 215 case kCFNumberCFIndexType: 216 case kCFNumberNSIntegerType: 217 case kCFNumberCGFloatType: 218 // FIXME: We need a way to map from names to Type*. 219 default: 220 return Optional<uint64_t>(); 221 } 222 223 return Ctx.getTypeSize(T); 224 } 225 226 #if 0 227 static const char* GetCFNumberTypeStr(uint64_t i) { 228 static const char* Names[] = { 229 "kCFNumberSInt8Type", 230 "kCFNumberSInt16Type", 231 "kCFNumberSInt32Type", 232 "kCFNumberSInt64Type", 233 "kCFNumberFloat32Type", 234 "kCFNumberFloat64Type", 235 "kCFNumberCharType", 236 "kCFNumberShortType", 237 "kCFNumberIntType", 238 "kCFNumberLongType", 239 "kCFNumberLongLongType", 240 "kCFNumberFloatType", 241 "kCFNumberDoubleType", 242 "kCFNumberCFIndexType", 243 "kCFNumberNSIntegerType", 244 "kCFNumberCGFloatType" 245 }; 246 247 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType"; 248 } 249 #endif 250 251 void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, 252 CheckerContext &C) const { 253 const Expr* Callee = CE->getCallee(); 254 const GRState *state = C.getState(); 255 SVal CallV = state->getSVal(Callee); 256 const FunctionDecl* FD = CallV.getAsFunctionDecl(); 257 258 if (!FD) 259 return; 260 261 ASTContext &Ctx = C.getASTContext(); 262 if (!II) 263 II = &Ctx.Idents.get("CFNumberCreate"); 264 265 if (FD->getIdentifier() != II || CE->getNumArgs() != 3) 266 return; 267 268 // Get the value of the "theType" argument. 269 SVal TheTypeVal = state->getSVal(CE->getArg(1)); 270 271 // FIXME: We really should allow ranges of valid theType values, and 272 // bifurcate the state appropriately. 273 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal); 274 if (!V) 275 return; 276 277 uint64_t NumberKind = V->getValue().getLimitedValue(); 278 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind); 279 280 // FIXME: In some cases we can emit an error. 281 if (!TargetSize.isKnown()) 282 return; 283 284 // Look at the value of the integer being passed by reference. Essentially 285 // we want to catch cases where the value passed in is not equal to the 286 // size of the type being created. 287 SVal TheValueExpr = state->getSVal(CE->getArg(2)); 288 289 // FIXME: Eventually we should handle arbitrary locations. We can do this 290 // by having an enhanced memory model that does low-level typing. 291 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr); 292 if (!LV) 293 return; 294 295 const TypedRegion* R = dyn_cast<TypedRegion>(LV->stripCasts()); 296 if (!R) 297 return; 298 299 QualType T = Ctx.getCanonicalType(R->getValueType()); 300 301 // FIXME: If the pointee isn't an integer type, should we flag a warning? 302 // People can do weird stuff with pointers. 303 304 if (!T->isIntegerType()) 305 return; 306 307 uint64_t SourceSize = Ctx.getTypeSize(T); 308 309 // CHECK: is SourceSize == TargetSize 310 if (SourceSize == TargetSize) 311 return; 312 313 // Generate an error. Only generate a sink if 'SourceSize < TargetSize'; 314 // otherwise generate a regular node. 315 // 316 // FIXME: We can actually create an abstract "CFNumber" object that has 317 // the bits initialized to the provided values. 318 // 319 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink() 320 : C.generateNode()) { 321 llvm::SmallString<128> sbuf; 322 llvm::raw_svector_ostream os(sbuf); 323 324 os << (SourceSize == 8 ? "An " : "A ") 325 << SourceSize << " bit integer is used to initialize a CFNumber " 326 "object that represents " 327 << (TargetSize == 8 ? "an " : "a ") 328 << TargetSize << " bit integer. "; 329 330 if (SourceSize < TargetSize) 331 os << (TargetSize - SourceSize) 332 << " bits of the CFNumber value will be garbage." ; 333 else 334 os << (SourceSize - TargetSize) 335 << " bits of the input integer will be lost."; 336 337 if (!BT) 338 BT.reset(new APIMisuse("Bad use of CFNumberCreate")); 339 340 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); 341 report->addRange(CE->getArg(2)->getSourceRange()); 342 C.EmitReport(report); 343 } 344 } 345 346 //===----------------------------------------------------------------------===// 347 // CFRetain/CFRelease checking for null arguments. 348 //===----------------------------------------------------------------------===// 349 350 namespace { 351 class CFRetainReleaseChecker : public CheckerV2< check::PreStmt<CallExpr> > { 352 mutable llvm::OwningPtr<APIMisuse> BT; 353 mutable IdentifierInfo *Retain, *Release; 354 public: 355 CFRetainReleaseChecker(): Retain(0), Release(0) {} 356 void checkPreStmt(const CallExpr* CE, CheckerContext& C) const; 357 }; 358 } // end anonymous namespace 359 360 361 void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE, 362 CheckerContext& C) const { 363 // If the CallExpr doesn't have exactly 1 argument just give up checking. 364 if (CE->getNumArgs() != 1) 365 return; 366 367 // Get the function declaration of the callee. 368 const GRState* state = C.getState(); 369 SVal X = state->getSVal(CE->getCallee()); 370 const FunctionDecl* FD = X.getAsFunctionDecl(); 371 372 if (!FD) 373 return; 374 375 if (!BT) { 376 ASTContext &Ctx = C.getASTContext(); 377 Retain = &Ctx.Idents.get("CFRetain"); 378 Release = &Ctx.Idents.get("CFRelease"); 379 BT.reset(new APIMisuse("null passed to CFRetain/CFRelease")); 380 } 381 382 // Check if we called CFRetain/CFRelease. 383 const IdentifierInfo *FuncII = FD->getIdentifier(); 384 if (!(FuncII == Retain || FuncII == Release)) 385 return; 386 387 // FIXME: The rest of this just checks that the argument is non-null. 388 // It should probably be refactored and combined with AttrNonNullChecker. 389 390 // Get the argument's value. 391 const Expr *Arg = CE->getArg(0); 392 SVal ArgVal = state->getSVal(Arg); 393 DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal); 394 if (!DefArgVal) 395 return; 396 397 // Get a NULL value. 398 SValBuilder &svalBuilder = C.getSValBuilder(); 399 DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType())); 400 401 // Make an expression asserting that they're equal. 402 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); 403 404 // Are they equal? 405 const GRState *stateTrue, *stateFalse; 406 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); 407 408 if (stateTrue && !stateFalse) { 409 ExplodedNode *N = C.generateSink(stateTrue); 410 if (!N) 411 return; 412 413 const char *description = (FuncII == Retain) 414 ? "Null pointer argument in call to CFRetain" 415 : "Null pointer argument in call to CFRelease"; 416 417 EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N); 418 report->addRange(Arg->getSourceRange()); 419 report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg); 420 C.EmitReport(report); 421 return; 422 } 423 424 // From here on, we know the argument is non-null. 425 C.addTransition(stateFalse); 426 } 427 428 //===----------------------------------------------------------------------===// 429 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class. 430 //===----------------------------------------------------------------------===// 431 432 namespace { 433 class ClassReleaseChecker : public CheckerV2<check::PreObjCMessage> { 434 mutable Selector releaseS; 435 mutable Selector retainS; 436 mutable Selector autoreleaseS; 437 mutable Selector drainS; 438 mutable llvm::OwningPtr<BugType> BT; 439 440 public: 441 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; 442 }; 443 } 444 445 void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg, 446 CheckerContext &C) const { 447 448 if (!BT) { 449 BT.reset(new APIMisuse("message incorrectly sent to class instead of class " 450 "instance")); 451 452 ASTContext &Ctx = C.getASTContext(); 453 releaseS = GetNullarySelector("release", Ctx); 454 retainS = GetNullarySelector("retain", Ctx); 455 autoreleaseS = GetNullarySelector("autorelease", Ctx); 456 drainS = GetNullarySelector("drain", Ctx); 457 } 458 459 if (msg.isInstanceMessage()) 460 return; 461 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 462 assert(Class); 463 464 Selector S = msg.getSelector(); 465 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) 466 return; 467 468 if (ExplodedNode *N = C.generateNode()) { 469 llvm::SmallString<200> buf; 470 llvm::raw_svector_ostream os(buf); 471 472 os << "The '" << S.getAsString() << "' message should be sent to instances " 473 "of class '" << Class->getName() 474 << "' and not the class directly"; 475 476 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); 477 report->addRange(msg.getSourceRange()); 478 C.EmitReport(report); 479 } 480 } 481 482 //===----------------------------------------------------------------------===// 483 // Check registration. 484 //===----------------------------------------------------------------------===// 485 486 void ento::registerNilArgChecker(CheckerManager &mgr) { 487 mgr.registerChecker<NilArgChecker>(); 488 } 489 490 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) { 491 mgr.registerChecker<CFNumberCreateChecker>(); 492 } 493 494 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { 495 mgr.registerChecker<CFRetainReleaseChecker>(); 496 } 497 498 void ento::registerClassReleaseChecker(CheckerManager &mgr) { 499 mgr.registerChecker<ClassReleaseChecker>(); 500 } 501