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 "clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h" 19 #include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" 20 #include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" 21 #include "clang/StaticAnalyzer/PathSensitive/GRState.h" 22 #include "clang/StaticAnalyzer/BugReporter/BugType.h" 23 #include "clang/StaticAnalyzer/PathSensitive/MemRegion.h" 24 #include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" 25 #include "clang/StaticAnalyzer/Checkers/LocalCheckers.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 31 using namespace clang; 32 using namespace ento; 33 34 namespace { 35 class APIMisuse : public BugType { 36 public: 37 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} 38 }; 39 } // end anonymous namespace 40 41 //===----------------------------------------------------------------------===// 42 // Utility functions. 43 //===----------------------------------------------------------------------===// 44 45 static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) { 46 if (ObjCInterfaceDecl *ID = ME->getReceiverInterface()) 47 return ID->getTypeForDecl()->getAs<ObjCInterfaceType>(); 48 return NULL; 49 } 50 51 static const char* GetReceiverNameType(const ObjCMessageExpr* ME) { 52 if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME)) 53 return ReceiverType->getDecl()->getIdentifier()->getNameStart(); 54 return NULL; 55 } 56 57 static bool isNSString(llvm::StringRef ClassName) { 58 return ClassName == "NSString" || ClassName == "NSMutableString"; 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 CheckerVisitor<NilArgChecker> { 71 APIMisuse *BT; 72 void WarnNilArg(CheckerContext &C, const ObjCMessageExpr* ME, unsigned Arg); 73 public: 74 NilArgChecker() : BT(0) {} 75 static void *getTag() { static int x = 0; return &x; } 76 void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); 77 }; 78 } 79 80 void NilArgChecker::WarnNilArg(CheckerContext &C, 81 const clang::ObjCMessageExpr *ME, 82 unsigned int Arg) 83 { 84 if (!BT) 85 BT = new APIMisuse("nil argument"); 86 87 if (ExplodedNode *N = C.generateSink()) { 88 llvm::SmallString<128> sbuf; 89 llvm::raw_svector_ostream os(sbuf); 90 os << "Argument to '" << GetReceiverNameType(ME) << "' method '" 91 << ME->getSelector().getAsString() << "' cannot be nil"; 92 93 RangedBugReport *R = new RangedBugReport(*BT, os.str(), N); 94 R->addRange(ME->getArg(Arg)->getSourceRange()); 95 C.EmitReport(R); 96 } 97 } 98 99 void NilArgChecker::PreVisitObjCMessageExpr(CheckerContext &C, 100 const ObjCMessageExpr *ME) 101 { 102 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME); 103 if (!ReceiverType) 104 return; 105 106 if (isNSString(ReceiverType->getDecl()->getIdentifier()->getName())) { 107 Selector S = ME->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(C.getState()->getSVal(ME->getArg(0)))) 131 WarnNilArg(C, ME, 0); 132 } 133 } 134 } 135 136 //===----------------------------------------------------------------------===// 137 // Error reporting. 138 //===----------------------------------------------------------------------===// 139 140 namespace { 141 class CFNumberCreateChecker : public CheckerVisitor<CFNumberCreateChecker> { 142 APIMisuse* BT; 143 IdentifierInfo* II; 144 public: 145 CFNumberCreateChecker() : BT(0), II(0) {} 146 ~CFNumberCreateChecker() {} 147 static void *getTag() { static int x = 0; return &x; } 148 void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); 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::PreVisitCallExpr(CheckerContext &C, 249 const CallExpr *CE) 250 { 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 = 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 348 namespace { 349 class CFRetainReleaseChecker : public CheckerVisitor<CFRetainReleaseChecker> { 350 APIMisuse *BT; 351 IdentifierInfo *Retain, *Release; 352 public: 353 CFRetainReleaseChecker(): BT(0), Retain(0), Release(0) {} 354 static void *getTag() { static int x = 0; return &x; } 355 void PreVisitCallExpr(CheckerContext& C, const CallExpr* CE); 356 }; 357 } // end anonymous namespace 358 359 360 void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C, 361 const CallExpr* CE) { 362 // If the CallExpr doesn't have exactly 1 argument just give up checking. 363 if (CE->getNumArgs() != 1) 364 return; 365 366 // Get the function declaration of the callee. 367 const GRState* state = C.getState(); 368 SVal X = state->getSVal(CE->getCallee()); 369 const FunctionDecl* FD = X.getAsFunctionDecl(); 370 371 if (!FD) 372 return; 373 374 if (!BT) { 375 ASTContext &Ctx = C.getASTContext(); 376 Retain = &Ctx.Idents.get("CFRetain"); 377 Release = &Ctx.Idents.get("CFRelease"); 378 BT = new APIMisuse("null passed to CFRetain/CFRelease"); 379 } 380 381 // Check if we called CFRetain/CFRelease. 382 const IdentifierInfo *FuncII = FD->getIdentifier(); 383 if (!(FuncII == Retain || FuncII == Release)) 384 return; 385 386 // FIXME: The rest of this just checks that the argument is non-null. 387 // It should probably be refactored and combined with AttrNonNullChecker. 388 389 // Get the argument's value. 390 const Expr *Arg = CE->getArg(0); 391 SVal ArgVal = state->getSVal(Arg); 392 DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal); 393 if (!DefArgVal) 394 return; 395 396 // Get a NULL value. 397 SValBuilder &svalBuilder = C.getSValBuilder(); 398 DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType())); 399 400 // Make an expression asserting that they're equal. 401 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); 402 403 // Are they equal? 404 const GRState *stateTrue, *stateFalse; 405 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); 406 407 if (stateTrue && !stateFalse) { 408 ExplodedNode *N = C.generateSink(stateTrue); 409 if (!N) 410 return; 411 412 const char *description = (FuncII == Retain) 413 ? "Null pointer argument in call to CFRetain" 414 : "Null pointer argument in call to CFRelease"; 415 416 EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N); 417 report->addRange(Arg->getSourceRange()); 418 report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg); 419 C.EmitReport(report); 420 return; 421 } 422 423 // From here on, we know the argument is non-null. 424 C.addTransition(stateFalse); 425 } 426 427 //===----------------------------------------------------------------------===// 428 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class. 429 //===----------------------------------------------------------------------===// 430 431 namespace { 432 class ClassReleaseChecker : public CheckerVisitor<ClassReleaseChecker> { 433 Selector releaseS; 434 Selector retainS; 435 Selector autoreleaseS; 436 Selector drainS; 437 BugType *BT; 438 public: 439 ClassReleaseChecker() 440 : BT(0) {} 441 442 static void *getTag() { static int x = 0; return &x; } 443 444 void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); 445 }; 446 } 447 448 void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C, 449 const ObjCMessageExpr *ME) { 450 451 if (!BT) { 452 BT = new APIMisuse("message incorrectly sent to class instead of class " 453 "instance"); 454 455 ASTContext &Ctx = C.getASTContext(); 456 releaseS = GetNullarySelector("release", Ctx); 457 retainS = GetNullarySelector("retain", Ctx); 458 autoreleaseS = GetNullarySelector("autorelease", Ctx); 459 drainS = GetNullarySelector("drain", Ctx); 460 } 461 462 ObjCInterfaceDecl *Class = 0; 463 464 switch (ME->getReceiverKind()) { 465 case ObjCMessageExpr::Class: 466 Class = ME->getClassReceiver()->getAs<ObjCObjectType>()->getInterface(); 467 break; 468 case ObjCMessageExpr::SuperClass: 469 Class = ME->getSuperType()->getAs<ObjCObjectType>()->getInterface(); 470 break; 471 case ObjCMessageExpr::Instance: 472 case ObjCMessageExpr::SuperInstance: 473 return; 474 } 475 476 Selector S = ME->getSelector(); 477 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) 478 return; 479 480 if (ExplodedNode *N = C.generateNode()) { 481 llvm::SmallString<200> buf; 482 llvm::raw_svector_ostream os(buf); 483 484 os << "The '" << S.getAsString() << "' message should be sent to instances " 485 "of class '" << Class->getName() 486 << "' and not the class directly"; 487 488 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); 489 report->addRange(ME->getSourceRange()); 490 C.EmitReport(report); 491 } 492 } 493 494 //===----------------------------------------------------------------------===// 495 // Check registration. 496 //===----------------------------------------------------------------------===// 497 498 void ento::RegisterAppleChecks(ExprEngine& Eng, const Decl &D) { 499 Eng.registerCheck(new NilArgChecker()); 500 Eng.registerCheck(new CFNumberCreateChecker()); 501 RegisterNSErrorChecks(Eng.getBugReporter(), Eng, D); 502 RegisterNSAutoreleasePoolChecks(Eng); 503 Eng.registerCheck(new CFRetainReleaseChecker()); 504 Eng.registerCheck(new ClassReleaseChecker()); 505 } 506