1218887Sdim//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*-- 2218887Sdim// 3218887Sdim// The LLVM Compiler Infrastructure 4218887Sdim// 5218887Sdim// This file is distributed under the University of Illinois Open Source 6218887Sdim// License. See LICENSE.TXT for details. 7218887Sdim// 8218887Sdim//===----------------------------------------------------------------------===// 9218887Sdim// 10218887Sdim// This file defines BasicObjCFoundationChecks, a class that encapsulates 11218887Sdim// a set of simple checks to run on Objective-C code using Apple's Foundation 12218887Sdim// classes. 13218887Sdim// 14218887Sdim//===----------------------------------------------------------------------===// 15218887Sdim 16218887Sdim#include "ClangSACheckers.h" 17276479Sdim#include "SelectorExtras.h" 18249423Sdim#include "clang/AST/ASTContext.h" 19249423Sdim#include "clang/AST/DeclObjC.h" 20249423Sdim#include "clang/AST/Expr.h" 21249423Sdim#include "clang/AST/ExprObjC.h" 22249423Sdim#include "clang/AST/StmtObjC.h" 23221345Sdim#include "clang/Analysis/DomainSpecific/CocoaConventions.h" 24249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 25221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 26218887Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h" 27239462Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 28219077Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 29218887Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 30218887Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 31249423Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" 32226633Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 33234353Sdim#include "llvm/ADT/SmallString.h" 34239462Sdim#include "llvm/ADT/StringMap.h" 35249423Sdim#include "llvm/Support/raw_ostream.h" 36218887Sdim 37218887Sdimusing namespace clang; 38218887Sdimusing namespace ento; 39218887Sdim 40218887Sdimnamespace { 41218887Sdimclass APIMisuse : public BugType { 42218887Sdimpublic: 43276479Sdim APIMisuse(const CheckerBase *checker, const char *name) 44276479Sdim : BugType(checker, name, "API Misuse (Apple)") {} 45218887Sdim}; 46218887Sdim} // end anonymous namespace 47218887Sdim 48218887Sdim//===----------------------------------------------------------------------===// 49218887Sdim// Utility functions. 50218887Sdim//===----------------------------------------------------------------------===// 51218887Sdim 52239462Sdimstatic StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) { 53218887Sdim if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) 54239462Sdim return ID->getIdentifier()->getName(); 55239462Sdim return StringRef(); 56218887Sdim} 57218887Sdim 58239462Sdimenum FoundationClass { 59239462Sdim FC_None, 60239462Sdim FC_NSArray, 61239462Sdim FC_NSDictionary, 62239462Sdim FC_NSEnumerator, 63261991Sdim FC_NSNull, 64239462Sdim FC_NSOrderedSet, 65239462Sdim FC_NSSet, 66239462Sdim FC_NSString 67239462Sdim}; 68218887Sdim 69261991Sdimstatic FoundationClass findKnownClass(const ObjCInterfaceDecl *ID, 70261991Sdim bool IncludeSuperclasses = true) { 71239462Sdim static llvm::StringMap<FoundationClass> Classes; 72239462Sdim if (Classes.empty()) { 73239462Sdim Classes["NSArray"] = FC_NSArray; 74239462Sdim Classes["NSDictionary"] = FC_NSDictionary; 75239462Sdim Classes["NSEnumerator"] = FC_NSEnumerator; 76261991Sdim Classes["NSNull"] = FC_NSNull; 77239462Sdim Classes["NSOrderedSet"] = FC_NSOrderedSet; 78239462Sdim Classes["NSSet"] = FC_NSSet; 79239462Sdim Classes["NSString"] = FC_NSString; 80239462Sdim } 81221345Sdim 82239462Sdim // FIXME: Should we cache this at all? 83239462Sdim FoundationClass result = Classes.lookup(ID->getIdentifier()->getName()); 84261991Sdim if (result == FC_None && IncludeSuperclasses) 85239462Sdim if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) 86239462Sdim return findKnownClass(Super); 87239462Sdim 88239462Sdim return result; 89218887Sdim} 90218887Sdim 91218887Sdim//===----------------------------------------------------------------------===// 92218887Sdim// NilArgChecker - Check for prohibited nil arguments to ObjC method calls. 93218887Sdim//===----------------------------------------------------------------------===// 94218887Sdim 95218887Sdimnamespace { 96261991Sdim class NilArgChecker : public Checker<check::PreObjCMessage, 97261991Sdim check::PostStmt<ObjCDictionaryLiteral>, 98261991Sdim check::PostStmt<ObjCArrayLiteral> > { 99276479Sdim mutable std::unique_ptr<APIMisuse> BT; 100219077Sdim 101276479Sdim mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors; 102276479Sdim mutable Selector ArrayWithObjectSel; 103276479Sdim mutable Selector AddObjectSel; 104276479Sdim mutable Selector InsertObjectAtIndexSel; 105276479Sdim mutable Selector ReplaceObjectAtIndexWithObjectSel; 106276479Sdim mutable Selector SetObjectAtIndexedSubscriptSel; 107276479Sdim mutable Selector ArrayByAddingObjectSel; 108276479Sdim mutable Selector DictionaryWithObjectForKeySel; 109276479Sdim mutable Selector SetObjectForKeySel; 110276479Sdim mutable Selector SetObjectForKeyedSubscriptSel; 111276479Sdim mutable Selector RemoveObjectForKeySel; 112276479Sdim 113261991Sdim void warnIfNilExpr(const Expr *E, 114261991Sdim const char *Msg, 115261991Sdim CheckerContext &C) const; 116219077Sdim 117261991Sdim void warnIfNilArg(CheckerContext &C, 118261991Sdim const ObjCMethodCall &msg, unsigned Arg, 119261991Sdim FoundationClass Class, 120261991Sdim bool CanBeSubscript = false) const; 121261991Sdim 122261991Sdim void generateBugReport(ExplodedNode *N, 123261991Sdim StringRef Msg, 124261991Sdim SourceRange Range, 125261991Sdim const Expr *Expr, 126261991Sdim CheckerContext &C) const; 127261991Sdim 128218887Sdim public: 129239462Sdim void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 130261991Sdim void checkPostStmt(const ObjCDictionaryLiteral *DL, 131261991Sdim CheckerContext &C) const; 132261991Sdim void checkPostStmt(const ObjCArrayLiteral *AL, 133261991Sdim CheckerContext &C) const; 134218887Sdim }; 135218887Sdim} 136218887Sdim 137261991Sdimvoid NilArgChecker::warnIfNilExpr(const Expr *E, 138261991Sdim const char *Msg, 139261991Sdim CheckerContext &C) const { 140261991Sdim ProgramStateRef State = C.getState(); 141261991Sdim if (State->isNull(C.getSVal(E)).isConstrainedTrue()) { 142261991Sdim 143296417Sdim if (ExplodedNode *N = C.generateErrorNode()) { 144261991Sdim generateBugReport(N, Msg, E->getSourceRange(), E, C); 145261991Sdim } 146296417Sdim 147261991Sdim } 148261991Sdim} 149261991Sdim 150261991Sdimvoid NilArgChecker::warnIfNilArg(CheckerContext &C, 151249423Sdim const ObjCMethodCall &msg, 152249423Sdim unsigned int Arg, 153249423Sdim FoundationClass Class, 154249423Sdim bool CanBeSubscript) const { 155249423Sdim // Check if the argument is nil. 156249423Sdim ProgramStateRef State = C.getState(); 157249423Sdim if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue()) 158249423Sdim return; 159296417Sdim 160296417Sdim if (ExplodedNode *N = C.generateErrorNode()) { 161234353Sdim SmallString<128> sbuf; 162218887Sdim llvm::raw_svector_ostream os(sbuf); 163218887Sdim 164249423Sdim if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) { 165249423Sdim 166249423Sdim if (Class == FC_NSArray) { 167249423Sdim os << "Array element cannot be nil"; 168249423Sdim } else if (Class == FC_NSDictionary) { 169251662Sdim if (Arg == 0) { 170251662Sdim os << "Value stored into '"; 171251662Sdim os << GetReceiverInterfaceName(msg) << "' cannot be nil"; 172251662Sdim } else { 173249423Sdim assert(Arg == 1); 174251662Sdim os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil"; 175249423Sdim } 176249423Sdim } else 177249423Sdim llvm_unreachable("Missing foundation class for the subscript expr"); 178249423Sdim 179249423Sdim } else { 180251662Sdim if (Class == FC_NSDictionary) { 181251662Sdim if (Arg == 0) 182251662Sdim os << "Value argument "; 183251662Sdim else { 184251662Sdim assert(Arg == 1); 185251662Sdim os << "Key argument "; 186251662Sdim } 187276479Sdim os << "to '"; 188276479Sdim msg.getSelector().print(os); 189276479Sdim os << "' cannot be nil"; 190251662Sdim } else { 191276479Sdim os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"; 192276479Sdim msg.getSelector().print(os); 193276479Sdim os << "' cannot be nil"; 194251662Sdim } 195249423Sdim } 196296417Sdim 197261991Sdim generateBugReport(N, os.str(), msg.getArgSourceRange(Arg), 198261991Sdim msg.getArgExpr(Arg), C); 199218887Sdim } 200218887Sdim} 201218887Sdim 202261991Sdimvoid NilArgChecker::generateBugReport(ExplodedNode *N, 203261991Sdim StringRef Msg, 204261991Sdim SourceRange Range, 205261991Sdim const Expr *E, 206261991Sdim CheckerContext &C) const { 207261991Sdim if (!BT) 208276479Sdim BT.reset(new APIMisuse(this, "nil argument")); 209261991Sdim 210288943Sdim auto R = llvm::make_unique<BugReport>(*BT, Msg, N); 211261991Sdim R->addRange(Range); 212261991Sdim bugreporter::trackNullOrUndefValue(N, E, *R); 213288943Sdim C.emitReport(std::move(R)); 214261991Sdim} 215261991Sdim 216239462Sdimvoid NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 217219077Sdim CheckerContext &C) const { 218221345Sdim const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); 219221345Sdim if (!ID) 220218887Sdim return; 221249423Sdim 222249423Sdim FoundationClass Class = findKnownClass(ID); 223249423Sdim 224249423Sdim static const unsigned InvalidArgIndex = UINT_MAX; 225249423Sdim unsigned Arg = InvalidArgIndex; 226249423Sdim bool CanBeSubscript = false; 227296417Sdim 228249423Sdim if (Class == FC_NSString) { 229218887Sdim Selector S = msg.getSelector(); 230276479Sdim 231218887Sdim if (S.isUnarySelector()) 232218887Sdim return; 233276479Sdim 234276479Sdim if (StringSelectors.empty()) { 235276479Sdim ASTContext &Ctx = C.getASTContext(); 236276479Sdim Selector Sels[] = { 237276479Sdim getKeywordSelector(Ctx, "caseInsensitiveCompare", nullptr), 238276479Sdim getKeywordSelector(Ctx, "compare", nullptr), 239276479Sdim getKeywordSelector(Ctx, "compare", "options", nullptr), 240276479Sdim getKeywordSelector(Ctx, "compare", "options", "range", nullptr), 241276479Sdim getKeywordSelector(Ctx, "compare", "options", "range", "locale", 242276479Sdim nullptr), 243276479Sdim getKeywordSelector(Ctx, "componentsSeparatedByCharactersInSet", 244276479Sdim nullptr), 245276479Sdim getKeywordSelector(Ctx, "initWithFormat", 246276479Sdim nullptr), 247276479Sdim getKeywordSelector(Ctx, "localizedCaseInsensitiveCompare", nullptr), 248276479Sdim getKeywordSelector(Ctx, "localizedCompare", nullptr), 249276479Sdim getKeywordSelector(Ctx, "localizedStandardCompare", nullptr), 250276479Sdim }; 251276479Sdim for (Selector KnownSel : Sels) 252276479Sdim StringSelectors[KnownSel] = 0; 253218887Sdim } 254276479Sdim auto I = StringSelectors.find(S); 255276479Sdim if (I == StringSelectors.end()) 256276479Sdim return; 257276479Sdim Arg = I->second; 258249423Sdim } else if (Class == FC_NSArray) { 259249423Sdim Selector S = msg.getSelector(); 260249423Sdim 261249423Sdim if (S.isUnarySelector()) 262249423Sdim return; 263249423Sdim 264276479Sdim if (ArrayWithObjectSel.isNull()) { 265276479Sdim ASTContext &Ctx = C.getASTContext(); 266276479Sdim ArrayWithObjectSel = getKeywordSelector(Ctx, "arrayWithObject", nullptr); 267276479Sdim AddObjectSel = getKeywordSelector(Ctx, "addObject", nullptr); 268276479Sdim InsertObjectAtIndexSel = 269276479Sdim getKeywordSelector(Ctx, "insertObject", "atIndex", nullptr); 270276479Sdim ReplaceObjectAtIndexWithObjectSel = 271276479Sdim getKeywordSelector(Ctx, "replaceObjectAtIndex", "withObject", nullptr); 272276479Sdim SetObjectAtIndexedSubscriptSel = 273276479Sdim getKeywordSelector(Ctx, "setObject", "atIndexedSubscript", nullptr); 274276479Sdim ArrayByAddingObjectSel = 275276479Sdim getKeywordSelector(Ctx, "arrayByAddingObject", nullptr); 276276479Sdim } 277276479Sdim 278276479Sdim if (S == ArrayWithObjectSel || S == AddObjectSel || 279276479Sdim S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) { 280249423Sdim Arg = 0; 281276479Sdim } else if (S == SetObjectAtIndexedSubscriptSel) { 282249423Sdim Arg = 0; 283276479Sdim CanBeSubscript = true; 284276479Sdim } else if (S == ReplaceObjectAtIndexWithObjectSel) { 285249423Sdim Arg = 1; 286249423Sdim } 287249423Sdim } else if (Class == FC_NSDictionary) { 288249423Sdim Selector S = msg.getSelector(); 289249423Sdim 290249423Sdim if (S.isUnarySelector()) 291249423Sdim return; 292249423Sdim 293276479Sdim if (DictionaryWithObjectForKeySel.isNull()) { 294276479Sdim ASTContext &Ctx = C.getASTContext(); 295276479Sdim DictionaryWithObjectForKeySel = 296276479Sdim getKeywordSelector(Ctx, "dictionaryWithObject", "forKey", nullptr); 297276479Sdim SetObjectForKeySel = 298276479Sdim getKeywordSelector(Ctx, "setObject", "forKey", nullptr); 299276479Sdim SetObjectForKeyedSubscriptSel = 300276479Sdim getKeywordSelector(Ctx, "setObject", "forKeyedSubscript", nullptr); 301276479Sdim RemoveObjectForKeySel = 302276479Sdim getKeywordSelector(Ctx, "removeObjectForKey", nullptr); 303276479Sdim } 304276479Sdim 305276479Sdim if (S == DictionaryWithObjectForKeySel || S == SetObjectForKeySel) { 306249423Sdim Arg = 0; 307261991Sdim warnIfNilArg(C, msg, /* Arg */1, Class); 308276479Sdim } else if (S == SetObjectForKeyedSubscriptSel) { 309249423Sdim CanBeSubscript = true; 310296417Sdim Arg = 1; 311276479Sdim } else if (S == RemoveObjectForKeySel) { 312249423Sdim Arg = 0; 313249423Sdim } 314218887Sdim } 315249423Sdim 316249423Sdim // If argument is '0', report a warning. 317249423Sdim if ((Arg != InvalidArgIndex)) 318261991Sdim warnIfNilArg(C, msg, Arg, Class, CanBeSubscript); 319218887Sdim} 320218887Sdim 321261991Sdimvoid NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL, 322261991Sdim CheckerContext &C) const { 323261991Sdim unsigned NumOfElements = AL->getNumElements(); 324261991Sdim for (unsigned i = 0; i < NumOfElements; ++i) { 325261991Sdim warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C); 326261991Sdim } 327261991Sdim} 328261991Sdim 329261991Sdimvoid NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL, 330261991Sdim CheckerContext &C) const { 331261991Sdim unsigned NumOfElements = DL->getNumElements(); 332261991Sdim for (unsigned i = 0; i < NumOfElements; ++i) { 333261991Sdim ObjCDictionaryElement Element = DL->getKeyValueElement(i); 334261991Sdim warnIfNilExpr(Element.Key, "Dictionary key cannot be nil", C); 335261991Sdim warnIfNilExpr(Element.Value, "Dictionary value cannot be nil", C); 336261991Sdim } 337261991Sdim} 338261991Sdim 339218887Sdim//===----------------------------------------------------------------------===// 340218887Sdim// Error reporting. 341218887Sdim//===----------------------------------------------------------------------===// 342218887Sdim 343218887Sdimnamespace { 344221345Sdimclass CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > { 345276479Sdim mutable std::unique_ptr<APIMisuse> BT; 346219077Sdim mutable IdentifierInfo* II; 347218887Sdimpublic: 348276479Sdim CFNumberCreateChecker() : II(nullptr) {} 349219077Sdim 350219077Sdim void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 351219077Sdim 352218887Sdimprivate: 353226633Sdim void EmitError(const TypedRegion* R, const Expr *Ex, 354218887Sdim uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); 355218887Sdim}; 356218887Sdim} // end anonymous namespace 357218887Sdim 358218887Sdimenum CFNumberType { 359218887Sdim kCFNumberSInt8Type = 1, 360218887Sdim kCFNumberSInt16Type = 2, 361218887Sdim kCFNumberSInt32Type = 3, 362218887Sdim kCFNumberSInt64Type = 4, 363218887Sdim kCFNumberFloat32Type = 5, 364218887Sdim kCFNumberFloat64Type = 6, 365218887Sdim kCFNumberCharType = 7, 366218887Sdim kCFNumberShortType = 8, 367218887Sdim kCFNumberIntType = 9, 368218887Sdim kCFNumberLongType = 10, 369218887Sdim kCFNumberLongLongType = 11, 370218887Sdim kCFNumberFloatType = 12, 371218887Sdim kCFNumberDoubleType = 13, 372218887Sdim kCFNumberCFIndexType = 14, 373218887Sdim kCFNumberNSIntegerType = 15, 374218887Sdim kCFNumberCGFloatType = 16 375218887Sdim}; 376218887Sdim 377226633Sdimstatic Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { 378218887Sdim static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; 379218887Sdim 380218887Sdim if (i < kCFNumberCharType) 381218887Sdim return FixedSize[i-1]; 382218887Sdim 383218887Sdim QualType T; 384218887Sdim 385218887Sdim switch (i) { 386218887Sdim case kCFNumberCharType: T = Ctx.CharTy; break; 387218887Sdim case kCFNumberShortType: T = Ctx.ShortTy; break; 388218887Sdim case kCFNumberIntType: T = Ctx.IntTy; break; 389218887Sdim case kCFNumberLongType: T = Ctx.LongTy; break; 390218887Sdim case kCFNumberLongLongType: T = Ctx.LongLongTy; break; 391218887Sdim case kCFNumberFloatType: T = Ctx.FloatTy; break; 392218887Sdim case kCFNumberDoubleType: T = Ctx.DoubleTy; break; 393218887Sdim case kCFNumberCFIndexType: 394218887Sdim case kCFNumberNSIntegerType: 395218887Sdim case kCFNumberCGFloatType: 396218887Sdim // FIXME: We need a way to map from names to Type*. 397218887Sdim default: 398249423Sdim return None; 399218887Sdim } 400218887Sdim 401218887Sdim return Ctx.getTypeSize(T); 402218887Sdim} 403218887Sdim 404218887Sdim#if 0 405218887Sdimstatic const char* GetCFNumberTypeStr(uint64_t i) { 406218887Sdim static const char* Names[] = { 407218887Sdim "kCFNumberSInt8Type", 408218887Sdim "kCFNumberSInt16Type", 409218887Sdim "kCFNumberSInt32Type", 410218887Sdim "kCFNumberSInt64Type", 411218887Sdim "kCFNumberFloat32Type", 412218887Sdim "kCFNumberFloat64Type", 413218887Sdim "kCFNumberCharType", 414218887Sdim "kCFNumberShortType", 415218887Sdim "kCFNumberIntType", 416218887Sdim "kCFNumberLongType", 417218887Sdim "kCFNumberLongLongType", 418218887Sdim "kCFNumberFloatType", 419218887Sdim "kCFNumberDoubleType", 420218887Sdim "kCFNumberCFIndexType", 421218887Sdim "kCFNumberNSIntegerType", 422218887Sdim "kCFNumberCGFloatType" 423218887Sdim }; 424218887Sdim 425218887Sdim return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType"; 426218887Sdim} 427218887Sdim#endif 428218887Sdim 429219077Sdimvoid CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, 430219077Sdim CheckerContext &C) const { 431234353Sdim ProgramStateRef state = C.getState(); 432234353Sdim const FunctionDecl *FD = C.getCalleeDecl(CE); 433218887Sdim if (!FD) 434218887Sdim return; 435296417Sdim 436218887Sdim ASTContext &Ctx = C.getASTContext(); 437218887Sdim if (!II) 438218887Sdim II = &Ctx.Idents.get("CFNumberCreate"); 439218887Sdim 440218887Sdim if (FD->getIdentifier() != II || CE->getNumArgs() != 3) 441218887Sdim return; 442218887Sdim 443218887Sdim // Get the value of the "theType" argument. 444234353Sdim const LocationContext *LCtx = C.getLocationContext(); 445234353Sdim SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx); 446218887Sdim 447218887Sdim // FIXME: We really should allow ranges of valid theType values, and 448218887Sdim // bifurcate the state appropriately. 449249423Sdim Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>(); 450218887Sdim if (!V) 451218887Sdim return; 452218887Sdim 453218887Sdim uint64_t NumberKind = V->getValue().getLimitedValue(); 454249423Sdim Optional<uint64_t> OptTargetSize = GetCFNumberSize(Ctx, NumberKind); 455218887Sdim 456218887Sdim // FIXME: In some cases we can emit an error. 457249423Sdim if (!OptTargetSize) 458218887Sdim return; 459218887Sdim 460249423Sdim uint64_t TargetSize = *OptTargetSize; 461249423Sdim 462218887Sdim // Look at the value of the integer being passed by reference. Essentially 463218887Sdim // we want to catch cases where the value passed in is not equal to the 464218887Sdim // size of the type being created. 465234353Sdim SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx); 466218887Sdim 467218887Sdim // FIXME: Eventually we should handle arbitrary locations. We can do this 468218887Sdim // by having an enhanced memory model that does low-level typing. 469249423Sdim Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>(); 470218887Sdim if (!LV) 471218887Sdim return; 472218887Sdim 473226633Sdim const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts()); 474218887Sdim if (!R) 475218887Sdim return; 476218887Sdim 477218887Sdim QualType T = Ctx.getCanonicalType(R->getValueType()); 478218887Sdim 479218887Sdim // FIXME: If the pointee isn't an integer type, should we flag a warning? 480218887Sdim // People can do weird stuff with pointers. 481218887Sdim 482251662Sdim if (!T->isIntegralOrEnumerationType()) 483218887Sdim return; 484218887Sdim 485218887Sdim uint64_t SourceSize = Ctx.getTypeSize(T); 486218887Sdim 487218887Sdim // CHECK: is SourceSize == TargetSize 488218887Sdim if (SourceSize == TargetSize) 489218887Sdim return; 490218887Sdim 491296417Sdim // Generate an error. Only generate a sink error node 492296417Sdim // if 'SourceSize < TargetSize'; otherwise generate a non-fatal error node. 493218887Sdim // 494218887Sdim // FIXME: We can actually create an abstract "CFNumber" object that has 495218887Sdim // the bits initialized to the provided values. 496218887Sdim // 497296417Sdim ExplodedNode *N = SourceSize < TargetSize ? C.generateErrorNode() 498296417Sdim : C.generateNonFatalErrorNode(); 499296417Sdim if (N) { 500234353Sdim SmallString<128> sbuf; 501218887Sdim llvm::raw_svector_ostream os(sbuf); 502296417Sdim 503218887Sdim os << (SourceSize == 8 ? "An " : "A ") 504218887Sdim << SourceSize << " bit integer is used to initialize a CFNumber " 505218887Sdim "object that represents " 506218887Sdim << (TargetSize == 8 ? "an " : "a ") 507218887Sdim << TargetSize << " bit integer. "; 508296417Sdim 509218887Sdim if (SourceSize < TargetSize) 510218887Sdim os << (TargetSize - SourceSize) 511218887Sdim << " bits of the CFNumber value will be garbage." ; 512218887Sdim else 513218887Sdim os << (SourceSize - TargetSize) 514218887Sdim << " bits of the input integer will be lost."; 515218887Sdim 516218887Sdim if (!BT) 517276479Sdim BT.reset(new APIMisuse(this, "Bad use of CFNumberCreate")); 518276479Sdim 519288943Sdim auto report = llvm::make_unique<BugReport>(*BT, os.str(), N); 520218887Sdim report->addRange(CE->getArg(2)->getSourceRange()); 521288943Sdim C.emitReport(std::move(report)); 522218887Sdim } 523218887Sdim} 524218887Sdim 525218887Sdim//===----------------------------------------------------------------------===// 526276479Sdim// CFRetain/CFRelease/CFMakeCollectable/CFAutorelease checking for null arguments. 527218887Sdim//===----------------------------------------------------------------------===// 528218887Sdim 529218887Sdimnamespace { 530221345Sdimclass CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > { 531276479Sdim mutable std::unique_ptr<APIMisuse> BT; 532276479Sdim mutable IdentifierInfo *Retain, *Release, *MakeCollectable, *Autorelease; 533218887Sdimpublic: 534276479Sdim CFRetainReleaseChecker() 535276479Sdim : Retain(nullptr), Release(nullptr), MakeCollectable(nullptr), 536276479Sdim Autorelease(nullptr) {} 537226633Sdim void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 538218887Sdim}; 539218887Sdim} // end anonymous namespace 540218887Sdim 541218887Sdim 542226633Sdimvoid CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, 543226633Sdim CheckerContext &C) const { 544218887Sdim // If the CallExpr doesn't have exactly 1 argument just give up checking. 545218887Sdim if (CE->getNumArgs() != 1) 546218887Sdim return; 547218887Sdim 548234353Sdim ProgramStateRef state = C.getState(); 549234353Sdim const FunctionDecl *FD = C.getCalleeDecl(CE); 550218887Sdim if (!FD) 551218887Sdim return; 552296417Sdim 553218887Sdim if (!BT) { 554218887Sdim ASTContext &Ctx = C.getASTContext(); 555218887Sdim Retain = &Ctx.Idents.get("CFRetain"); 556218887Sdim Release = &Ctx.Idents.get("CFRelease"); 557243830Sdim MakeCollectable = &Ctx.Idents.get("CFMakeCollectable"); 558276479Sdim Autorelease = &Ctx.Idents.get("CFAutorelease"); 559276479Sdim BT.reset(new APIMisuse( 560276479Sdim this, "null passed to CF memory management function")); 561218887Sdim } 562218887Sdim 563276479Sdim // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease. 564218887Sdim const IdentifierInfo *FuncII = FD->getIdentifier(); 565276479Sdim if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable || 566276479Sdim FuncII == Autorelease)) 567218887Sdim return; 568218887Sdim 569218887Sdim // FIXME: The rest of this just checks that the argument is non-null. 570249423Sdim // It should probably be refactored and combined with NonNullParamChecker. 571218887Sdim 572218887Sdim // Get the argument's value. 573218887Sdim const Expr *Arg = CE->getArg(0); 574234353Sdim SVal ArgVal = state->getSVal(Arg, C.getLocationContext()); 575249423Sdim Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); 576218887Sdim if (!DefArgVal) 577218887Sdim return; 578218887Sdim 579218887Sdim // Get a NULL value. 580218887Sdim SValBuilder &svalBuilder = C.getSValBuilder(); 581249423Sdim DefinedSVal zero = 582249423Sdim svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>(); 583218887Sdim 584218887Sdim // Make an expression asserting that they're equal. 585218887Sdim DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); 586218887Sdim 587218887Sdim // Are they equal? 588234353Sdim ProgramStateRef stateTrue, stateFalse; 589276479Sdim std::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); 590218887Sdim 591218887Sdim if (stateTrue && !stateFalse) { 592296417Sdim ExplodedNode *N = C.generateErrorNode(stateTrue); 593218887Sdim if (!N) 594218887Sdim return; 595218887Sdim 596243830Sdim const char *description; 597243830Sdim if (FuncII == Retain) 598243830Sdim description = "Null pointer argument in call to CFRetain"; 599243830Sdim else if (FuncII == Release) 600243830Sdim description = "Null pointer argument in call to CFRelease"; 601243830Sdim else if (FuncII == MakeCollectable) 602243830Sdim description = "Null pointer argument in call to CFMakeCollectable"; 603276479Sdim else if (FuncII == Autorelease) 604276479Sdim description = "Null pointer argument in call to CFAutorelease"; 605243830Sdim else 606243830Sdim llvm_unreachable("impossible case"); 607218887Sdim 608288943Sdim auto report = llvm::make_unique<BugReport>(*BT, description, N); 609218887Sdim report->addRange(Arg->getSourceRange()); 610243830Sdim bugreporter::trackNullOrUndefValue(N, Arg, *report); 611288943Sdim C.emitReport(std::move(report)); 612218887Sdim return; 613218887Sdim } 614218887Sdim 615218887Sdim // From here on, we know the argument is non-null. 616218887Sdim C.addTransition(stateFalse); 617218887Sdim} 618218887Sdim 619218887Sdim//===----------------------------------------------------------------------===// 620218887Sdim// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. 621218887Sdim//===----------------------------------------------------------------------===// 622218887Sdim 623218887Sdimnamespace { 624221345Sdimclass ClassReleaseChecker : public Checker<check::PreObjCMessage> { 625219077Sdim mutable Selector releaseS; 626219077Sdim mutable Selector retainS; 627219077Sdim mutable Selector autoreleaseS; 628219077Sdim mutable Selector drainS; 629276479Sdim mutable std::unique_ptr<BugType> BT; 630219077Sdim 631218887Sdimpublic: 632239462Sdim void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 633218887Sdim}; 634218887Sdim} 635218887Sdim 636239462Sdimvoid ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 637219077Sdim CheckerContext &C) const { 638296417Sdim 639218887Sdim if (!BT) { 640276479Sdim BT.reset(new APIMisuse( 641276479Sdim this, "message incorrectly sent to class instead of class instance")); 642276479Sdim 643218887Sdim ASTContext &Ctx = C.getASTContext(); 644218887Sdim releaseS = GetNullarySelector("release", Ctx); 645218887Sdim retainS = GetNullarySelector("retain", Ctx); 646218887Sdim autoreleaseS = GetNullarySelector("autorelease", Ctx); 647218887Sdim drainS = GetNullarySelector("drain", Ctx); 648218887Sdim } 649296417Sdim 650218887Sdim if (msg.isInstanceMessage()) 651218887Sdim return; 652218887Sdim const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 653218887Sdim assert(Class); 654218887Sdim 655218887Sdim Selector S = msg.getSelector(); 656218887Sdim if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) 657218887Sdim return; 658296417Sdim 659296417Sdim if (ExplodedNode *N = C.generateNonFatalErrorNode()) { 660234353Sdim SmallString<200> buf; 661218887Sdim llvm::raw_svector_ostream os(buf); 662218887Sdim 663276479Sdim os << "The '"; 664276479Sdim S.print(os); 665276479Sdim os << "' message should be sent to instances " 666218887Sdim "of class '" << Class->getName() 667218887Sdim << "' and not the class directly"; 668296417Sdim 669288943Sdim auto report = llvm::make_unique<BugReport>(*BT, os.str(), N); 670218887Sdim report->addRange(msg.getSourceRange()); 671288943Sdim C.emitReport(std::move(report)); 672218887Sdim } 673218887Sdim} 674218887Sdim 675218887Sdim//===----------------------------------------------------------------------===// 676221345Sdim// Check for passing non-Objective-C types to variadic methods that expect 677221345Sdim// only Objective-C types. 678221345Sdim//===----------------------------------------------------------------------===// 679221345Sdim 680221345Sdimnamespace { 681221345Sdimclass VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> { 682221345Sdim mutable Selector arrayWithObjectsS; 683221345Sdim mutable Selector dictionaryWithObjectsAndKeysS; 684221345Sdim mutable Selector setWithObjectsS; 685234353Sdim mutable Selector orderedSetWithObjectsS; 686221345Sdim mutable Selector initWithObjectsS; 687221345Sdim mutable Selector initWithObjectsAndKeysS; 688276479Sdim mutable std::unique_ptr<BugType> BT; 689221345Sdim 690239462Sdim bool isVariadicMessage(const ObjCMethodCall &msg) const; 691221345Sdim 692221345Sdimpublic: 693239462Sdim void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 694221345Sdim}; 695221345Sdim} 696221345Sdim 697221345Sdim/// isVariadicMessage - Returns whether the given message is a variadic message, 698221345Sdim/// where all arguments must be Objective-C types. 699221345Sdimbool 700239462SdimVariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const { 701239462Sdim const ObjCMethodDecl *MD = msg.getDecl(); 702296417Sdim 703221345Sdim if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext())) 704221345Sdim return false; 705296417Sdim 706221345Sdim Selector S = msg.getSelector(); 707296417Sdim 708221345Sdim if (msg.isInstanceMessage()) { 709221345Sdim // FIXME: Ideally we'd look at the receiver interface here, but that's not 710221345Sdim // useful for init, because alloc returns 'id'. In theory, this could lead 711221345Sdim // to false positives, for example if there existed a class that had an 712221345Sdim // initWithObjects: implementation that does accept non-Objective-C pointer 713221345Sdim // types, but the chance of that happening is pretty small compared to the 714221345Sdim // gains that this analysis gives. 715221345Sdim const ObjCInterfaceDecl *Class = MD->getClassInterface(); 716221345Sdim 717239462Sdim switch (findKnownClass(Class)) { 718239462Sdim case FC_NSArray: 719239462Sdim case FC_NSOrderedSet: 720239462Sdim case FC_NSSet: 721239462Sdim return S == initWithObjectsS; 722239462Sdim case FC_NSDictionary: 723239462Sdim return S == initWithObjectsAndKeysS; 724239462Sdim default: 725239462Sdim return false; 726239462Sdim } 727221345Sdim } else { 728221345Sdim const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 729221345Sdim 730239462Sdim switch (findKnownClass(Class)) { 731239462Sdim case FC_NSArray: 732239462Sdim return S == arrayWithObjectsS; 733239462Sdim case FC_NSOrderedSet: 734239462Sdim return S == orderedSetWithObjectsS; 735239462Sdim case FC_NSSet: 736239462Sdim return S == setWithObjectsS; 737239462Sdim case FC_NSDictionary: 738239462Sdim return S == dictionaryWithObjectsAndKeysS; 739239462Sdim default: 740239462Sdim return false; 741239462Sdim } 742221345Sdim } 743221345Sdim} 744221345Sdim 745239462Sdimvoid VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 746221345Sdim CheckerContext &C) const { 747221345Sdim if (!BT) { 748276479Sdim BT.reset(new APIMisuse(this, 749276479Sdim "Arguments passed to variadic method aren't all " 750221345Sdim "Objective-C pointer types")); 751221345Sdim 752221345Sdim ASTContext &Ctx = C.getASTContext(); 753221345Sdim arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); 754296417Sdim dictionaryWithObjectsAndKeysS = 755221345Sdim GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); 756221345Sdim setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); 757234353Sdim orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx); 758221345Sdim 759221345Sdim initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); 760221345Sdim initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); 761221345Sdim } 762221345Sdim 763221345Sdim if (!isVariadicMessage(msg)) 764221345Sdim return; 765221345Sdim 766221345Sdim // We are not interested in the selector arguments since they have 767221345Sdim // well-defined types, so the compiler will issue a warning for them. 768221345Sdim unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); 769221345Sdim 770221345Sdim // We're not interested in the last argument since it has to be nil or the 771221345Sdim // compiler would have issued a warning for it elsewhere. 772221345Sdim unsigned variadicArgsEnd = msg.getNumArgs() - 1; 773221345Sdim 774221345Sdim if (variadicArgsEnd <= variadicArgsBegin) 775221345Sdim return; 776221345Sdim 777221345Sdim // Verify that all arguments have Objective-C types. 778249423Sdim Optional<ExplodedNode*> errorNode; 779276479Sdim 780221345Sdim for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { 781239462Sdim QualType ArgTy = msg.getArgExpr(I)->getType(); 782221345Sdim if (ArgTy->isObjCObjectPointerType()) 783221345Sdim continue; 784221345Sdim 785221345Sdim // Block pointers are treaded as Objective-C pointers. 786221345Sdim if (ArgTy->isBlockPointerType()) 787221345Sdim continue; 788221345Sdim 789221345Sdim // Ignore pointer constants. 790249423Sdim if (msg.getArgSVal(I).getAs<loc::ConcreteInt>()) 791221345Sdim continue; 792296417Sdim 793221345Sdim // Ignore pointer types annotated with 'NSObject' attribute. 794221345Sdim if (C.getASTContext().isObjCNSObjectType(ArgTy)) 795221345Sdim continue; 796296417Sdim 797221345Sdim // Ignore CF references, which can be toll-free bridged. 798224145Sdim if (coreFoundation::isCFObjectRef(ArgTy)) 799221345Sdim continue; 800221345Sdim 801221345Sdim // Generate only one error node to use for all bug reports. 802239462Sdim if (!errorNode.hasValue()) 803296417Sdim errorNode = C.generateNonFatalErrorNode(); 804221345Sdim 805221345Sdim if (!errorNode.getValue()) 806221345Sdim continue; 807221345Sdim 808234353Sdim SmallString<128> sbuf; 809221345Sdim llvm::raw_svector_ostream os(sbuf); 810221345Sdim 811239462Sdim StringRef TypeName = GetReceiverInterfaceName(msg); 812239462Sdim if (!TypeName.empty()) 813221345Sdim os << "Argument to '" << TypeName << "' method '"; 814221345Sdim else 815221345Sdim os << "Argument to method '"; 816221345Sdim 817276479Sdim msg.getSelector().print(os); 818276479Sdim os << "' should be an Objective-C pointer type, not '"; 819239462Sdim ArgTy.print(os, C.getLangOpts()); 820239462Sdim os << "'"; 821221345Sdim 822288943Sdim auto R = llvm::make_unique<BugReport>(*BT, os.str(), errorNode.getValue()); 823221345Sdim R->addRange(msg.getArgSourceRange(I)); 824288943Sdim C.emitReport(std::move(R)); 825221345Sdim } 826221345Sdim} 827221345Sdim 828221345Sdim//===----------------------------------------------------------------------===// 829239462Sdim// Improves the modeling of loops over Cocoa collections. 830239462Sdim//===----------------------------------------------------------------------===// 831239462Sdim 832261991Sdim// The map from container symbol to the container count symbol. 833261991Sdim// We currently will remember the last countainer count symbol encountered. 834261991SdimREGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap, SymbolRef, SymbolRef) 835261991SdimREGISTER_MAP_WITH_PROGRAMSTATE(ContainerNonEmptyMap, SymbolRef, bool) 836261991Sdim 837239462Sdimnamespace { 838239462Sdimclass ObjCLoopChecker 839261991Sdim : public Checker<check::PostStmt<ObjCForCollectionStmt>, 840261991Sdim check::PostObjCMessage, 841261991Sdim check::DeadSymbols, 842261991Sdim check::PointerEscape > { 843261991Sdim mutable IdentifierInfo *CountSelectorII; 844261991Sdim 845261991Sdim bool isCollectionCountMethod(const ObjCMethodCall &M, 846261991Sdim CheckerContext &C) const; 847261991Sdim 848239462Sdimpublic: 849276479Sdim ObjCLoopChecker() : CountSelectorII(nullptr) {} 850239462Sdim void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const; 851261991Sdim void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 852261991Sdim void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 853261991Sdim ProgramStateRef checkPointerEscape(ProgramStateRef State, 854261991Sdim const InvalidatedSymbols &Escaped, 855261991Sdim const CallEvent *Call, 856261991Sdim PointerEscapeKind Kind) const; 857239462Sdim}; 858239462Sdim} 859239462Sdim 860239462Sdimstatic bool isKnownNonNilCollectionType(QualType T) { 861239462Sdim const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); 862239462Sdim if (!PT) 863239462Sdim return false; 864296417Sdim 865239462Sdim const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); 866239462Sdim if (!ID) 867239462Sdim return false; 868239462Sdim 869239462Sdim switch (findKnownClass(ID)) { 870239462Sdim case FC_NSArray: 871239462Sdim case FC_NSDictionary: 872239462Sdim case FC_NSEnumerator: 873239462Sdim case FC_NSOrderedSet: 874239462Sdim case FC_NSSet: 875239462Sdim return true; 876239462Sdim default: 877239462Sdim return false; 878239462Sdim } 879239462Sdim} 880239462Sdim 881251662Sdim/// Assumes that the collection is non-nil. 882251662Sdim/// 883251662Sdim/// If the collection is known to be nil, returns NULL to indicate an infeasible 884251662Sdim/// path. 885251662Sdimstatic ProgramStateRef checkCollectionNonNil(CheckerContext &C, 886251662Sdim ProgramStateRef State, 887251662Sdim const ObjCForCollectionStmt *FCS) { 888251662Sdim if (!State) 889276479Sdim return nullptr; 890251662Sdim 891251662Sdim SVal CollectionVal = C.getSVal(FCS->getCollection()); 892251662Sdim Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>(); 893251662Sdim if (!KnownCollection) 894251662Sdim return State; 895251662Sdim 896251662Sdim ProgramStateRef StNonNil, StNil; 897276479Sdim std::tie(StNonNil, StNil) = State->assume(*KnownCollection); 898251662Sdim if (StNil && !StNonNil) { 899251662Sdim // The collection is nil. This path is infeasible. 900276479Sdim return nullptr; 901251662Sdim } 902251662Sdim 903251662Sdim return StNonNil; 904251662Sdim} 905251662Sdim 906251662Sdim/// Assumes that the collection elements are non-nil. 907251662Sdim/// 908251662Sdim/// This only applies if the collection is one of those known not to contain 909251662Sdim/// nil values. 910251662Sdimstatic ProgramStateRef checkElementNonNil(CheckerContext &C, 911251662Sdim ProgramStateRef State, 912251662Sdim const ObjCForCollectionStmt *FCS) { 913251662Sdim if (!State) 914276479Sdim return nullptr; 915251662Sdim 916239462Sdim // See if the collection is one where we /know/ the elements are non-nil. 917251662Sdim if (!isKnownNonNilCollectionType(FCS->getCollection()->getType())) 918251662Sdim return State; 919251662Sdim 920251662Sdim const LocationContext *LCtx = C.getLocationContext(); 921251662Sdim const Stmt *Element = FCS->getElement(); 922251662Sdim 923239462Sdim // FIXME: Copied from ExprEngineObjC. 924251662Sdim Optional<Loc> ElementLoc; 925239462Sdim if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { 926239462Sdim const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); 927276479Sdim assert(ElemDecl->getInit() == nullptr); 928251662Sdim ElementLoc = State->getLValue(ElemDecl, LCtx); 929239462Sdim } else { 930251662Sdim ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>(); 931239462Sdim } 932239462Sdim 933251662Sdim if (!ElementLoc) 934251662Sdim return State; 935239462Sdim 936239462Sdim // Go ahead and assume the value is non-nil. 937251662Sdim SVal Val = State->getSVal(*ElementLoc); 938251662Sdim return State->assume(Val.castAs<DefinedOrUnknownSVal>(), true); 939239462Sdim} 940239462Sdim 941261991Sdim/// Returns NULL state if the collection is known to contain elements 942261991Sdim/// (or is known not to contain elements if the Assumption parameter is false.) 943261991Sdimstatic ProgramStateRef 944261991SdimassumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, 945261991Sdim SymbolRef CollectionS, bool Assumption) { 946261991Sdim if (!State || !CollectionS) 947261991Sdim return State; 948261991Sdim 949261991Sdim const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS); 950261991Sdim if (!CountS) { 951261991Sdim const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS); 952261991Sdim if (!KnownNonEmpty) 953261991Sdim return State->set<ContainerNonEmptyMap>(CollectionS, Assumption); 954276479Sdim return (Assumption == *KnownNonEmpty) ? State : nullptr; 955261991Sdim } 956261991Sdim 957261991Sdim SValBuilder &SvalBuilder = C.getSValBuilder(); 958261991Sdim SVal CountGreaterThanZeroVal = 959261991Sdim SvalBuilder.evalBinOp(State, BO_GT, 960261991Sdim nonloc::SymbolVal(*CountS), 961261991Sdim SvalBuilder.makeIntVal(0, (*CountS)->getType()), 962261991Sdim SvalBuilder.getConditionType()); 963261991Sdim Optional<DefinedSVal> CountGreaterThanZero = 964261991Sdim CountGreaterThanZeroVal.getAs<DefinedSVal>(); 965261991Sdim if (!CountGreaterThanZero) { 966261991Sdim // The SValBuilder cannot construct a valid SVal for this condition. 967261991Sdim // This means we cannot properly reason about it. 968261991Sdim return State; 969261991Sdim } 970261991Sdim 971261991Sdim return State->assume(*CountGreaterThanZero, Assumption); 972261991Sdim} 973261991Sdim 974261991Sdimstatic ProgramStateRef 975261991SdimassumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, 976261991Sdim const ObjCForCollectionStmt *FCS, 977261991Sdim bool Assumption) { 978261991Sdim if (!State) 979276479Sdim return nullptr; 980261991Sdim 981261991Sdim SymbolRef CollectionS = 982261991Sdim State->getSVal(FCS->getCollection(), C.getLocationContext()).getAsSymbol(); 983261991Sdim return assumeCollectionNonEmpty(C, State, CollectionS, Assumption); 984261991Sdim} 985261991Sdim 986261991Sdim 987261991Sdim/// If the fist block edge is a back edge, we are reentering the loop. 988261991Sdimstatic bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N, 989261991Sdim const ObjCForCollectionStmt *FCS) { 990261991Sdim if (!N) 991261991Sdim return false; 992261991Sdim 993261991Sdim ProgramPoint P = N->getLocation(); 994261991Sdim if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { 995296417Sdim return BE->getSrc()->getLoopTarget() == FCS; 996261991Sdim } 997261991Sdim 998261991Sdim // Keep looking for a block edge. 999261991Sdim for (ExplodedNode::const_pred_iterator I = N->pred_begin(), 1000261991Sdim E = N->pred_end(); I != E; ++I) { 1001261991Sdim if (alreadyExecutedAtLeastOneLoopIteration(*I, FCS)) 1002261991Sdim return true; 1003261991Sdim } 1004261991Sdim 1005261991Sdim return false; 1006261991Sdim} 1007261991Sdim 1008251662Sdimvoid ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, 1009251662Sdim CheckerContext &C) const { 1010261991Sdim ProgramStateRef State = C.getState(); 1011261991Sdim 1012251662Sdim // Check if this is the branch for the end of the loop. 1013251662Sdim SVal CollectionSentinel = C.getSVal(FCS); 1014261991Sdim if (CollectionSentinel.isZeroConstant()) { 1015261991Sdim if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS)) 1016261991Sdim State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false); 1017251662Sdim 1018261991Sdim // Otherwise, this is a branch that goes through the loop body. 1019261991Sdim } else { 1020261991Sdim State = checkCollectionNonNil(C, State, FCS); 1021261991Sdim State = checkElementNonNil(C, State, FCS); 1022261991Sdim State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true); 1023261991Sdim } 1024296417Sdim 1025251662Sdim if (!State) 1026296417Sdim C.generateSink(C.getState(), C.getPredecessor()); 1027251662Sdim else if (State != C.getState()) 1028251662Sdim C.addTransition(State); 1029251662Sdim} 1030251662Sdim 1031261991Sdimbool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M, 1032261991Sdim CheckerContext &C) const { 1033261991Sdim Selector S = M.getSelector(); 1034261991Sdim // Initialize the identifiers on first use. 1035261991Sdim if (!CountSelectorII) 1036261991Sdim CountSelectorII = &C.getASTContext().Idents.get("count"); 1037261991Sdim 1038261991Sdim // If the method returns collection count, record the value. 1039296417Sdim return S.isUnarySelector() && 1040296417Sdim (S.getIdentifierInfoForSlot(0) == CountSelectorII); 1041261991Sdim} 1042261991Sdim 1043261991Sdimvoid ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M, 1044261991Sdim CheckerContext &C) const { 1045261991Sdim if (!M.isInstanceMessage()) 1046261991Sdim return; 1047261991Sdim 1048261991Sdim const ObjCInterfaceDecl *ClassID = M.getReceiverInterface(); 1049261991Sdim if (!ClassID) 1050261991Sdim return; 1051261991Sdim 1052261991Sdim FoundationClass Class = findKnownClass(ClassID); 1053261991Sdim if (Class != FC_NSDictionary && 1054261991Sdim Class != FC_NSArray && 1055261991Sdim Class != FC_NSSet && 1056261991Sdim Class != FC_NSOrderedSet) 1057261991Sdim return; 1058261991Sdim 1059261991Sdim SymbolRef ContainerS = M.getReceiverSVal().getAsSymbol(); 1060261991Sdim if (!ContainerS) 1061261991Sdim return; 1062261991Sdim 1063261991Sdim // If we are processing a call to "count", get the symbolic value returned by 1064261991Sdim // a call to "count" and add it to the map. 1065261991Sdim if (!isCollectionCountMethod(M, C)) 1066261991Sdim return; 1067296417Sdim 1068261991Sdim const Expr *MsgExpr = M.getOriginExpr(); 1069261991Sdim SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol(); 1070261991Sdim if (CountS) { 1071261991Sdim ProgramStateRef State = C.getState(); 1072261991Sdim 1073261991Sdim C.getSymbolManager().addSymbolDependency(ContainerS, CountS); 1074261991Sdim State = State->set<ContainerCountMap>(ContainerS, CountS); 1075261991Sdim 1076261991Sdim if (const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) { 1077261991Sdim State = State->remove<ContainerNonEmptyMap>(ContainerS); 1078261991Sdim State = assumeCollectionNonEmpty(C, State, ContainerS, *NonEmpty); 1079261991Sdim } 1080261991Sdim 1081261991Sdim C.addTransition(State); 1082261991Sdim } 1083261991Sdim return; 1084261991Sdim} 1085261991Sdim 1086261991Sdimstatic SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call) { 1087261991Sdim const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call); 1088261991Sdim if (!Message) 1089276479Sdim return nullptr; 1090261991Sdim 1091261991Sdim const ObjCMethodDecl *MD = Message->getDecl(); 1092261991Sdim if (!MD) 1093276479Sdim return nullptr; 1094261991Sdim 1095261991Sdim const ObjCInterfaceDecl *StaticClass; 1096261991Sdim if (isa<ObjCProtocolDecl>(MD->getDeclContext())) { 1097261991Sdim // We can't find out where the method was declared without doing more work. 1098261991Sdim // Instead, see if the receiver is statically typed as a known immutable 1099261991Sdim // collection. 1100261991Sdim StaticClass = Message->getOriginExpr()->getReceiverInterface(); 1101261991Sdim } else { 1102261991Sdim StaticClass = MD->getClassInterface(); 1103261991Sdim } 1104261991Sdim 1105261991Sdim if (!StaticClass) 1106276479Sdim return nullptr; 1107261991Sdim 1108261991Sdim switch (findKnownClass(StaticClass, /*IncludeSuper=*/false)) { 1109261991Sdim case FC_None: 1110276479Sdim return nullptr; 1111261991Sdim case FC_NSArray: 1112261991Sdim case FC_NSDictionary: 1113261991Sdim case FC_NSEnumerator: 1114261991Sdim case FC_NSNull: 1115261991Sdim case FC_NSOrderedSet: 1116261991Sdim case FC_NSSet: 1117261991Sdim case FC_NSString: 1118261991Sdim break; 1119261991Sdim } 1120261991Sdim 1121261991Sdim return Message->getReceiverSVal().getAsSymbol(); 1122261991Sdim} 1123261991Sdim 1124261991SdimProgramStateRef 1125261991SdimObjCLoopChecker::checkPointerEscape(ProgramStateRef State, 1126261991Sdim const InvalidatedSymbols &Escaped, 1127261991Sdim const CallEvent *Call, 1128261991Sdim PointerEscapeKind Kind) const { 1129261991Sdim SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call); 1130261991Sdim 1131261991Sdim // Remove the invalidated symbols form the collection count map. 1132261991Sdim for (InvalidatedSymbols::const_iterator I = Escaped.begin(), 1133261991Sdim E = Escaped.end(); 1134261991Sdim I != E; ++I) { 1135261991Sdim SymbolRef Sym = *I; 1136261991Sdim 1137261991Sdim // Don't invalidate this symbol's count if we know the method being called 1138261991Sdim // is declared on an immutable class. This isn't completely correct if the 1139261991Sdim // receiver is also passed as an argument, but in most uses of NSArray, 1140261991Sdim // NSDictionary, etc. this isn't likely to happen in a dangerous way. 1141261991Sdim if (Sym == ImmutableReceiver) 1142261991Sdim continue; 1143261991Sdim 1144261991Sdim // The symbol escaped. Pessimistically, assume that the count could have 1145261991Sdim // changed. 1146261991Sdim State = State->remove<ContainerCountMap>(Sym); 1147261991Sdim State = State->remove<ContainerNonEmptyMap>(Sym); 1148261991Sdim } 1149261991Sdim return State; 1150261991Sdim} 1151261991Sdim 1152261991Sdimvoid ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper, 1153261991Sdim CheckerContext &C) const { 1154261991Sdim ProgramStateRef State = C.getState(); 1155261991Sdim 1156261991Sdim // Remove the dead symbols from the collection count map. 1157261991Sdim ContainerCountMapTy Tracked = State->get<ContainerCountMap>(); 1158261991Sdim for (ContainerCountMapTy::iterator I = Tracked.begin(), 1159261991Sdim E = Tracked.end(); I != E; ++I) { 1160261991Sdim SymbolRef Sym = I->first; 1161261991Sdim if (SymReaper.isDead(Sym)) { 1162261991Sdim State = State->remove<ContainerCountMap>(Sym); 1163261991Sdim State = State->remove<ContainerNonEmptyMap>(Sym); 1164261991Sdim } 1165261991Sdim } 1166261991Sdim 1167261991Sdim C.addTransition(State); 1168261991Sdim} 1169261991Sdim 1170243830Sdimnamespace { 1171243830Sdim/// \class ObjCNonNilReturnValueChecker 1172243830Sdim/// \brief The checker restricts the return values of APIs known to 1173243830Sdim/// never (or almost never) return 'nil'. 1174243830Sdimclass ObjCNonNilReturnValueChecker 1175276479Sdim : public Checker<check::PostObjCMessage, 1176276479Sdim check::PostStmt<ObjCArrayLiteral>, 1177276479Sdim check::PostStmt<ObjCDictionaryLiteral>, 1178276479Sdim check::PostStmt<ObjCBoxedExpr> > { 1179243830Sdim mutable bool Initialized; 1180243830Sdim mutable Selector ObjectAtIndex; 1181243830Sdim mutable Selector ObjectAtIndexedSubscript; 1182261991Sdim mutable Selector NullSelector; 1183239462Sdim 1184243830Sdimpublic: 1185243830Sdim ObjCNonNilReturnValueChecker() : Initialized(false) {} 1186276479Sdim 1187276479Sdim ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr, 1188276479Sdim ProgramStateRef State, 1189276479Sdim CheckerContext &C) const; 1190276479Sdim void assumeExprIsNonNull(const Expr *E, CheckerContext &C) const { 1191276479Sdim C.addTransition(assumeExprIsNonNull(E, C.getState(), C)); 1192276479Sdim } 1193276479Sdim 1194276479Sdim void checkPostStmt(const ObjCArrayLiteral *E, CheckerContext &C) const { 1195276479Sdim assumeExprIsNonNull(E, C); 1196276479Sdim } 1197276479Sdim void checkPostStmt(const ObjCDictionaryLiteral *E, CheckerContext &C) const { 1198276479Sdim assumeExprIsNonNull(E, C); 1199276479Sdim } 1200276479Sdim void checkPostStmt(const ObjCBoxedExpr *E, CheckerContext &C) const { 1201276479Sdim assumeExprIsNonNull(E, C); 1202276479Sdim } 1203276479Sdim 1204243830Sdim void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 1205243830Sdim}; 1206243830Sdim} 1207243830Sdim 1208276479SdimProgramStateRef 1209276479SdimObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr, 1210276479Sdim ProgramStateRef State, 1211276479Sdim CheckerContext &C) const { 1212243830Sdim SVal Val = State->getSVal(NonNullExpr, C.getLocationContext()); 1213249423Sdim if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>()) 1214243830Sdim return State->assume(*DV, true); 1215243830Sdim return State; 1216243830Sdim} 1217243830Sdim 1218243830Sdimvoid ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M, 1219243830Sdim CheckerContext &C) 1220243830Sdim const { 1221243830Sdim ProgramStateRef State = C.getState(); 1222243830Sdim 1223243830Sdim if (!Initialized) { 1224243830Sdim ASTContext &Ctx = C.getASTContext(); 1225243830Sdim ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx); 1226243830Sdim ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx); 1227261991Sdim NullSelector = GetNullarySelector("null", Ctx); 1228243830Sdim } 1229243830Sdim 1230243830Sdim // Check the receiver type. 1231243830Sdim if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) { 1232243830Sdim 1233243830Sdim // Assume that object returned from '[self init]' or '[super init]' is not 1234243830Sdim // 'nil' if we are processing an inlined function/method. 1235243830Sdim // 1236243830Sdim // A defensive callee will (and should) check if the object returned by 1237243830Sdim // '[super init]' is 'nil' before doing it's own initialization. However, 1238243830Sdim // since 'nil' is rarely returned in practice, we should not warn when the 1239243830Sdim // caller to the defensive constructor uses the object in contexts where 1240243830Sdim // 'nil' is not accepted. 1241243830Sdim if (!C.inTopFrame() && M.getDecl() && 1242243830Sdim M.getDecl()->getMethodFamily() == OMF_init && 1243243830Sdim M.isReceiverSelfOrSuper()) { 1244243830Sdim State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 1245243830Sdim } 1246243830Sdim 1247261991Sdim FoundationClass Cl = findKnownClass(Interface); 1248261991Sdim 1249243830Sdim // Objects returned from 1250243830Sdim // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript] 1251243830Sdim // are never 'nil'. 1252243830Sdim if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) { 1253243830Sdim Selector Sel = M.getSelector(); 1254243830Sdim if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) { 1255243830Sdim // Go ahead and assume the value is non-nil. 1256243830Sdim State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 1257243830Sdim } 1258243830Sdim } 1259261991Sdim 1260261991Sdim // Objects returned from [NSNull null] are not nil. 1261261991Sdim if (Cl == FC_NSNull) { 1262261991Sdim if (M.getSelector() == NullSelector) { 1263261991Sdim // Go ahead and assume the value is non-nil. 1264261991Sdim State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 1265261991Sdim } 1266261991Sdim } 1267243830Sdim } 1268243830Sdim C.addTransition(State); 1269243830Sdim} 1270243830Sdim 1271239462Sdim//===----------------------------------------------------------------------===// 1272218887Sdim// Check registration. 1273218887Sdim//===----------------------------------------------------------------------===// 1274218887Sdim 1275218887Sdimvoid ento::registerNilArgChecker(CheckerManager &mgr) { 1276219077Sdim mgr.registerChecker<NilArgChecker>(); 1277218887Sdim} 1278218887Sdim 1279218887Sdimvoid ento::registerCFNumberCreateChecker(CheckerManager &mgr) { 1280219077Sdim mgr.registerChecker<CFNumberCreateChecker>(); 1281218887Sdim} 1282218887Sdim 1283218887Sdimvoid ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { 1284219077Sdim mgr.registerChecker<CFRetainReleaseChecker>(); 1285218887Sdim} 1286218887Sdim 1287218887Sdimvoid ento::registerClassReleaseChecker(CheckerManager &mgr) { 1288219077Sdim mgr.registerChecker<ClassReleaseChecker>(); 1289218887Sdim} 1290221345Sdim 1291221345Sdimvoid ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { 1292221345Sdim mgr.registerChecker<VariadicMethodTypeChecker>(); 1293221345Sdim} 1294239462Sdim 1295239462Sdimvoid ento::registerObjCLoopChecker(CheckerManager &mgr) { 1296239462Sdim mgr.registerChecker<ObjCLoopChecker>(); 1297239462Sdim} 1298243830Sdim 1299276479Sdimvoid 1300276479Sdimento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) { 1301243830Sdim mgr.registerChecker<ObjCNonNilReturnValueChecker>(); 1302243830Sdim} 1303