1*0b57cec5SDimitry Andric //== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*-- 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // This file defines BasicObjCFoundationChecks, a class that encapsulates 10*0b57cec5SDimitry Andric // a set of simple checks to run on Objective-C code using Apple's Foundation 11*0b57cec5SDimitry Andric // classes. 12*0b57cec5SDimitry Andric // 13*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 14*0b57cec5SDimitry Andric 15*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 16*0b57cec5SDimitry Andric #include "clang/AST/ASTContext.h" 17*0b57cec5SDimitry Andric #include "clang/AST/DeclObjC.h" 18*0b57cec5SDimitry Andric #include "clang/AST/Expr.h" 19*0b57cec5SDimitry Andric #include "clang/AST/ExprObjC.h" 20*0b57cec5SDimitry Andric #include "clang/AST/StmtObjC.h" 21*0b57cec5SDimitry Andric #include "clang/Analysis/DomainSpecific/CocoaConventions.h" 22*0b57cec5SDimitry Andric #include "clang/Analysis/SelectorExtras.h" 23*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 24*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 25*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 26*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 27*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 28*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 29*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 30*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" 31*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 32*0b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 33*0b57cec5SDimitry Andric #include "llvm/ADT/StringMap.h" 34*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 35*0b57cec5SDimitry Andric 36*0b57cec5SDimitry Andric using namespace clang; 37*0b57cec5SDimitry Andric using namespace ento; 38*0b57cec5SDimitry Andric using namespace llvm; 39*0b57cec5SDimitry Andric 40*0b57cec5SDimitry Andric namespace { 41*0b57cec5SDimitry Andric class APIMisuse : public BugType { 42*0b57cec5SDimitry Andric public: 43*0b57cec5SDimitry Andric APIMisuse(const CheckerBase *checker, const char *name) 44*0b57cec5SDimitry Andric : BugType(checker, name, "API Misuse (Apple)") {} 45*0b57cec5SDimitry Andric }; 46*0b57cec5SDimitry Andric } // end anonymous namespace 47*0b57cec5SDimitry Andric 48*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 49*0b57cec5SDimitry Andric // Utility functions. 50*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 51*0b57cec5SDimitry Andric 52*0b57cec5SDimitry Andric static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) { 53*0b57cec5SDimitry Andric if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) 54*0b57cec5SDimitry Andric return ID->getIdentifier()->getName(); 55*0b57cec5SDimitry Andric return StringRef(); 56*0b57cec5SDimitry Andric } 57*0b57cec5SDimitry Andric 58*0b57cec5SDimitry Andric enum FoundationClass { 59*0b57cec5SDimitry Andric FC_None, 60*0b57cec5SDimitry Andric FC_NSArray, 61*0b57cec5SDimitry Andric FC_NSDictionary, 62*0b57cec5SDimitry Andric FC_NSEnumerator, 63*0b57cec5SDimitry Andric FC_NSNull, 64*0b57cec5SDimitry Andric FC_NSOrderedSet, 65*0b57cec5SDimitry Andric FC_NSSet, 66*0b57cec5SDimitry Andric FC_NSString 67*0b57cec5SDimitry Andric }; 68*0b57cec5SDimitry Andric 69*0b57cec5SDimitry Andric static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID, 70*0b57cec5SDimitry Andric bool IncludeSuperclasses = true) { 71*0b57cec5SDimitry Andric static llvm::StringMap<FoundationClass> Classes; 72*0b57cec5SDimitry Andric if (Classes.empty()) { 73*0b57cec5SDimitry Andric Classes["NSArray"] = FC_NSArray; 74*0b57cec5SDimitry Andric Classes["NSDictionary"] = FC_NSDictionary; 75*0b57cec5SDimitry Andric Classes["NSEnumerator"] = FC_NSEnumerator; 76*0b57cec5SDimitry Andric Classes["NSNull"] = FC_NSNull; 77*0b57cec5SDimitry Andric Classes["NSOrderedSet"] = FC_NSOrderedSet; 78*0b57cec5SDimitry Andric Classes["NSSet"] = FC_NSSet; 79*0b57cec5SDimitry Andric Classes["NSString"] = FC_NSString; 80*0b57cec5SDimitry Andric } 81*0b57cec5SDimitry Andric 82*0b57cec5SDimitry Andric // FIXME: Should we cache this at all? 83*0b57cec5SDimitry Andric FoundationClass result = Classes.lookup(ID->getIdentifier()->getName()); 84*0b57cec5SDimitry Andric if (result == FC_None && IncludeSuperclasses) 85*0b57cec5SDimitry Andric if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) 86*0b57cec5SDimitry Andric return findKnownClass(Super); 87*0b57cec5SDimitry Andric 88*0b57cec5SDimitry Andric return result; 89*0b57cec5SDimitry Andric } 90*0b57cec5SDimitry Andric 91*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 92*0b57cec5SDimitry Andric // NilArgChecker - Check for prohibited nil arguments to ObjC method calls. 93*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 94*0b57cec5SDimitry Andric 95*0b57cec5SDimitry Andric namespace { 96*0b57cec5SDimitry Andric class NilArgChecker : public Checker<check::PreObjCMessage, 97*0b57cec5SDimitry Andric check::PostStmt<ObjCDictionaryLiteral>, 98*0b57cec5SDimitry Andric check::PostStmt<ObjCArrayLiteral> > { 99*0b57cec5SDimitry Andric mutable std::unique_ptr<APIMisuse> BT; 100*0b57cec5SDimitry Andric 101*0b57cec5SDimitry Andric mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors; 102*0b57cec5SDimitry Andric mutable Selector ArrayWithObjectSel; 103*0b57cec5SDimitry Andric mutable Selector AddObjectSel; 104*0b57cec5SDimitry Andric mutable Selector InsertObjectAtIndexSel; 105*0b57cec5SDimitry Andric mutable Selector ReplaceObjectAtIndexWithObjectSel; 106*0b57cec5SDimitry Andric mutable Selector SetObjectAtIndexedSubscriptSel; 107*0b57cec5SDimitry Andric mutable Selector ArrayByAddingObjectSel; 108*0b57cec5SDimitry Andric mutable Selector DictionaryWithObjectForKeySel; 109*0b57cec5SDimitry Andric mutable Selector SetObjectForKeySel; 110*0b57cec5SDimitry Andric mutable Selector SetObjectForKeyedSubscriptSel; 111*0b57cec5SDimitry Andric mutable Selector RemoveObjectForKeySel; 112*0b57cec5SDimitry Andric 113*0b57cec5SDimitry Andric void warnIfNilExpr(const Expr *E, 114*0b57cec5SDimitry Andric const char *Msg, 115*0b57cec5SDimitry Andric CheckerContext &C) const; 116*0b57cec5SDimitry Andric 117*0b57cec5SDimitry Andric void warnIfNilArg(CheckerContext &C, 118*0b57cec5SDimitry Andric const ObjCMethodCall &msg, unsigned Arg, 119*0b57cec5SDimitry Andric FoundationClass Class, 120*0b57cec5SDimitry Andric bool CanBeSubscript = false) const; 121*0b57cec5SDimitry Andric 122*0b57cec5SDimitry Andric void generateBugReport(ExplodedNode *N, 123*0b57cec5SDimitry Andric StringRef Msg, 124*0b57cec5SDimitry Andric SourceRange Range, 125*0b57cec5SDimitry Andric const Expr *Expr, 126*0b57cec5SDimitry Andric CheckerContext &C) const; 127*0b57cec5SDimitry Andric 128*0b57cec5SDimitry Andric public: 129*0b57cec5SDimitry Andric void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 130*0b57cec5SDimitry Andric void checkPostStmt(const ObjCDictionaryLiteral *DL, 131*0b57cec5SDimitry Andric CheckerContext &C) const; 132*0b57cec5SDimitry Andric void checkPostStmt(const ObjCArrayLiteral *AL, 133*0b57cec5SDimitry Andric CheckerContext &C) const; 134*0b57cec5SDimitry Andric }; 135*0b57cec5SDimitry Andric } // end anonymous namespace 136*0b57cec5SDimitry Andric 137*0b57cec5SDimitry Andric void NilArgChecker::warnIfNilExpr(const Expr *E, 138*0b57cec5SDimitry Andric const char *Msg, 139*0b57cec5SDimitry Andric CheckerContext &C) const { 140*0b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 141*0b57cec5SDimitry Andric if (State->isNull(C.getSVal(E)).isConstrainedTrue()) { 142*0b57cec5SDimitry Andric 143*0b57cec5SDimitry Andric if (ExplodedNode *N = C.generateErrorNode()) { 144*0b57cec5SDimitry Andric generateBugReport(N, Msg, E->getSourceRange(), E, C); 145*0b57cec5SDimitry Andric } 146*0b57cec5SDimitry Andric } 147*0b57cec5SDimitry Andric } 148*0b57cec5SDimitry Andric 149*0b57cec5SDimitry Andric void NilArgChecker::warnIfNilArg(CheckerContext &C, 150*0b57cec5SDimitry Andric const ObjCMethodCall &msg, 151*0b57cec5SDimitry Andric unsigned int Arg, 152*0b57cec5SDimitry Andric FoundationClass Class, 153*0b57cec5SDimitry Andric bool CanBeSubscript) const { 154*0b57cec5SDimitry Andric // Check if the argument is nil. 155*0b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 156*0b57cec5SDimitry Andric if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue()) 157*0b57cec5SDimitry Andric return; 158*0b57cec5SDimitry Andric 159*0b57cec5SDimitry Andric // NOTE: We cannot throw non-fatal errors from warnIfNilExpr, 160*0b57cec5SDimitry Andric // because it's called multiple times from some callers, so it'd cause 161*0b57cec5SDimitry Andric // an unwanted state split if two or more non-fatal errors are thrown 162*0b57cec5SDimitry Andric // within the same checker callback. For now we don't want to, but 163*0b57cec5SDimitry Andric // it'll need to be fixed if we ever want to. 164*0b57cec5SDimitry Andric if (ExplodedNode *N = C.generateErrorNode()) { 165*0b57cec5SDimitry Andric SmallString<128> sbuf; 166*0b57cec5SDimitry Andric llvm::raw_svector_ostream os(sbuf); 167*0b57cec5SDimitry Andric 168*0b57cec5SDimitry Andric if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) { 169*0b57cec5SDimitry Andric 170*0b57cec5SDimitry Andric if (Class == FC_NSArray) { 171*0b57cec5SDimitry Andric os << "Array element cannot be nil"; 172*0b57cec5SDimitry Andric } else if (Class == FC_NSDictionary) { 173*0b57cec5SDimitry Andric if (Arg == 0) { 174*0b57cec5SDimitry Andric os << "Value stored into '"; 175*0b57cec5SDimitry Andric os << GetReceiverInterfaceName(msg) << "' cannot be nil"; 176*0b57cec5SDimitry Andric } else { 177*0b57cec5SDimitry Andric assert(Arg == 1); 178*0b57cec5SDimitry Andric os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil"; 179*0b57cec5SDimitry Andric } 180*0b57cec5SDimitry Andric } else 181*0b57cec5SDimitry Andric llvm_unreachable("Missing foundation class for the subscript expr"); 182*0b57cec5SDimitry Andric 183*0b57cec5SDimitry Andric } else { 184*0b57cec5SDimitry Andric if (Class == FC_NSDictionary) { 185*0b57cec5SDimitry Andric if (Arg == 0) 186*0b57cec5SDimitry Andric os << "Value argument "; 187*0b57cec5SDimitry Andric else { 188*0b57cec5SDimitry Andric assert(Arg == 1); 189*0b57cec5SDimitry Andric os << "Key argument "; 190*0b57cec5SDimitry Andric } 191*0b57cec5SDimitry Andric os << "to '"; 192*0b57cec5SDimitry Andric msg.getSelector().print(os); 193*0b57cec5SDimitry Andric os << "' cannot be nil"; 194*0b57cec5SDimitry Andric } else { 195*0b57cec5SDimitry Andric os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"; 196*0b57cec5SDimitry Andric msg.getSelector().print(os); 197*0b57cec5SDimitry Andric os << "' cannot be nil"; 198*0b57cec5SDimitry Andric } 199*0b57cec5SDimitry Andric } 200*0b57cec5SDimitry Andric 201*0b57cec5SDimitry Andric generateBugReport(N, os.str(), msg.getArgSourceRange(Arg), 202*0b57cec5SDimitry Andric msg.getArgExpr(Arg), C); 203*0b57cec5SDimitry Andric } 204*0b57cec5SDimitry Andric } 205*0b57cec5SDimitry Andric 206*0b57cec5SDimitry Andric void NilArgChecker::generateBugReport(ExplodedNode *N, 207*0b57cec5SDimitry Andric StringRef Msg, 208*0b57cec5SDimitry Andric SourceRange Range, 209*0b57cec5SDimitry Andric const Expr *E, 210*0b57cec5SDimitry Andric CheckerContext &C) const { 211*0b57cec5SDimitry Andric if (!BT) 212*0b57cec5SDimitry Andric BT.reset(new APIMisuse(this, "nil argument")); 213*0b57cec5SDimitry Andric 214*0b57cec5SDimitry Andric auto R = llvm::make_unique<BugReport>(*BT, Msg, N); 215*0b57cec5SDimitry Andric R->addRange(Range); 216*0b57cec5SDimitry Andric bugreporter::trackExpressionValue(N, E, *R); 217*0b57cec5SDimitry Andric C.emitReport(std::move(R)); 218*0b57cec5SDimitry Andric } 219*0b57cec5SDimitry Andric 220*0b57cec5SDimitry Andric void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 221*0b57cec5SDimitry Andric CheckerContext &C) const { 222*0b57cec5SDimitry Andric const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); 223*0b57cec5SDimitry Andric if (!ID) 224*0b57cec5SDimitry Andric return; 225*0b57cec5SDimitry Andric 226*0b57cec5SDimitry Andric FoundationClass Class = findKnownClass(ID); 227*0b57cec5SDimitry Andric 228*0b57cec5SDimitry Andric static const unsigned InvalidArgIndex = UINT_MAX; 229*0b57cec5SDimitry Andric unsigned Arg = InvalidArgIndex; 230*0b57cec5SDimitry Andric bool CanBeSubscript = false; 231*0b57cec5SDimitry Andric 232*0b57cec5SDimitry Andric if (Class == FC_NSString) { 233*0b57cec5SDimitry Andric Selector S = msg.getSelector(); 234*0b57cec5SDimitry Andric 235*0b57cec5SDimitry Andric if (S.isUnarySelector()) 236*0b57cec5SDimitry Andric return; 237*0b57cec5SDimitry Andric 238*0b57cec5SDimitry Andric if (StringSelectors.empty()) { 239*0b57cec5SDimitry Andric ASTContext &Ctx = C.getASTContext(); 240*0b57cec5SDimitry Andric Selector Sels[] = { 241*0b57cec5SDimitry Andric getKeywordSelector(Ctx, "caseInsensitiveCompare"), 242*0b57cec5SDimitry Andric getKeywordSelector(Ctx, "compare"), 243*0b57cec5SDimitry Andric getKeywordSelector(Ctx, "compare", "options"), 244*0b57cec5SDimitry Andric getKeywordSelector(Ctx, "compare", "options", "range"), 245*0b57cec5SDimitry Andric getKeywordSelector(Ctx, "compare", "options", "range", "locale"), 246*0b57cec5SDimitry Andric getKeywordSelector(Ctx, "componentsSeparatedByCharactersInSet"), 247*0b57cec5SDimitry Andric getKeywordSelector(Ctx, "initWithFormat"), 248*0b57cec5SDimitry Andric getKeywordSelector(Ctx, "localizedCaseInsensitiveCompare"), 249*0b57cec5SDimitry Andric getKeywordSelector(Ctx, "localizedCompare"), 250*0b57cec5SDimitry Andric getKeywordSelector(Ctx, "localizedStandardCompare"), 251*0b57cec5SDimitry Andric }; 252*0b57cec5SDimitry Andric for (Selector KnownSel : Sels) 253*0b57cec5SDimitry Andric StringSelectors[KnownSel] = 0; 254*0b57cec5SDimitry Andric } 255*0b57cec5SDimitry Andric auto I = StringSelectors.find(S); 256*0b57cec5SDimitry Andric if (I == StringSelectors.end()) 257*0b57cec5SDimitry Andric return; 258*0b57cec5SDimitry Andric Arg = I->second; 259*0b57cec5SDimitry Andric } else if (Class == FC_NSArray) { 260*0b57cec5SDimitry Andric Selector S = msg.getSelector(); 261*0b57cec5SDimitry Andric 262*0b57cec5SDimitry Andric if (S.isUnarySelector()) 263*0b57cec5SDimitry Andric return; 264*0b57cec5SDimitry Andric 265*0b57cec5SDimitry Andric if (ArrayWithObjectSel.isNull()) { 266*0b57cec5SDimitry Andric ASTContext &Ctx = C.getASTContext(); 267*0b57cec5SDimitry Andric ArrayWithObjectSel = getKeywordSelector(Ctx, "arrayWithObject"); 268*0b57cec5SDimitry Andric AddObjectSel = getKeywordSelector(Ctx, "addObject"); 269*0b57cec5SDimitry Andric InsertObjectAtIndexSel = 270*0b57cec5SDimitry Andric getKeywordSelector(Ctx, "insertObject", "atIndex"); 271*0b57cec5SDimitry Andric ReplaceObjectAtIndexWithObjectSel = 272*0b57cec5SDimitry Andric getKeywordSelector(Ctx, "replaceObjectAtIndex", "withObject"); 273*0b57cec5SDimitry Andric SetObjectAtIndexedSubscriptSel = 274*0b57cec5SDimitry Andric getKeywordSelector(Ctx, "setObject", "atIndexedSubscript"); 275*0b57cec5SDimitry Andric ArrayByAddingObjectSel = getKeywordSelector(Ctx, "arrayByAddingObject"); 276*0b57cec5SDimitry Andric } 277*0b57cec5SDimitry Andric 278*0b57cec5SDimitry Andric if (S == ArrayWithObjectSel || S == AddObjectSel || 279*0b57cec5SDimitry Andric S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) { 280*0b57cec5SDimitry Andric Arg = 0; 281*0b57cec5SDimitry Andric } else if (S == SetObjectAtIndexedSubscriptSel) { 282*0b57cec5SDimitry Andric Arg = 0; 283*0b57cec5SDimitry Andric CanBeSubscript = true; 284*0b57cec5SDimitry Andric } else if (S == ReplaceObjectAtIndexWithObjectSel) { 285*0b57cec5SDimitry Andric Arg = 1; 286*0b57cec5SDimitry Andric } 287*0b57cec5SDimitry Andric } else if (Class == FC_NSDictionary) { 288*0b57cec5SDimitry Andric Selector S = msg.getSelector(); 289*0b57cec5SDimitry Andric 290*0b57cec5SDimitry Andric if (S.isUnarySelector()) 291*0b57cec5SDimitry Andric return; 292*0b57cec5SDimitry Andric 293*0b57cec5SDimitry Andric if (DictionaryWithObjectForKeySel.isNull()) { 294*0b57cec5SDimitry Andric ASTContext &Ctx = C.getASTContext(); 295*0b57cec5SDimitry Andric DictionaryWithObjectForKeySel = 296*0b57cec5SDimitry Andric getKeywordSelector(Ctx, "dictionaryWithObject", "forKey"); 297*0b57cec5SDimitry Andric SetObjectForKeySel = getKeywordSelector(Ctx, "setObject", "forKey"); 298*0b57cec5SDimitry Andric SetObjectForKeyedSubscriptSel = 299*0b57cec5SDimitry Andric getKeywordSelector(Ctx, "setObject", "forKeyedSubscript"); 300*0b57cec5SDimitry Andric RemoveObjectForKeySel = getKeywordSelector(Ctx, "removeObjectForKey"); 301*0b57cec5SDimitry Andric } 302*0b57cec5SDimitry Andric 303*0b57cec5SDimitry Andric if (S == DictionaryWithObjectForKeySel || S == SetObjectForKeySel) { 304*0b57cec5SDimitry Andric Arg = 0; 305*0b57cec5SDimitry Andric warnIfNilArg(C, msg, /* Arg */1, Class); 306*0b57cec5SDimitry Andric } else if (S == SetObjectForKeyedSubscriptSel) { 307*0b57cec5SDimitry Andric CanBeSubscript = true; 308*0b57cec5SDimitry Andric Arg = 1; 309*0b57cec5SDimitry Andric } else if (S == RemoveObjectForKeySel) { 310*0b57cec5SDimitry Andric Arg = 0; 311*0b57cec5SDimitry Andric } 312*0b57cec5SDimitry Andric } 313*0b57cec5SDimitry Andric 314*0b57cec5SDimitry Andric // If argument is '0', report a warning. 315*0b57cec5SDimitry Andric if ((Arg != InvalidArgIndex)) 316*0b57cec5SDimitry Andric warnIfNilArg(C, msg, Arg, Class, CanBeSubscript); 317*0b57cec5SDimitry Andric } 318*0b57cec5SDimitry Andric 319*0b57cec5SDimitry Andric void NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL, 320*0b57cec5SDimitry Andric CheckerContext &C) const { 321*0b57cec5SDimitry Andric unsigned NumOfElements = AL->getNumElements(); 322*0b57cec5SDimitry Andric for (unsigned i = 0; i < NumOfElements; ++i) { 323*0b57cec5SDimitry Andric warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C); 324*0b57cec5SDimitry Andric } 325*0b57cec5SDimitry Andric } 326*0b57cec5SDimitry Andric 327*0b57cec5SDimitry Andric void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL, 328*0b57cec5SDimitry Andric CheckerContext &C) const { 329*0b57cec5SDimitry Andric unsigned NumOfElements = DL->getNumElements(); 330*0b57cec5SDimitry Andric for (unsigned i = 0; i < NumOfElements; ++i) { 331*0b57cec5SDimitry Andric ObjCDictionaryElement Element = DL->getKeyValueElement(i); 332*0b57cec5SDimitry Andric warnIfNilExpr(Element.Key, "Dictionary key cannot be nil", C); 333*0b57cec5SDimitry Andric warnIfNilExpr(Element.Value, "Dictionary value cannot be nil", C); 334*0b57cec5SDimitry Andric } 335*0b57cec5SDimitry Andric } 336*0b57cec5SDimitry Andric 337*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 338*0b57cec5SDimitry Andric // Checking for mismatched types passed to CFNumberCreate/CFNumberGetValue. 339*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 340*0b57cec5SDimitry Andric 341*0b57cec5SDimitry Andric namespace { 342*0b57cec5SDimitry Andric class CFNumberChecker : public Checker< check::PreStmt<CallExpr> > { 343*0b57cec5SDimitry Andric mutable std::unique_ptr<APIMisuse> BT; 344*0b57cec5SDimitry Andric mutable IdentifierInfo *ICreate, *IGetValue; 345*0b57cec5SDimitry Andric public: 346*0b57cec5SDimitry Andric CFNumberChecker() : ICreate(nullptr), IGetValue(nullptr) {} 347*0b57cec5SDimitry Andric 348*0b57cec5SDimitry Andric void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 349*0b57cec5SDimitry Andric 350*0b57cec5SDimitry Andric private: 351*0b57cec5SDimitry Andric void EmitError(const TypedRegion* R, const Expr *Ex, 352*0b57cec5SDimitry Andric uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); 353*0b57cec5SDimitry Andric }; 354*0b57cec5SDimitry Andric } // end anonymous namespace 355*0b57cec5SDimitry Andric 356*0b57cec5SDimitry Andric enum CFNumberType { 357*0b57cec5SDimitry Andric kCFNumberSInt8Type = 1, 358*0b57cec5SDimitry Andric kCFNumberSInt16Type = 2, 359*0b57cec5SDimitry Andric kCFNumberSInt32Type = 3, 360*0b57cec5SDimitry Andric kCFNumberSInt64Type = 4, 361*0b57cec5SDimitry Andric kCFNumberFloat32Type = 5, 362*0b57cec5SDimitry Andric kCFNumberFloat64Type = 6, 363*0b57cec5SDimitry Andric kCFNumberCharType = 7, 364*0b57cec5SDimitry Andric kCFNumberShortType = 8, 365*0b57cec5SDimitry Andric kCFNumberIntType = 9, 366*0b57cec5SDimitry Andric kCFNumberLongType = 10, 367*0b57cec5SDimitry Andric kCFNumberLongLongType = 11, 368*0b57cec5SDimitry Andric kCFNumberFloatType = 12, 369*0b57cec5SDimitry Andric kCFNumberDoubleType = 13, 370*0b57cec5SDimitry Andric kCFNumberCFIndexType = 14, 371*0b57cec5SDimitry Andric kCFNumberNSIntegerType = 15, 372*0b57cec5SDimitry Andric kCFNumberCGFloatType = 16 373*0b57cec5SDimitry Andric }; 374*0b57cec5SDimitry Andric 375*0b57cec5SDimitry Andric static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { 376*0b57cec5SDimitry Andric static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; 377*0b57cec5SDimitry Andric 378*0b57cec5SDimitry Andric if (i < kCFNumberCharType) 379*0b57cec5SDimitry Andric return FixedSize[i-1]; 380*0b57cec5SDimitry Andric 381*0b57cec5SDimitry Andric QualType T; 382*0b57cec5SDimitry Andric 383*0b57cec5SDimitry Andric switch (i) { 384*0b57cec5SDimitry Andric case kCFNumberCharType: T = Ctx.CharTy; break; 385*0b57cec5SDimitry Andric case kCFNumberShortType: T = Ctx.ShortTy; break; 386*0b57cec5SDimitry Andric case kCFNumberIntType: T = Ctx.IntTy; break; 387*0b57cec5SDimitry Andric case kCFNumberLongType: T = Ctx.LongTy; break; 388*0b57cec5SDimitry Andric case kCFNumberLongLongType: T = Ctx.LongLongTy; break; 389*0b57cec5SDimitry Andric case kCFNumberFloatType: T = Ctx.FloatTy; break; 390*0b57cec5SDimitry Andric case kCFNumberDoubleType: T = Ctx.DoubleTy; break; 391*0b57cec5SDimitry Andric case kCFNumberCFIndexType: 392*0b57cec5SDimitry Andric case kCFNumberNSIntegerType: 393*0b57cec5SDimitry Andric case kCFNumberCGFloatType: 394*0b57cec5SDimitry Andric // FIXME: We need a way to map from names to Type*. 395*0b57cec5SDimitry Andric default: 396*0b57cec5SDimitry Andric return None; 397*0b57cec5SDimitry Andric } 398*0b57cec5SDimitry Andric 399*0b57cec5SDimitry Andric return Ctx.getTypeSize(T); 400*0b57cec5SDimitry Andric } 401*0b57cec5SDimitry Andric 402*0b57cec5SDimitry Andric #if 0 403*0b57cec5SDimitry Andric static const char* GetCFNumberTypeStr(uint64_t i) { 404*0b57cec5SDimitry Andric static const char* Names[] = { 405*0b57cec5SDimitry Andric "kCFNumberSInt8Type", 406*0b57cec5SDimitry Andric "kCFNumberSInt16Type", 407*0b57cec5SDimitry Andric "kCFNumberSInt32Type", 408*0b57cec5SDimitry Andric "kCFNumberSInt64Type", 409*0b57cec5SDimitry Andric "kCFNumberFloat32Type", 410*0b57cec5SDimitry Andric "kCFNumberFloat64Type", 411*0b57cec5SDimitry Andric "kCFNumberCharType", 412*0b57cec5SDimitry Andric "kCFNumberShortType", 413*0b57cec5SDimitry Andric "kCFNumberIntType", 414*0b57cec5SDimitry Andric "kCFNumberLongType", 415*0b57cec5SDimitry Andric "kCFNumberLongLongType", 416*0b57cec5SDimitry Andric "kCFNumberFloatType", 417*0b57cec5SDimitry Andric "kCFNumberDoubleType", 418*0b57cec5SDimitry Andric "kCFNumberCFIndexType", 419*0b57cec5SDimitry Andric "kCFNumberNSIntegerType", 420*0b57cec5SDimitry Andric "kCFNumberCGFloatType" 421*0b57cec5SDimitry Andric }; 422*0b57cec5SDimitry Andric 423*0b57cec5SDimitry Andric return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType"; 424*0b57cec5SDimitry Andric } 425*0b57cec5SDimitry Andric #endif 426*0b57cec5SDimitry Andric 427*0b57cec5SDimitry Andric void CFNumberChecker::checkPreStmt(const CallExpr *CE, 428*0b57cec5SDimitry Andric CheckerContext &C) const { 429*0b57cec5SDimitry Andric ProgramStateRef state = C.getState(); 430*0b57cec5SDimitry Andric const FunctionDecl *FD = C.getCalleeDecl(CE); 431*0b57cec5SDimitry Andric if (!FD) 432*0b57cec5SDimitry Andric return; 433*0b57cec5SDimitry Andric 434*0b57cec5SDimitry Andric ASTContext &Ctx = C.getASTContext(); 435*0b57cec5SDimitry Andric if (!ICreate) { 436*0b57cec5SDimitry Andric ICreate = &Ctx.Idents.get("CFNumberCreate"); 437*0b57cec5SDimitry Andric IGetValue = &Ctx.Idents.get("CFNumberGetValue"); 438*0b57cec5SDimitry Andric } 439*0b57cec5SDimitry Andric if (!(FD->getIdentifier() == ICreate || FD->getIdentifier() == IGetValue) || 440*0b57cec5SDimitry Andric CE->getNumArgs() != 3) 441*0b57cec5SDimitry Andric return; 442*0b57cec5SDimitry Andric 443*0b57cec5SDimitry Andric // Get the value of the "theType" argument. 444*0b57cec5SDimitry Andric SVal TheTypeVal = C.getSVal(CE->getArg(1)); 445*0b57cec5SDimitry Andric 446*0b57cec5SDimitry Andric // FIXME: We really should allow ranges of valid theType values, and 447*0b57cec5SDimitry Andric // bifurcate the state appropriately. 448*0b57cec5SDimitry Andric Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>(); 449*0b57cec5SDimitry Andric if (!V) 450*0b57cec5SDimitry Andric return; 451*0b57cec5SDimitry Andric 452*0b57cec5SDimitry Andric uint64_t NumberKind = V->getValue().getLimitedValue(); 453*0b57cec5SDimitry Andric Optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind); 454*0b57cec5SDimitry Andric 455*0b57cec5SDimitry Andric // FIXME: In some cases we can emit an error. 456*0b57cec5SDimitry Andric if (!OptCFNumberSize) 457*0b57cec5SDimitry Andric return; 458*0b57cec5SDimitry Andric 459*0b57cec5SDimitry Andric uint64_t CFNumberSize = *OptCFNumberSize; 460*0b57cec5SDimitry Andric 461*0b57cec5SDimitry Andric // Look at the value of the integer being passed by reference. Essentially 462*0b57cec5SDimitry Andric // we want to catch cases where the value passed in is not equal to the 463*0b57cec5SDimitry Andric // size of the type being created. 464*0b57cec5SDimitry Andric SVal TheValueExpr = C.getSVal(CE->getArg(2)); 465*0b57cec5SDimitry Andric 466*0b57cec5SDimitry Andric // FIXME: Eventually we should handle arbitrary locations. We can do this 467*0b57cec5SDimitry Andric // by having an enhanced memory model that does low-level typing. 468*0b57cec5SDimitry Andric Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>(); 469*0b57cec5SDimitry Andric if (!LV) 470*0b57cec5SDimitry Andric return; 471*0b57cec5SDimitry Andric 472*0b57cec5SDimitry Andric const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts()); 473*0b57cec5SDimitry Andric if (!R) 474*0b57cec5SDimitry Andric return; 475*0b57cec5SDimitry Andric 476*0b57cec5SDimitry Andric QualType T = Ctx.getCanonicalType(R->getValueType()); 477*0b57cec5SDimitry Andric 478*0b57cec5SDimitry Andric // FIXME: If the pointee isn't an integer type, should we flag a warning? 479*0b57cec5SDimitry Andric // People can do weird stuff with pointers. 480*0b57cec5SDimitry Andric 481*0b57cec5SDimitry Andric if (!T->isIntegralOrEnumerationType()) 482*0b57cec5SDimitry Andric return; 483*0b57cec5SDimitry Andric 484*0b57cec5SDimitry Andric uint64_t PrimitiveTypeSize = Ctx.getTypeSize(T); 485*0b57cec5SDimitry Andric 486*0b57cec5SDimitry Andric if (PrimitiveTypeSize == CFNumberSize) 487*0b57cec5SDimitry Andric return; 488*0b57cec5SDimitry Andric 489*0b57cec5SDimitry Andric // FIXME: We can actually create an abstract "CFNumber" object that has 490*0b57cec5SDimitry Andric // the bits initialized to the provided values. 491*0b57cec5SDimitry Andric ExplodedNode *N = C.generateNonFatalErrorNode(); 492*0b57cec5SDimitry Andric if (N) { 493*0b57cec5SDimitry Andric SmallString<128> sbuf; 494*0b57cec5SDimitry Andric llvm::raw_svector_ostream os(sbuf); 495*0b57cec5SDimitry Andric bool isCreate = (FD->getIdentifier() == ICreate); 496*0b57cec5SDimitry Andric 497*0b57cec5SDimitry Andric if (isCreate) { 498*0b57cec5SDimitry Andric os << (PrimitiveTypeSize == 8 ? "An " : "A ") 499*0b57cec5SDimitry Andric << PrimitiveTypeSize << "-bit integer is used to initialize a " 500*0b57cec5SDimitry Andric << "CFNumber object that represents " 501*0b57cec5SDimitry Andric << (CFNumberSize == 8 ? "an " : "a ") 502*0b57cec5SDimitry Andric << CFNumberSize << "-bit integer; "; 503*0b57cec5SDimitry Andric } else { 504*0b57cec5SDimitry Andric os << "A CFNumber object that represents " 505*0b57cec5SDimitry Andric << (CFNumberSize == 8 ? "an " : "a ") 506*0b57cec5SDimitry Andric << CFNumberSize << "-bit integer is used to initialize " 507*0b57cec5SDimitry Andric << (PrimitiveTypeSize == 8 ? "an " : "a ") 508*0b57cec5SDimitry Andric << PrimitiveTypeSize << "-bit integer; "; 509*0b57cec5SDimitry Andric } 510*0b57cec5SDimitry Andric 511*0b57cec5SDimitry Andric if (PrimitiveTypeSize < CFNumberSize) 512*0b57cec5SDimitry Andric os << (CFNumberSize - PrimitiveTypeSize) 513*0b57cec5SDimitry Andric << " bits of the CFNumber value will " 514*0b57cec5SDimitry Andric << (isCreate ? "be garbage." : "overwrite adjacent storage."); 515*0b57cec5SDimitry Andric else 516*0b57cec5SDimitry Andric os << (PrimitiveTypeSize - CFNumberSize) 517*0b57cec5SDimitry Andric << " bits of the integer value will be " 518*0b57cec5SDimitry Andric << (isCreate ? "lost." : "garbage."); 519*0b57cec5SDimitry Andric 520*0b57cec5SDimitry Andric if (!BT) 521*0b57cec5SDimitry Andric BT.reset(new APIMisuse(this, "Bad use of CFNumber APIs")); 522*0b57cec5SDimitry Andric 523*0b57cec5SDimitry Andric auto report = llvm::make_unique<BugReport>(*BT, os.str(), N); 524*0b57cec5SDimitry Andric report->addRange(CE->getArg(2)->getSourceRange()); 525*0b57cec5SDimitry Andric C.emitReport(std::move(report)); 526*0b57cec5SDimitry Andric } 527*0b57cec5SDimitry Andric } 528*0b57cec5SDimitry Andric 529*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 530*0b57cec5SDimitry Andric // CFRetain/CFRelease/CFMakeCollectable/CFAutorelease checking for null arguments. 531*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 532*0b57cec5SDimitry Andric 533*0b57cec5SDimitry Andric namespace { 534*0b57cec5SDimitry Andric class CFRetainReleaseChecker : public Checker<check::PreCall> { 535*0b57cec5SDimitry Andric mutable APIMisuse BT{this, "null passed to CF memory management function"}; 536*0b57cec5SDimitry Andric CallDescription CFRetain{"CFRetain", 1}, 537*0b57cec5SDimitry Andric CFRelease{"CFRelease", 1}, 538*0b57cec5SDimitry Andric CFMakeCollectable{"CFMakeCollectable", 1}, 539*0b57cec5SDimitry Andric CFAutorelease{"CFAutorelease", 1}; 540*0b57cec5SDimitry Andric 541*0b57cec5SDimitry Andric public: 542*0b57cec5SDimitry Andric void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 543*0b57cec5SDimitry Andric }; 544*0b57cec5SDimitry Andric } // end anonymous namespace 545*0b57cec5SDimitry Andric 546*0b57cec5SDimitry Andric void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call, 547*0b57cec5SDimitry Andric CheckerContext &C) const { 548*0b57cec5SDimitry Andric // TODO: Make this check part of CallDescription. 549*0b57cec5SDimitry Andric if (!Call.isGlobalCFunction()) 550*0b57cec5SDimitry Andric return; 551*0b57cec5SDimitry Andric 552*0b57cec5SDimitry Andric // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease. 553*0b57cec5SDimitry Andric if (!(Call.isCalled(CFRetain) || Call.isCalled(CFRelease) || 554*0b57cec5SDimitry Andric Call.isCalled(CFMakeCollectable) || Call.isCalled(CFAutorelease))) 555*0b57cec5SDimitry Andric return; 556*0b57cec5SDimitry Andric 557*0b57cec5SDimitry Andric // Get the argument's value. 558*0b57cec5SDimitry Andric SVal ArgVal = Call.getArgSVal(0); 559*0b57cec5SDimitry Andric Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); 560*0b57cec5SDimitry Andric if (!DefArgVal) 561*0b57cec5SDimitry Andric return; 562*0b57cec5SDimitry Andric 563*0b57cec5SDimitry Andric // Is it null? 564*0b57cec5SDimitry Andric ProgramStateRef state = C.getState(); 565*0b57cec5SDimitry Andric ProgramStateRef stateNonNull, stateNull; 566*0b57cec5SDimitry Andric std::tie(stateNonNull, stateNull) = state->assume(*DefArgVal); 567*0b57cec5SDimitry Andric 568*0b57cec5SDimitry Andric if (!stateNonNull) { 569*0b57cec5SDimitry Andric ExplodedNode *N = C.generateErrorNode(stateNull); 570*0b57cec5SDimitry Andric if (!N) 571*0b57cec5SDimitry Andric return; 572*0b57cec5SDimitry Andric 573*0b57cec5SDimitry Andric SmallString<64> Str; 574*0b57cec5SDimitry Andric raw_svector_ostream OS(Str); 575*0b57cec5SDimitry Andric OS << "Null pointer argument in call to " 576*0b57cec5SDimitry Andric << cast<FunctionDecl>(Call.getDecl())->getName(); 577*0b57cec5SDimitry Andric 578*0b57cec5SDimitry Andric auto report = llvm::make_unique<BugReport>(BT, OS.str(), N); 579*0b57cec5SDimitry Andric report->addRange(Call.getArgSourceRange(0)); 580*0b57cec5SDimitry Andric bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *report); 581*0b57cec5SDimitry Andric C.emitReport(std::move(report)); 582*0b57cec5SDimitry Andric return; 583*0b57cec5SDimitry Andric } 584*0b57cec5SDimitry Andric 585*0b57cec5SDimitry Andric // From here on, we know the argument is non-null. 586*0b57cec5SDimitry Andric C.addTransition(stateNonNull); 587*0b57cec5SDimitry Andric } 588*0b57cec5SDimitry Andric 589*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 590*0b57cec5SDimitry Andric // Check for sending 'retain', 'release', or 'autorelease' directly to a Class. 591*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 592*0b57cec5SDimitry Andric 593*0b57cec5SDimitry Andric namespace { 594*0b57cec5SDimitry Andric class ClassReleaseChecker : public Checker<check::PreObjCMessage> { 595*0b57cec5SDimitry Andric mutable Selector releaseS; 596*0b57cec5SDimitry Andric mutable Selector retainS; 597*0b57cec5SDimitry Andric mutable Selector autoreleaseS; 598*0b57cec5SDimitry Andric mutable Selector drainS; 599*0b57cec5SDimitry Andric mutable std::unique_ptr<BugType> BT; 600*0b57cec5SDimitry Andric 601*0b57cec5SDimitry Andric public: 602*0b57cec5SDimitry Andric void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 603*0b57cec5SDimitry Andric }; 604*0b57cec5SDimitry Andric } // end anonymous namespace 605*0b57cec5SDimitry Andric 606*0b57cec5SDimitry Andric void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 607*0b57cec5SDimitry Andric CheckerContext &C) const { 608*0b57cec5SDimitry Andric if (!BT) { 609*0b57cec5SDimitry Andric BT.reset(new APIMisuse( 610*0b57cec5SDimitry Andric this, "message incorrectly sent to class instead of class instance")); 611*0b57cec5SDimitry Andric 612*0b57cec5SDimitry Andric ASTContext &Ctx = C.getASTContext(); 613*0b57cec5SDimitry Andric releaseS = GetNullarySelector("release", Ctx); 614*0b57cec5SDimitry Andric retainS = GetNullarySelector("retain", Ctx); 615*0b57cec5SDimitry Andric autoreleaseS = GetNullarySelector("autorelease", Ctx); 616*0b57cec5SDimitry Andric drainS = GetNullarySelector("drain", Ctx); 617*0b57cec5SDimitry Andric } 618*0b57cec5SDimitry Andric 619*0b57cec5SDimitry Andric if (msg.isInstanceMessage()) 620*0b57cec5SDimitry Andric return; 621*0b57cec5SDimitry Andric const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 622*0b57cec5SDimitry Andric assert(Class); 623*0b57cec5SDimitry Andric 624*0b57cec5SDimitry Andric Selector S = msg.getSelector(); 625*0b57cec5SDimitry Andric if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) 626*0b57cec5SDimitry Andric return; 627*0b57cec5SDimitry Andric 628*0b57cec5SDimitry Andric if (ExplodedNode *N = C.generateNonFatalErrorNode()) { 629*0b57cec5SDimitry Andric SmallString<200> buf; 630*0b57cec5SDimitry Andric llvm::raw_svector_ostream os(buf); 631*0b57cec5SDimitry Andric 632*0b57cec5SDimitry Andric os << "The '"; 633*0b57cec5SDimitry Andric S.print(os); 634*0b57cec5SDimitry Andric os << "' message should be sent to instances " 635*0b57cec5SDimitry Andric "of class '" << Class->getName() 636*0b57cec5SDimitry Andric << "' and not the class directly"; 637*0b57cec5SDimitry Andric 638*0b57cec5SDimitry Andric auto report = llvm::make_unique<BugReport>(*BT, os.str(), N); 639*0b57cec5SDimitry Andric report->addRange(msg.getSourceRange()); 640*0b57cec5SDimitry Andric C.emitReport(std::move(report)); 641*0b57cec5SDimitry Andric } 642*0b57cec5SDimitry Andric } 643*0b57cec5SDimitry Andric 644*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 645*0b57cec5SDimitry Andric // Check for passing non-Objective-C types to variadic methods that expect 646*0b57cec5SDimitry Andric // only Objective-C types. 647*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 648*0b57cec5SDimitry Andric 649*0b57cec5SDimitry Andric namespace { 650*0b57cec5SDimitry Andric class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> { 651*0b57cec5SDimitry Andric mutable Selector arrayWithObjectsS; 652*0b57cec5SDimitry Andric mutable Selector dictionaryWithObjectsAndKeysS; 653*0b57cec5SDimitry Andric mutable Selector setWithObjectsS; 654*0b57cec5SDimitry Andric mutable Selector orderedSetWithObjectsS; 655*0b57cec5SDimitry Andric mutable Selector initWithObjectsS; 656*0b57cec5SDimitry Andric mutable Selector initWithObjectsAndKeysS; 657*0b57cec5SDimitry Andric mutable std::unique_ptr<BugType> BT; 658*0b57cec5SDimitry Andric 659*0b57cec5SDimitry Andric bool isVariadicMessage(const ObjCMethodCall &msg) const; 660*0b57cec5SDimitry Andric 661*0b57cec5SDimitry Andric public: 662*0b57cec5SDimitry Andric void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 663*0b57cec5SDimitry Andric }; 664*0b57cec5SDimitry Andric } // end anonymous namespace 665*0b57cec5SDimitry Andric 666*0b57cec5SDimitry Andric /// isVariadicMessage - Returns whether the given message is a variadic message, 667*0b57cec5SDimitry Andric /// where all arguments must be Objective-C types. 668*0b57cec5SDimitry Andric bool 669*0b57cec5SDimitry Andric VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const { 670*0b57cec5SDimitry Andric const ObjCMethodDecl *MD = msg.getDecl(); 671*0b57cec5SDimitry Andric 672*0b57cec5SDimitry Andric if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext())) 673*0b57cec5SDimitry Andric return false; 674*0b57cec5SDimitry Andric 675*0b57cec5SDimitry Andric Selector S = msg.getSelector(); 676*0b57cec5SDimitry Andric 677*0b57cec5SDimitry Andric if (msg.isInstanceMessage()) { 678*0b57cec5SDimitry Andric // FIXME: Ideally we'd look at the receiver interface here, but that's not 679*0b57cec5SDimitry Andric // useful for init, because alloc returns 'id'. In theory, this could lead 680*0b57cec5SDimitry Andric // to false positives, for example if there existed a class that had an 681*0b57cec5SDimitry Andric // initWithObjects: implementation that does accept non-Objective-C pointer 682*0b57cec5SDimitry Andric // types, but the chance of that happening is pretty small compared to the 683*0b57cec5SDimitry Andric // gains that this analysis gives. 684*0b57cec5SDimitry Andric const ObjCInterfaceDecl *Class = MD->getClassInterface(); 685*0b57cec5SDimitry Andric 686*0b57cec5SDimitry Andric switch (findKnownClass(Class)) { 687*0b57cec5SDimitry Andric case FC_NSArray: 688*0b57cec5SDimitry Andric case FC_NSOrderedSet: 689*0b57cec5SDimitry Andric case FC_NSSet: 690*0b57cec5SDimitry Andric return S == initWithObjectsS; 691*0b57cec5SDimitry Andric case FC_NSDictionary: 692*0b57cec5SDimitry Andric return S == initWithObjectsAndKeysS; 693*0b57cec5SDimitry Andric default: 694*0b57cec5SDimitry Andric return false; 695*0b57cec5SDimitry Andric } 696*0b57cec5SDimitry Andric } else { 697*0b57cec5SDimitry Andric const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 698*0b57cec5SDimitry Andric 699*0b57cec5SDimitry Andric switch (findKnownClass(Class)) { 700*0b57cec5SDimitry Andric case FC_NSArray: 701*0b57cec5SDimitry Andric return S == arrayWithObjectsS; 702*0b57cec5SDimitry Andric case FC_NSOrderedSet: 703*0b57cec5SDimitry Andric return S == orderedSetWithObjectsS; 704*0b57cec5SDimitry Andric case FC_NSSet: 705*0b57cec5SDimitry Andric return S == setWithObjectsS; 706*0b57cec5SDimitry Andric case FC_NSDictionary: 707*0b57cec5SDimitry Andric return S == dictionaryWithObjectsAndKeysS; 708*0b57cec5SDimitry Andric default: 709*0b57cec5SDimitry Andric return false; 710*0b57cec5SDimitry Andric } 711*0b57cec5SDimitry Andric } 712*0b57cec5SDimitry Andric } 713*0b57cec5SDimitry Andric 714*0b57cec5SDimitry Andric void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 715*0b57cec5SDimitry Andric CheckerContext &C) const { 716*0b57cec5SDimitry Andric if (!BT) { 717*0b57cec5SDimitry Andric BT.reset(new APIMisuse(this, 718*0b57cec5SDimitry Andric "Arguments passed to variadic method aren't all " 719*0b57cec5SDimitry Andric "Objective-C pointer types")); 720*0b57cec5SDimitry Andric 721*0b57cec5SDimitry Andric ASTContext &Ctx = C.getASTContext(); 722*0b57cec5SDimitry Andric arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); 723*0b57cec5SDimitry Andric dictionaryWithObjectsAndKeysS = 724*0b57cec5SDimitry Andric GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); 725*0b57cec5SDimitry Andric setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); 726*0b57cec5SDimitry Andric orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx); 727*0b57cec5SDimitry Andric 728*0b57cec5SDimitry Andric initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); 729*0b57cec5SDimitry Andric initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); 730*0b57cec5SDimitry Andric } 731*0b57cec5SDimitry Andric 732*0b57cec5SDimitry Andric if (!isVariadicMessage(msg)) 733*0b57cec5SDimitry Andric return; 734*0b57cec5SDimitry Andric 735*0b57cec5SDimitry Andric // We are not interested in the selector arguments since they have 736*0b57cec5SDimitry Andric // well-defined types, so the compiler will issue a warning for them. 737*0b57cec5SDimitry Andric unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); 738*0b57cec5SDimitry Andric 739*0b57cec5SDimitry Andric // We're not interested in the last argument since it has to be nil or the 740*0b57cec5SDimitry Andric // compiler would have issued a warning for it elsewhere. 741*0b57cec5SDimitry Andric unsigned variadicArgsEnd = msg.getNumArgs() - 1; 742*0b57cec5SDimitry Andric 743*0b57cec5SDimitry Andric if (variadicArgsEnd <= variadicArgsBegin) 744*0b57cec5SDimitry Andric return; 745*0b57cec5SDimitry Andric 746*0b57cec5SDimitry Andric // Verify that all arguments have Objective-C types. 747*0b57cec5SDimitry Andric Optional<ExplodedNode*> errorNode; 748*0b57cec5SDimitry Andric 749*0b57cec5SDimitry Andric for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { 750*0b57cec5SDimitry Andric QualType ArgTy = msg.getArgExpr(I)->getType(); 751*0b57cec5SDimitry Andric if (ArgTy->isObjCObjectPointerType()) 752*0b57cec5SDimitry Andric continue; 753*0b57cec5SDimitry Andric 754*0b57cec5SDimitry Andric // Block pointers are treaded as Objective-C pointers. 755*0b57cec5SDimitry Andric if (ArgTy->isBlockPointerType()) 756*0b57cec5SDimitry Andric continue; 757*0b57cec5SDimitry Andric 758*0b57cec5SDimitry Andric // Ignore pointer constants. 759*0b57cec5SDimitry Andric if (msg.getArgSVal(I).getAs<loc::ConcreteInt>()) 760*0b57cec5SDimitry Andric continue; 761*0b57cec5SDimitry Andric 762*0b57cec5SDimitry Andric // Ignore pointer types annotated with 'NSObject' attribute. 763*0b57cec5SDimitry Andric if (C.getASTContext().isObjCNSObjectType(ArgTy)) 764*0b57cec5SDimitry Andric continue; 765*0b57cec5SDimitry Andric 766*0b57cec5SDimitry Andric // Ignore CF references, which can be toll-free bridged. 767*0b57cec5SDimitry Andric if (coreFoundation::isCFObjectRef(ArgTy)) 768*0b57cec5SDimitry Andric continue; 769*0b57cec5SDimitry Andric 770*0b57cec5SDimitry Andric // Generate only one error node to use for all bug reports. 771*0b57cec5SDimitry Andric if (!errorNode.hasValue()) 772*0b57cec5SDimitry Andric errorNode = C.generateNonFatalErrorNode(); 773*0b57cec5SDimitry Andric 774*0b57cec5SDimitry Andric if (!errorNode.getValue()) 775*0b57cec5SDimitry Andric continue; 776*0b57cec5SDimitry Andric 777*0b57cec5SDimitry Andric SmallString<128> sbuf; 778*0b57cec5SDimitry Andric llvm::raw_svector_ostream os(sbuf); 779*0b57cec5SDimitry Andric 780*0b57cec5SDimitry Andric StringRef TypeName = GetReceiverInterfaceName(msg); 781*0b57cec5SDimitry Andric if (!TypeName.empty()) 782*0b57cec5SDimitry Andric os << "Argument to '" << TypeName << "' method '"; 783*0b57cec5SDimitry Andric else 784*0b57cec5SDimitry Andric os << "Argument to method '"; 785*0b57cec5SDimitry Andric 786*0b57cec5SDimitry Andric msg.getSelector().print(os); 787*0b57cec5SDimitry Andric os << "' should be an Objective-C pointer type, not '"; 788*0b57cec5SDimitry Andric ArgTy.print(os, C.getLangOpts()); 789*0b57cec5SDimitry Andric os << "'"; 790*0b57cec5SDimitry Andric 791*0b57cec5SDimitry Andric auto R = llvm::make_unique<BugReport>(*BT, os.str(), errorNode.getValue()); 792*0b57cec5SDimitry Andric R->addRange(msg.getArgSourceRange(I)); 793*0b57cec5SDimitry Andric C.emitReport(std::move(R)); 794*0b57cec5SDimitry Andric } 795*0b57cec5SDimitry Andric } 796*0b57cec5SDimitry Andric 797*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 798*0b57cec5SDimitry Andric // Improves the modeling of loops over Cocoa collections. 799*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 800*0b57cec5SDimitry Andric 801*0b57cec5SDimitry Andric // The map from container symbol to the container count symbol. 802*0b57cec5SDimitry Andric // We currently will remember the last container count symbol encountered. 803*0b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap, SymbolRef, SymbolRef) 804*0b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(ContainerNonEmptyMap, SymbolRef, bool) 805*0b57cec5SDimitry Andric 806*0b57cec5SDimitry Andric namespace { 807*0b57cec5SDimitry Andric class ObjCLoopChecker 808*0b57cec5SDimitry Andric : public Checker<check::PostStmt<ObjCForCollectionStmt>, 809*0b57cec5SDimitry Andric check::PostObjCMessage, 810*0b57cec5SDimitry Andric check::DeadSymbols, 811*0b57cec5SDimitry Andric check::PointerEscape > { 812*0b57cec5SDimitry Andric mutable IdentifierInfo *CountSelectorII; 813*0b57cec5SDimitry Andric 814*0b57cec5SDimitry Andric bool isCollectionCountMethod(const ObjCMethodCall &M, 815*0b57cec5SDimitry Andric CheckerContext &C) const; 816*0b57cec5SDimitry Andric 817*0b57cec5SDimitry Andric public: 818*0b57cec5SDimitry Andric ObjCLoopChecker() : CountSelectorII(nullptr) {} 819*0b57cec5SDimitry Andric void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const; 820*0b57cec5SDimitry Andric void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 821*0b57cec5SDimitry Andric void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 822*0b57cec5SDimitry Andric ProgramStateRef checkPointerEscape(ProgramStateRef State, 823*0b57cec5SDimitry Andric const InvalidatedSymbols &Escaped, 824*0b57cec5SDimitry Andric const CallEvent *Call, 825*0b57cec5SDimitry Andric PointerEscapeKind Kind) const; 826*0b57cec5SDimitry Andric }; 827*0b57cec5SDimitry Andric } // end anonymous namespace 828*0b57cec5SDimitry Andric 829*0b57cec5SDimitry Andric static bool isKnownNonNilCollectionType(QualType T) { 830*0b57cec5SDimitry Andric const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); 831*0b57cec5SDimitry Andric if (!PT) 832*0b57cec5SDimitry Andric return false; 833*0b57cec5SDimitry Andric 834*0b57cec5SDimitry Andric const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); 835*0b57cec5SDimitry Andric if (!ID) 836*0b57cec5SDimitry Andric return false; 837*0b57cec5SDimitry Andric 838*0b57cec5SDimitry Andric switch (findKnownClass(ID)) { 839*0b57cec5SDimitry Andric case FC_NSArray: 840*0b57cec5SDimitry Andric case FC_NSDictionary: 841*0b57cec5SDimitry Andric case FC_NSEnumerator: 842*0b57cec5SDimitry Andric case FC_NSOrderedSet: 843*0b57cec5SDimitry Andric case FC_NSSet: 844*0b57cec5SDimitry Andric return true; 845*0b57cec5SDimitry Andric default: 846*0b57cec5SDimitry Andric return false; 847*0b57cec5SDimitry Andric } 848*0b57cec5SDimitry Andric } 849*0b57cec5SDimitry Andric 850*0b57cec5SDimitry Andric /// Assumes that the collection is non-nil. 851*0b57cec5SDimitry Andric /// 852*0b57cec5SDimitry Andric /// If the collection is known to be nil, returns NULL to indicate an infeasible 853*0b57cec5SDimitry Andric /// path. 854*0b57cec5SDimitry Andric static ProgramStateRef checkCollectionNonNil(CheckerContext &C, 855*0b57cec5SDimitry Andric ProgramStateRef State, 856*0b57cec5SDimitry Andric const ObjCForCollectionStmt *FCS) { 857*0b57cec5SDimitry Andric if (!State) 858*0b57cec5SDimitry Andric return nullptr; 859*0b57cec5SDimitry Andric 860*0b57cec5SDimitry Andric SVal CollectionVal = C.getSVal(FCS->getCollection()); 861*0b57cec5SDimitry Andric Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>(); 862*0b57cec5SDimitry Andric if (!KnownCollection) 863*0b57cec5SDimitry Andric return State; 864*0b57cec5SDimitry Andric 865*0b57cec5SDimitry Andric ProgramStateRef StNonNil, StNil; 866*0b57cec5SDimitry Andric std::tie(StNonNil, StNil) = State->assume(*KnownCollection); 867*0b57cec5SDimitry Andric if (StNil && !StNonNil) { 868*0b57cec5SDimitry Andric // The collection is nil. This path is infeasible. 869*0b57cec5SDimitry Andric return nullptr; 870*0b57cec5SDimitry Andric } 871*0b57cec5SDimitry Andric 872*0b57cec5SDimitry Andric return StNonNil; 873*0b57cec5SDimitry Andric } 874*0b57cec5SDimitry Andric 875*0b57cec5SDimitry Andric /// Assumes that the collection elements are non-nil. 876*0b57cec5SDimitry Andric /// 877*0b57cec5SDimitry Andric /// This only applies if the collection is one of those known not to contain 878*0b57cec5SDimitry Andric /// nil values. 879*0b57cec5SDimitry Andric static ProgramStateRef checkElementNonNil(CheckerContext &C, 880*0b57cec5SDimitry Andric ProgramStateRef State, 881*0b57cec5SDimitry Andric const ObjCForCollectionStmt *FCS) { 882*0b57cec5SDimitry Andric if (!State) 883*0b57cec5SDimitry Andric return nullptr; 884*0b57cec5SDimitry Andric 885*0b57cec5SDimitry Andric // See if the collection is one where we /know/ the elements are non-nil. 886*0b57cec5SDimitry Andric if (!isKnownNonNilCollectionType(FCS->getCollection()->getType())) 887*0b57cec5SDimitry Andric return State; 888*0b57cec5SDimitry Andric 889*0b57cec5SDimitry Andric const LocationContext *LCtx = C.getLocationContext(); 890*0b57cec5SDimitry Andric const Stmt *Element = FCS->getElement(); 891*0b57cec5SDimitry Andric 892*0b57cec5SDimitry Andric // FIXME: Copied from ExprEngineObjC. 893*0b57cec5SDimitry Andric Optional<Loc> ElementLoc; 894*0b57cec5SDimitry Andric if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { 895*0b57cec5SDimitry Andric const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); 896*0b57cec5SDimitry Andric assert(ElemDecl->getInit() == nullptr); 897*0b57cec5SDimitry Andric ElementLoc = State->getLValue(ElemDecl, LCtx); 898*0b57cec5SDimitry Andric } else { 899*0b57cec5SDimitry Andric ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>(); 900*0b57cec5SDimitry Andric } 901*0b57cec5SDimitry Andric 902*0b57cec5SDimitry Andric if (!ElementLoc) 903*0b57cec5SDimitry Andric return State; 904*0b57cec5SDimitry Andric 905*0b57cec5SDimitry Andric // Go ahead and assume the value is non-nil. 906*0b57cec5SDimitry Andric SVal Val = State->getSVal(*ElementLoc); 907*0b57cec5SDimitry Andric return State->assume(Val.castAs<DefinedOrUnknownSVal>(), true); 908*0b57cec5SDimitry Andric } 909*0b57cec5SDimitry Andric 910*0b57cec5SDimitry Andric /// Returns NULL state if the collection is known to contain elements 911*0b57cec5SDimitry Andric /// (or is known not to contain elements if the Assumption parameter is false.) 912*0b57cec5SDimitry Andric static ProgramStateRef 913*0b57cec5SDimitry Andric assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, 914*0b57cec5SDimitry Andric SymbolRef CollectionS, bool Assumption) { 915*0b57cec5SDimitry Andric if (!State || !CollectionS) 916*0b57cec5SDimitry Andric return State; 917*0b57cec5SDimitry Andric 918*0b57cec5SDimitry Andric const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS); 919*0b57cec5SDimitry Andric if (!CountS) { 920*0b57cec5SDimitry Andric const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS); 921*0b57cec5SDimitry Andric if (!KnownNonEmpty) 922*0b57cec5SDimitry Andric return State->set<ContainerNonEmptyMap>(CollectionS, Assumption); 923*0b57cec5SDimitry Andric return (Assumption == *KnownNonEmpty) ? State : nullptr; 924*0b57cec5SDimitry Andric } 925*0b57cec5SDimitry Andric 926*0b57cec5SDimitry Andric SValBuilder &SvalBuilder = C.getSValBuilder(); 927*0b57cec5SDimitry Andric SVal CountGreaterThanZeroVal = 928*0b57cec5SDimitry Andric SvalBuilder.evalBinOp(State, BO_GT, 929*0b57cec5SDimitry Andric nonloc::SymbolVal(*CountS), 930*0b57cec5SDimitry Andric SvalBuilder.makeIntVal(0, (*CountS)->getType()), 931*0b57cec5SDimitry Andric SvalBuilder.getConditionType()); 932*0b57cec5SDimitry Andric Optional<DefinedSVal> CountGreaterThanZero = 933*0b57cec5SDimitry Andric CountGreaterThanZeroVal.getAs<DefinedSVal>(); 934*0b57cec5SDimitry Andric if (!CountGreaterThanZero) { 935*0b57cec5SDimitry Andric // The SValBuilder cannot construct a valid SVal for this condition. 936*0b57cec5SDimitry Andric // This means we cannot properly reason about it. 937*0b57cec5SDimitry Andric return State; 938*0b57cec5SDimitry Andric } 939*0b57cec5SDimitry Andric 940*0b57cec5SDimitry Andric return State->assume(*CountGreaterThanZero, Assumption); 941*0b57cec5SDimitry Andric } 942*0b57cec5SDimitry Andric 943*0b57cec5SDimitry Andric static ProgramStateRef 944*0b57cec5SDimitry Andric assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, 945*0b57cec5SDimitry Andric const ObjCForCollectionStmt *FCS, 946*0b57cec5SDimitry Andric bool Assumption) { 947*0b57cec5SDimitry Andric if (!State) 948*0b57cec5SDimitry Andric return nullptr; 949*0b57cec5SDimitry Andric 950*0b57cec5SDimitry Andric SymbolRef CollectionS = C.getSVal(FCS->getCollection()).getAsSymbol(); 951*0b57cec5SDimitry Andric return assumeCollectionNonEmpty(C, State, CollectionS, Assumption); 952*0b57cec5SDimitry Andric } 953*0b57cec5SDimitry Andric 954*0b57cec5SDimitry Andric /// If the fist block edge is a back edge, we are reentering the loop. 955*0b57cec5SDimitry Andric static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N, 956*0b57cec5SDimitry Andric const ObjCForCollectionStmt *FCS) { 957*0b57cec5SDimitry Andric if (!N) 958*0b57cec5SDimitry Andric return false; 959*0b57cec5SDimitry Andric 960*0b57cec5SDimitry Andric ProgramPoint P = N->getLocation(); 961*0b57cec5SDimitry Andric if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { 962*0b57cec5SDimitry Andric return BE->getSrc()->getLoopTarget() == FCS; 963*0b57cec5SDimitry Andric } 964*0b57cec5SDimitry Andric 965*0b57cec5SDimitry Andric // Keep looking for a block edge. 966*0b57cec5SDimitry Andric for (ExplodedNode::const_pred_iterator I = N->pred_begin(), 967*0b57cec5SDimitry Andric E = N->pred_end(); I != E; ++I) { 968*0b57cec5SDimitry Andric if (alreadyExecutedAtLeastOneLoopIteration(*I, FCS)) 969*0b57cec5SDimitry Andric return true; 970*0b57cec5SDimitry Andric } 971*0b57cec5SDimitry Andric 972*0b57cec5SDimitry Andric return false; 973*0b57cec5SDimitry Andric } 974*0b57cec5SDimitry Andric 975*0b57cec5SDimitry Andric void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, 976*0b57cec5SDimitry Andric CheckerContext &C) const { 977*0b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 978*0b57cec5SDimitry Andric 979*0b57cec5SDimitry Andric // Check if this is the branch for the end of the loop. 980*0b57cec5SDimitry Andric SVal CollectionSentinel = C.getSVal(FCS); 981*0b57cec5SDimitry Andric if (CollectionSentinel.isZeroConstant()) { 982*0b57cec5SDimitry Andric if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS)) 983*0b57cec5SDimitry Andric State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false); 984*0b57cec5SDimitry Andric 985*0b57cec5SDimitry Andric // Otherwise, this is a branch that goes through the loop body. 986*0b57cec5SDimitry Andric } else { 987*0b57cec5SDimitry Andric State = checkCollectionNonNil(C, State, FCS); 988*0b57cec5SDimitry Andric State = checkElementNonNil(C, State, FCS); 989*0b57cec5SDimitry Andric State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true); 990*0b57cec5SDimitry Andric } 991*0b57cec5SDimitry Andric 992*0b57cec5SDimitry Andric if (!State) 993*0b57cec5SDimitry Andric C.generateSink(C.getState(), C.getPredecessor()); 994*0b57cec5SDimitry Andric else if (State != C.getState()) 995*0b57cec5SDimitry Andric C.addTransition(State); 996*0b57cec5SDimitry Andric } 997*0b57cec5SDimitry Andric 998*0b57cec5SDimitry Andric bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M, 999*0b57cec5SDimitry Andric CheckerContext &C) const { 1000*0b57cec5SDimitry Andric Selector S = M.getSelector(); 1001*0b57cec5SDimitry Andric // Initialize the identifiers on first use. 1002*0b57cec5SDimitry Andric if (!CountSelectorII) 1003*0b57cec5SDimitry Andric CountSelectorII = &C.getASTContext().Idents.get("count"); 1004*0b57cec5SDimitry Andric 1005*0b57cec5SDimitry Andric // If the method returns collection count, record the value. 1006*0b57cec5SDimitry Andric return S.isUnarySelector() && 1007*0b57cec5SDimitry Andric (S.getIdentifierInfoForSlot(0) == CountSelectorII); 1008*0b57cec5SDimitry Andric } 1009*0b57cec5SDimitry Andric 1010*0b57cec5SDimitry Andric void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M, 1011*0b57cec5SDimitry Andric CheckerContext &C) const { 1012*0b57cec5SDimitry Andric if (!M.isInstanceMessage()) 1013*0b57cec5SDimitry Andric return; 1014*0b57cec5SDimitry Andric 1015*0b57cec5SDimitry Andric const ObjCInterfaceDecl *ClassID = M.getReceiverInterface(); 1016*0b57cec5SDimitry Andric if (!ClassID) 1017*0b57cec5SDimitry Andric return; 1018*0b57cec5SDimitry Andric 1019*0b57cec5SDimitry Andric FoundationClass Class = findKnownClass(ClassID); 1020*0b57cec5SDimitry Andric if (Class != FC_NSDictionary && 1021*0b57cec5SDimitry Andric Class != FC_NSArray && 1022*0b57cec5SDimitry Andric Class != FC_NSSet && 1023*0b57cec5SDimitry Andric Class != FC_NSOrderedSet) 1024*0b57cec5SDimitry Andric return; 1025*0b57cec5SDimitry Andric 1026*0b57cec5SDimitry Andric SymbolRef ContainerS = M.getReceiverSVal().getAsSymbol(); 1027*0b57cec5SDimitry Andric if (!ContainerS) 1028*0b57cec5SDimitry Andric return; 1029*0b57cec5SDimitry Andric 1030*0b57cec5SDimitry Andric // If we are processing a call to "count", get the symbolic value returned by 1031*0b57cec5SDimitry Andric // a call to "count" and add it to the map. 1032*0b57cec5SDimitry Andric if (!isCollectionCountMethod(M, C)) 1033*0b57cec5SDimitry Andric return; 1034*0b57cec5SDimitry Andric 1035*0b57cec5SDimitry Andric const Expr *MsgExpr = M.getOriginExpr(); 1036*0b57cec5SDimitry Andric SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol(); 1037*0b57cec5SDimitry Andric if (CountS) { 1038*0b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 1039*0b57cec5SDimitry Andric 1040*0b57cec5SDimitry Andric C.getSymbolManager().addSymbolDependency(ContainerS, CountS); 1041*0b57cec5SDimitry Andric State = State->set<ContainerCountMap>(ContainerS, CountS); 1042*0b57cec5SDimitry Andric 1043*0b57cec5SDimitry Andric if (const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) { 1044*0b57cec5SDimitry Andric State = State->remove<ContainerNonEmptyMap>(ContainerS); 1045*0b57cec5SDimitry Andric State = assumeCollectionNonEmpty(C, State, ContainerS, *NonEmpty); 1046*0b57cec5SDimitry Andric } 1047*0b57cec5SDimitry Andric 1048*0b57cec5SDimitry Andric C.addTransition(State); 1049*0b57cec5SDimitry Andric } 1050*0b57cec5SDimitry Andric } 1051*0b57cec5SDimitry Andric 1052*0b57cec5SDimitry Andric static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call) { 1053*0b57cec5SDimitry Andric const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call); 1054*0b57cec5SDimitry Andric if (!Message) 1055*0b57cec5SDimitry Andric return nullptr; 1056*0b57cec5SDimitry Andric 1057*0b57cec5SDimitry Andric const ObjCMethodDecl *MD = Message->getDecl(); 1058*0b57cec5SDimitry Andric if (!MD) 1059*0b57cec5SDimitry Andric return nullptr; 1060*0b57cec5SDimitry Andric 1061*0b57cec5SDimitry Andric const ObjCInterfaceDecl *StaticClass; 1062*0b57cec5SDimitry Andric if (isa<ObjCProtocolDecl>(MD->getDeclContext())) { 1063*0b57cec5SDimitry Andric // We can't find out where the method was declared without doing more work. 1064*0b57cec5SDimitry Andric // Instead, see if the receiver is statically typed as a known immutable 1065*0b57cec5SDimitry Andric // collection. 1066*0b57cec5SDimitry Andric StaticClass = Message->getOriginExpr()->getReceiverInterface(); 1067*0b57cec5SDimitry Andric } else { 1068*0b57cec5SDimitry Andric StaticClass = MD->getClassInterface(); 1069*0b57cec5SDimitry Andric } 1070*0b57cec5SDimitry Andric 1071*0b57cec5SDimitry Andric if (!StaticClass) 1072*0b57cec5SDimitry Andric return nullptr; 1073*0b57cec5SDimitry Andric 1074*0b57cec5SDimitry Andric switch (findKnownClass(StaticClass, /*IncludeSuper=*/false)) { 1075*0b57cec5SDimitry Andric case FC_None: 1076*0b57cec5SDimitry Andric return nullptr; 1077*0b57cec5SDimitry Andric case FC_NSArray: 1078*0b57cec5SDimitry Andric case FC_NSDictionary: 1079*0b57cec5SDimitry Andric case FC_NSEnumerator: 1080*0b57cec5SDimitry Andric case FC_NSNull: 1081*0b57cec5SDimitry Andric case FC_NSOrderedSet: 1082*0b57cec5SDimitry Andric case FC_NSSet: 1083*0b57cec5SDimitry Andric case FC_NSString: 1084*0b57cec5SDimitry Andric break; 1085*0b57cec5SDimitry Andric } 1086*0b57cec5SDimitry Andric 1087*0b57cec5SDimitry Andric return Message->getReceiverSVal().getAsSymbol(); 1088*0b57cec5SDimitry Andric } 1089*0b57cec5SDimitry Andric 1090*0b57cec5SDimitry Andric ProgramStateRef 1091*0b57cec5SDimitry Andric ObjCLoopChecker::checkPointerEscape(ProgramStateRef State, 1092*0b57cec5SDimitry Andric const InvalidatedSymbols &Escaped, 1093*0b57cec5SDimitry Andric const CallEvent *Call, 1094*0b57cec5SDimitry Andric PointerEscapeKind Kind) const { 1095*0b57cec5SDimitry Andric SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call); 1096*0b57cec5SDimitry Andric 1097*0b57cec5SDimitry Andric // Remove the invalidated symbols form the collection count map. 1098*0b57cec5SDimitry Andric for (InvalidatedSymbols::const_iterator I = Escaped.begin(), 1099*0b57cec5SDimitry Andric E = Escaped.end(); 1100*0b57cec5SDimitry Andric I != E; ++I) { 1101*0b57cec5SDimitry Andric SymbolRef Sym = *I; 1102*0b57cec5SDimitry Andric 1103*0b57cec5SDimitry Andric // Don't invalidate this symbol's count if we know the method being called 1104*0b57cec5SDimitry Andric // is declared on an immutable class. This isn't completely correct if the 1105*0b57cec5SDimitry Andric // receiver is also passed as an argument, but in most uses of NSArray, 1106*0b57cec5SDimitry Andric // NSDictionary, etc. this isn't likely to happen in a dangerous way. 1107*0b57cec5SDimitry Andric if (Sym == ImmutableReceiver) 1108*0b57cec5SDimitry Andric continue; 1109*0b57cec5SDimitry Andric 1110*0b57cec5SDimitry Andric // The symbol escaped. Pessimistically, assume that the count could have 1111*0b57cec5SDimitry Andric // changed. 1112*0b57cec5SDimitry Andric State = State->remove<ContainerCountMap>(Sym); 1113*0b57cec5SDimitry Andric State = State->remove<ContainerNonEmptyMap>(Sym); 1114*0b57cec5SDimitry Andric } 1115*0b57cec5SDimitry Andric return State; 1116*0b57cec5SDimitry Andric } 1117*0b57cec5SDimitry Andric 1118*0b57cec5SDimitry Andric void ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper, 1119*0b57cec5SDimitry Andric CheckerContext &C) const { 1120*0b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 1121*0b57cec5SDimitry Andric 1122*0b57cec5SDimitry Andric // Remove the dead symbols from the collection count map. 1123*0b57cec5SDimitry Andric ContainerCountMapTy Tracked = State->get<ContainerCountMap>(); 1124*0b57cec5SDimitry Andric for (ContainerCountMapTy::iterator I = Tracked.begin(), 1125*0b57cec5SDimitry Andric E = Tracked.end(); I != E; ++I) { 1126*0b57cec5SDimitry Andric SymbolRef Sym = I->first; 1127*0b57cec5SDimitry Andric if (SymReaper.isDead(Sym)) { 1128*0b57cec5SDimitry Andric State = State->remove<ContainerCountMap>(Sym); 1129*0b57cec5SDimitry Andric State = State->remove<ContainerNonEmptyMap>(Sym); 1130*0b57cec5SDimitry Andric } 1131*0b57cec5SDimitry Andric } 1132*0b57cec5SDimitry Andric 1133*0b57cec5SDimitry Andric C.addTransition(State); 1134*0b57cec5SDimitry Andric } 1135*0b57cec5SDimitry Andric 1136*0b57cec5SDimitry Andric namespace { 1137*0b57cec5SDimitry Andric /// \class ObjCNonNilReturnValueChecker 1138*0b57cec5SDimitry Andric /// The checker restricts the return values of APIs known to 1139*0b57cec5SDimitry Andric /// never (or almost never) return 'nil'. 1140*0b57cec5SDimitry Andric class ObjCNonNilReturnValueChecker 1141*0b57cec5SDimitry Andric : public Checker<check::PostObjCMessage, 1142*0b57cec5SDimitry Andric check::PostStmt<ObjCArrayLiteral>, 1143*0b57cec5SDimitry Andric check::PostStmt<ObjCDictionaryLiteral>, 1144*0b57cec5SDimitry Andric check::PostStmt<ObjCBoxedExpr> > { 1145*0b57cec5SDimitry Andric mutable bool Initialized; 1146*0b57cec5SDimitry Andric mutable Selector ObjectAtIndex; 1147*0b57cec5SDimitry Andric mutable Selector ObjectAtIndexedSubscript; 1148*0b57cec5SDimitry Andric mutable Selector NullSelector; 1149*0b57cec5SDimitry Andric 1150*0b57cec5SDimitry Andric public: 1151*0b57cec5SDimitry Andric ObjCNonNilReturnValueChecker() : Initialized(false) {} 1152*0b57cec5SDimitry Andric 1153*0b57cec5SDimitry Andric ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr, 1154*0b57cec5SDimitry Andric ProgramStateRef State, 1155*0b57cec5SDimitry Andric CheckerContext &C) const; 1156*0b57cec5SDimitry Andric void assumeExprIsNonNull(const Expr *E, CheckerContext &C) const { 1157*0b57cec5SDimitry Andric C.addTransition(assumeExprIsNonNull(E, C.getState(), C)); 1158*0b57cec5SDimitry Andric } 1159*0b57cec5SDimitry Andric 1160*0b57cec5SDimitry Andric void checkPostStmt(const ObjCArrayLiteral *E, CheckerContext &C) const { 1161*0b57cec5SDimitry Andric assumeExprIsNonNull(E, C); 1162*0b57cec5SDimitry Andric } 1163*0b57cec5SDimitry Andric void checkPostStmt(const ObjCDictionaryLiteral *E, CheckerContext &C) const { 1164*0b57cec5SDimitry Andric assumeExprIsNonNull(E, C); 1165*0b57cec5SDimitry Andric } 1166*0b57cec5SDimitry Andric void checkPostStmt(const ObjCBoxedExpr *E, CheckerContext &C) const { 1167*0b57cec5SDimitry Andric assumeExprIsNonNull(E, C); 1168*0b57cec5SDimitry Andric } 1169*0b57cec5SDimitry Andric 1170*0b57cec5SDimitry Andric void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 1171*0b57cec5SDimitry Andric }; 1172*0b57cec5SDimitry Andric } // end anonymous namespace 1173*0b57cec5SDimitry Andric 1174*0b57cec5SDimitry Andric ProgramStateRef 1175*0b57cec5SDimitry Andric ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr, 1176*0b57cec5SDimitry Andric ProgramStateRef State, 1177*0b57cec5SDimitry Andric CheckerContext &C) const { 1178*0b57cec5SDimitry Andric SVal Val = C.getSVal(NonNullExpr); 1179*0b57cec5SDimitry Andric if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>()) 1180*0b57cec5SDimitry Andric return State->assume(*DV, true); 1181*0b57cec5SDimitry Andric return State; 1182*0b57cec5SDimitry Andric } 1183*0b57cec5SDimitry Andric 1184*0b57cec5SDimitry Andric void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M, 1185*0b57cec5SDimitry Andric CheckerContext &C) 1186*0b57cec5SDimitry Andric const { 1187*0b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 1188*0b57cec5SDimitry Andric 1189*0b57cec5SDimitry Andric if (!Initialized) { 1190*0b57cec5SDimitry Andric ASTContext &Ctx = C.getASTContext(); 1191*0b57cec5SDimitry Andric ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx); 1192*0b57cec5SDimitry Andric ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx); 1193*0b57cec5SDimitry Andric NullSelector = GetNullarySelector("null", Ctx); 1194*0b57cec5SDimitry Andric } 1195*0b57cec5SDimitry Andric 1196*0b57cec5SDimitry Andric // Check the receiver type. 1197*0b57cec5SDimitry Andric if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) { 1198*0b57cec5SDimitry Andric 1199*0b57cec5SDimitry Andric // Assume that object returned from '[self init]' or '[super init]' is not 1200*0b57cec5SDimitry Andric // 'nil' if we are processing an inlined function/method. 1201*0b57cec5SDimitry Andric // 1202*0b57cec5SDimitry Andric // A defensive callee will (and should) check if the object returned by 1203*0b57cec5SDimitry Andric // '[super init]' is 'nil' before doing it's own initialization. However, 1204*0b57cec5SDimitry Andric // since 'nil' is rarely returned in practice, we should not warn when the 1205*0b57cec5SDimitry Andric // caller to the defensive constructor uses the object in contexts where 1206*0b57cec5SDimitry Andric // 'nil' is not accepted. 1207*0b57cec5SDimitry Andric if (!C.inTopFrame() && M.getDecl() && 1208*0b57cec5SDimitry Andric M.getDecl()->getMethodFamily() == OMF_init && 1209*0b57cec5SDimitry Andric M.isReceiverSelfOrSuper()) { 1210*0b57cec5SDimitry Andric State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 1211*0b57cec5SDimitry Andric } 1212*0b57cec5SDimitry Andric 1213*0b57cec5SDimitry Andric FoundationClass Cl = findKnownClass(Interface); 1214*0b57cec5SDimitry Andric 1215*0b57cec5SDimitry Andric // Objects returned from 1216*0b57cec5SDimitry Andric // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript] 1217*0b57cec5SDimitry Andric // are never 'nil'. 1218*0b57cec5SDimitry Andric if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) { 1219*0b57cec5SDimitry Andric Selector Sel = M.getSelector(); 1220*0b57cec5SDimitry Andric if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) { 1221*0b57cec5SDimitry Andric // Go ahead and assume the value is non-nil. 1222*0b57cec5SDimitry Andric State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 1223*0b57cec5SDimitry Andric } 1224*0b57cec5SDimitry Andric } 1225*0b57cec5SDimitry Andric 1226*0b57cec5SDimitry Andric // Objects returned from [NSNull null] are not nil. 1227*0b57cec5SDimitry Andric if (Cl == FC_NSNull) { 1228*0b57cec5SDimitry Andric if (M.getSelector() == NullSelector) { 1229*0b57cec5SDimitry Andric // Go ahead and assume the value is non-nil. 1230*0b57cec5SDimitry Andric State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 1231*0b57cec5SDimitry Andric } 1232*0b57cec5SDimitry Andric } 1233*0b57cec5SDimitry Andric } 1234*0b57cec5SDimitry Andric C.addTransition(State); 1235*0b57cec5SDimitry Andric } 1236*0b57cec5SDimitry Andric 1237*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 1238*0b57cec5SDimitry Andric // Check registration. 1239*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 1240*0b57cec5SDimitry Andric 1241*0b57cec5SDimitry Andric void ento::registerNilArgChecker(CheckerManager &mgr) { 1242*0b57cec5SDimitry Andric mgr.registerChecker<NilArgChecker>(); 1243*0b57cec5SDimitry Andric } 1244*0b57cec5SDimitry Andric 1245*0b57cec5SDimitry Andric bool ento::shouldRegisterNilArgChecker(const LangOptions &LO) { 1246*0b57cec5SDimitry Andric return true; 1247*0b57cec5SDimitry Andric } 1248*0b57cec5SDimitry Andric 1249*0b57cec5SDimitry Andric void ento::registerCFNumberChecker(CheckerManager &mgr) { 1250*0b57cec5SDimitry Andric mgr.registerChecker<CFNumberChecker>(); 1251*0b57cec5SDimitry Andric } 1252*0b57cec5SDimitry Andric 1253*0b57cec5SDimitry Andric bool ento::shouldRegisterCFNumberChecker(const LangOptions &LO) { 1254*0b57cec5SDimitry Andric return true; 1255*0b57cec5SDimitry Andric } 1256*0b57cec5SDimitry Andric 1257*0b57cec5SDimitry Andric void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { 1258*0b57cec5SDimitry Andric mgr.registerChecker<CFRetainReleaseChecker>(); 1259*0b57cec5SDimitry Andric } 1260*0b57cec5SDimitry Andric 1261*0b57cec5SDimitry Andric bool ento::shouldRegisterCFRetainReleaseChecker(const LangOptions &LO) { 1262*0b57cec5SDimitry Andric return true; 1263*0b57cec5SDimitry Andric } 1264*0b57cec5SDimitry Andric 1265*0b57cec5SDimitry Andric void ento::registerClassReleaseChecker(CheckerManager &mgr) { 1266*0b57cec5SDimitry Andric mgr.registerChecker<ClassReleaseChecker>(); 1267*0b57cec5SDimitry Andric } 1268*0b57cec5SDimitry Andric 1269*0b57cec5SDimitry Andric bool ento::shouldRegisterClassReleaseChecker(const LangOptions &LO) { 1270*0b57cec5SDimitry Andric return true; 1271*0b57cec5SDimitry Andric } 1272*0b57cec5SDimitry Andric 1273*0b57cec5SDimitry Andric void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { 1274*0b57cec5SDimitry Andric mgr.registerChecker<VariadicMethodTypeChecker>(); 1275*0b57cec5SDimitry Andric } 1276*0b57cec5SDimitry Andric 1277*0b57cec5SDimitry Andric bool ento::shouldRegisterVariadicMethodTypeChecker(const LangOptions &LO) { 1278*0b57cec5SDimitry Andric return true; 1279*0b57cec5SDimitry Andric } 1280*0b57cec5SDimitry Andric 1281*0b57cec5SDimitry Andric void ento::registerObjCLoopChecker(CheckerManager &mgr) { 1282*0b57cec5SDimitry Andric mgr.registerChecker<ObjCLoopChecker>(); 1283*0b57cec5SDimitry Andric } 1284*0b57cec5SDimitry Andric 1285*0b57cec5SDimitry Andric bool ento::shouldRegisterObjCLoopChecker(const LangOptions &LO) { 1286*0b57cec5SDimitry Andric return true; 1287*0b57cec5SDimitry Andric } 1288*0b57cec5SDimitry Andric 1289*0b57cec5SDimitry Andric void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) { 1290*0b57cec5SDimitry Andric mgr.registerChecker<ObjCNonNilReturnValueChecker>(); 1291*0b57cec5SDimitry Andric } 1292*0b57cec5SDimitry Andric 1293*0b57cec5SDimitry Andric bool ento::shouldRegisterObjCNonNilReturnValueChecker(const LangOptions &LO) { 1294*0b57cec5SDimitry Andric return true; 1295*0b57cec5SDimitry Andric } 1296