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