clang API Documentation
00001 //== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*-- 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This file defines BasicObjCFoundationChecks, a class that encapsulates 00011 // a set of simple checks to run on Objective-C code using Apple's Foundation 00012 // classes. 00013 // 00014 //===----------------------------------------------------------------------===// 00015 00016 #include "ClangSACheckers.h" 00017 #include "SelectorExtras.h" 00018 #include "clang/AST/ASTContext.h" 00019 #include "clang/AST/DeclObjC.h" 00020 #include "clang/AST/Expr.h" 00021 #include "clang/AST/ExprObjC.h" 00022 #include "clang/AST/StmtObjC.h" 00023 #include "clang/Analysis/DomainSpecific/CocoaConventions.h" 00024 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 00025 #include "clang/StaticAnalyzer/Core/Checker.h" 00026 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 00027 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 00028 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 00029 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 00030 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 00031 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" 00032 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 00033 #include "llvm/ADT/SmallString.h" 00034 #include "llvm/ADT/StringMap.h" 00035 #include "llvm/Support/raw_ostream.h" 00036 00037 using namespace clang; 00038 using namespace ento; 00039 00040 namespace { 00041 class APIMisuse : public BugType { 00042 public: 00043 APIMisuse(const CheckerBase *checker, const char *name) 00044 : BugType(checker, name, "API Misuse (Apple)") {} 00045 }; 00046 } // end anonymous namespace 00047 00048 //===----------------------------------------------------------------------===// 00049 // Utility functions. 00050 //===----------------------------------------------------------------------===// 00051 00052 static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) { 00053 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) 00054 return ID->getIdentifier()->getName(); 00055 return StringRef(); 00056 } 00057 00058 enum FoundationClass { 00059 FC_None, 00060 FC_NSArray, 00061 FC_NSDictionary, 00062 FC_NSEnumerator, 00063 FC_NSNull, 00064 FC_NSOrderedSet, 00065 FC_NSSet, 00066 FC_NSString 00067 }; 00068 00069 static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID, 00070 bool IncludeSuperclasses = true) { 00071 static llvm::StringMap<FoundationClass> Classes; 00072 if (Classes.empty()) { 00073 Classes["NSArray"] = FC_NSArray; 00074 Classes["NSDictionary"] = FC_NSDictionary; 00075 Classes["NSEnumerator"] = FC_NSEnumerator; 00076 Classes["NSNull"] = FC_NSNull; 00077 Classes["NSOrderedSet"] = FC_NSOrderedSet; 00078 Classes["NSSet"] = FC_NSSet; 00079 Classes["NSString"] = FC_NSString; 00080 } 00081 00082 // FIXME: Should we cache this at all? 00083 FoundationClass result = Classes.lookup(ID->getIdentifier()->getName()); 00084 if (result == FC_None && IncludeSuperclasses) 00085 if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) 00086 return findKnownClass(Super); 00087 00088 return result; 00089 } 00090 00091 //===----------------------------------------------------------------------===// 00092 // NilArgChecker - Check for prohibited nil arguments to ObjC method calls. 00093 //===----------------------------------------------------------------------===// 00094 00095 namespace { 00096 class NilArgChecker : public Checker<check::PreObjCMessage, 00097 check::PostStmt<ObjCDictionaryLiteral>, 00098 check::PostStmt<ObjCArrayLiteral> > { 00099 mutable std::unique_ptr<APIMisuse> BT; 00100 00101 mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors; 00102 mutable Selector ArrayWithObjectSel; 00103 mutable Selector AddObjectSel; 00104 mutable Selector InsertObjectAtIndexSel; 00105 mutable Selector ReplaceObjectAtIndexWithObjectSel; 00106 mutable Selector SetObjectAtIndexedSubscriptSel; 00107 mutable Selector ArrayByAddingObjectSel; 00108 mutable Selector DictionaryWithObjectForKeySel; 00109 mutable Selector SetObjectForKeySel; 00110 mutable Selector SetObjectForKeyedSubscriptSel; 00111 mutable Selector RemoveObjectForKeySel; 00112 00113 void warnIfNilExpr(const Expr *E, 00114 const char *Msg, 00115 CheckerContext &C) const; 00116 00117 void warnIfNilArg(CheckerContext &C, 00118 const ObjCMethodCall &msg, unsigned Arg, 00119 FoundationClass Class, 00120 bool CanBeSubscript = false) const; 00121 00122 void generateBugReport(ExplodedNode *N, 00123 StringRef Msg, 00124 SourceRange Range, 00125 const Expr *Expr, 00126 CheckerContext &C) const; 00127 00128 public: 00129 void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 00130 void checkPostStmt(const ObjCDictionaryLiteral *DL, 00131 CheckerContext &C) const; 00132 void checkPostStmt(const ObjCArrayLiteral *AL, 00133 CheckerContext &C) const; 00134 }; 00135 } 00136 00137 void NilArgChecker::warnIfNilExpr(const Expr *E, 00138 const char *Msg, 00139 CheckerContext &C) const { 00140 ProgramStateRef State = C.getState(); 00141 if (State->isNull(C.getSVal(E)).isConstrainedTrue()) { 00142 00143 if (ExplodedNode *N = C.generateSink()) { 00144 generateBugReport(N, Msg, E->getSourceRange(), E, C); 00145 } 00146 00147 } 00148 } 00149 00150 void NilArgChecker::warnIfNilArg(CheckerContext &C, 00151 const ObjCMethodCall &msg, 00152 unsigned int Arg, 00153 FoundationClass Class, 00154 bool CanBeSubscript) const { 00155 // Check if the argument is nil. 00156 ProgramStateRef State = C.getState(); 00157 if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue()) 00158 return; 00159 00160 if (ExplodedNode *N = C.generateSink()) { 00161 SmallString<128> sbuf; 00162 llvm::raw_svector_ostream os(sbuf); 00163 00164 if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) { 00165 00166 if (Class == FC_NSArray) { 00167 os << "Array element cannot be nil"; 00168 } else if (Class == FC_NSDictionary) { 00169 if (Arg == 0) { 00170 os << "Value stored into '"; 00171 os << GetReceiverInterfaceName(msg) << "' cannot be nil"; 00172 } else { 00173 assert(Arg == 1); 00174 os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil"; 00175 } 00176 } else 00177 llvm_unreachable("Missing foundation class for the subscript expr"); 00178 00179 } else { 00180 if (Class == FC_NSDictionary) { 00181 if (Arg == 0) 00182 os << "Value argument "; 00183 else { 00184 assert(Arg == 1); 00185 os << "Key argument "; 00186 } 00187 os << "to '"; 00188 msg.getSelector().print(os); 00189 os << "' cannot be nil"; 00190 } else { 00191 os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"; 00192 msg.getSelector().print(os); 00193 os << "' cannot be nil"; 00194 } 00195 } 00196 00197 generateBugReport(N, os.str(), msg.getArgSourceRange(Arg), 00198 msg.getArgExpr(Arg), C); 00199 } 00200 } 00201 00202 void NilArgChecker::generateBugReport(ExplodedNode *N, 00203 StringRef Msg, 00204 SourceRange Range, 00205 const Expr *E, 00206 CheckerContext &C) const { 00207 if (!BT) 00208 BT.reset(new APIMisuse(this, "nil argument")); 00209 00210 BugReport *R = new BugReport(*BT, Msg, N); 00211 R->addRange(Range); 00212 bugreporter::trackNullOrUndefValue(N, E, *R); 00213 C.emitReport(R); 00214 } 00215 00216 void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 00217 CheckerContext &C) const { 00218 const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); 00219 if (!ID) 00220 return; 00221 00222 FoundationClass Class = findKnownClass(ID); 00223 00224 static const unsigned InvalidArgIndex = UINT_MAX; 00225 unsigned Arg = InvalidArgIndex; 00226 bool CanBeSubscript = false; 00227 00228 if (Class == FC_NSString) { 00229 Selector S = msg.getSelector(); 00230 00231 if (S.isUnarySelector()) 00232 return; 00233 00234 if (StringSelectors.empty()) { 00235 ASTContext &Ctx = C.getASTContext(); 00236 Selector Sels[] = { 00237 getKeywordSelector(Ctx, "caseInsensitiveCompare", nullptr), 00238 getKeywordSelector(Ctx, "compare", nullptr), 00239 getKeywordSelector(Ctx, "compare", "options", nullptr), 00240 getKeywordSelector(Ctx, "compare", "options", "range", nullptr), 00241 getKeywordSelector(Ctx, "compare", "options", "range", "locale", 00242 nullptr), 00243 getKeywordSelector(Ctx, "componentsSeparatedByCharactersInSet", 00244 nullptr), 00245 getKeywordSelector(Ctx, "initWithFormat", 00246 nullptr), 00247 getKeywordSelector(Ctx, "localizedCaseInsensitiveCompare", nullptr), 00248 getKeywordSelector(Ctx, "localizedCompare", nullptr), 00249 getKeywordSelector(Ctx, "localizedStandardCompare", nullptr), 00250 }; 00251 for (Selector KnownSel : Sels) 00252 StringSelectors[KnownSel] = 0; 00253 } 00254 auto I = StringSelectors.find(S); 00255 if (I == StringSelectors.end()) 00256 return; 00257 Arg = I->second; 00258 } else if (Class == FC_NSArray) { 00259 Selector S = msg.getSelector(); 00260 00261 if (S.isUnarySelector()) 00262 return; 00263 00264 if (ArrayWithObjectSel.isNull()) { 00265 ASTContext &Ctx = C.getASTContext(); 00266 ArrayWithObjectSel = getKeywordSelector(Ctx, "arrayWithObject", nullptr); 00267 AddObjectSel = getKeywordSelector(Ctx, "addObject", nullptr); 00268 InsertObjectAtIndexSel = 00269 getKeywordSelector(Ctx, "insertObject", "atIndex", nullptr); 00270 ReplaceObjectAtIndexWithObjectSel = 00271 getKeywordSelector(Ctx, "replaceObjectAtIndex", "withObject", nullptr); 00272 SetObjectAtIndexedSubscriptSel = 00273 getKeywordSelector(Ctx, "setObject", "atIndexedSubscript", nullptr); 00274 ArrayByAddingObjectSel = 00275 getKeywordSelector(Ctx, "arrayByAddingObject", nullptr); 00276 } 00277 00278 if (S == ArrayWithObjectSel || S == AddObjectSel || 00279 S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) { 00280 Arg = 0; 00281 } else if (S == SetObjectAtIndexedSubscriptSel) { 00282 Arg = 0; 00283 CanBeSubscript = true; 00284 } else if (S == ReplaceObjectAtIndexWithObjectSel) { 00285 Arg = 1; 00286 } 00287 } else if (Class == FC_NSDictionary) { 00288 Selector S = msg.getSelector(); 00289 00290 if (S.isUnarySelector()) 00291 return; 00292 00293 if (DictionaryWithObjectForKeySel.isNull()) { 00294 ASTContext &Ctx = C.getASTContext(); 00295 DictionaryWithObjectForKeySel = 00296 getKeywordSelector(Ctx, "dictionaryWithObject", "forKey", nullptr); 00297 SetObjectForKeySel = 00298 getKeywordSelector(Ctx, "setObject", "forKey", nullptr); 00299 SetObjectForKeyedSubscriptSel = 00300 getKeywordSelector(Ctx, "setObject", "forKeyedSubscript", nullptr); 00301 RemoveObjectForKeySel = 00302 getKeywordSelector(Ctx, "removeObjectForKey", nullptr); 00303 } 00304 00305 if (S == DictionaryWithObjectForKeySel || S == SetObjectForKeySel) { 00306 Arg = 0; 00307 warnIfNilArg(C, msg, /* Arg */1, Class); 00308 } else if (S == SetObjectForKeyedSubscriptSel) { 00309 CanBeSubscript = true; 00310 Arg = 0; 00311 warnIfNilArg(C, msg, /* Arg */1, Class, CanBeSubscript); 00312 } else if (S == RemoveObjectForKeySel) { 00313 Arg = 0; 00314 } 00315 } 00316 00317 // If argument is '0', report a warning. 00318 if ((Arg != InvalidArgIndex)) 00319 warnIfNilArg(C, msg, Arg, Class, CanBeSubscript); 00320 } 00321 00322 void NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL, 00323 CheckerContext &C) const { 00324 unsigned NumOfElements = AL->getNumElements(); 00325 for (unsigned i = 0; i < NumOfElements; ++i) { 00326 warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C); 00327 } 00328 } 00329 00330 void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL, 00331 CheckerContext &C) const { 00332 unsigned NumOfElements = DL->getNumElements(); 00333 for (unsigned i = 0; i < NumOfElements; ++i) { 00334 ObjCDictionaryElement Element = DL->getKeyValueElement(i); 00335 warnIfNilExpr(Element.Key, "Dictionary key cannot be nil", C); 00336 warnIfNilExpr(Element.Value, "Dictionary value cannot be nil", C); 00337 } 00338 } 00339 00340 //===----------------------------------------------------------------------===// 00341 // Error reporting. 00342 //===----------------------------------------------------------------------===// 00343 00344 namespace { 00345 class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > { 00346 mutable std::unique_ptr<APIMisuse> BT; 00347 mutable IdentifierInfo* II; 00348 public: 00349 CFNumberCreateChecker() : II(nullptr) {} 00350 00351 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 00352 00353 private: 00354 void EmitError(const TypedRegion* R, const Expr *Ex, 00355 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); 00356 }; 00357 } // end anonymous namespace 00358 00359 enum CFNumberType { 00360 kCFNumberSInt8Type = 1, 00361 kCFNumberSInt16Type = 2, 00362 kCFNumberSInt32Type = 3, 00363 kCFNumberSInt64Type = 4, 00364 kCFNumberFloat32Type = 5, 00365 kCFNumberFloat64Type = 6, 00366 kCFNumberCharType = 7, 00367 kCFNumberShortType = 8, 00368 kCFNumberIntType = 9, 00369 kCFNumberLongType = 10, 00370 kCFNumberLongLongType = 11, 00371 kCFNumberFloatType = 12, 00372 kCFNumberDoubleType = 13, 00373 kCFNumberCFIndexType = 14, 00374 kCFNumberNSIntegerType = 15, 00375 kCFNumberCGFloatType = 16 00376 }; 00377 00378 static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { 00379 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; 00380 00381 if (i < kCFNumberCharType) 00382 return FixedSize[i-1]; 00383 00384 QualType T; 00385 00386 switch (i) { 00387 case kCFNumberCharType: T = Ctx.CharTy; break; 00388 case kCFNumberShortType: T = Ctx.ShortTy; break; 00389 case kCFNumberIntType: T = Ctx.IntTy; break; 00390 case kCFNumberLongType: T = Ctx.LongTy; break; 00391 case kCFNumberLongLongType: T = Ctx.LongLongTy; break; 00392 case kCFNumberFloatType: T = Ctx.FloatTy; break; 00393 case kCFNumberDoubleType: T = Ctx.DoubleTy; break; 00394 case kCFNumberCFIndexType: 00395 case kCFNumberNSIntegerType: 00396 case kCFNumberCGFloatType: 00397 // FIXME: We need a way to map from names to Type*. 00398 default: 00399 return None; 00400 } 00401 00402 return Ctx.getTypeSize(T); 00403 } 00404 00405 #if 0 00406 static const char* GetCFNumberTypeStr(uint64_t i) { 00407 static const char* Names[] = { 00408 "kCFNumberSInt8Type", 00409 "kCFNumberSInt16Type", 00410 "kCFNumberSInt32Type", 00411 "kCFNumberSInt64Type", 00412 "kCFNumberFloat32Type", 00413 "kCFNumberFloat64Type", 00414 "kCFNumberCharType", 00415 "kCFNumberShortType", 00416 "kCFNumberIntType", 00417 "kCFNumberLongType", 00418 "kCFNumberLongLongType", 00419 "kCFNumberFloatType", 00420 "kCFNumberDoubleType", 00421 "kCFNumberCFIndexType", 00422 "kCFNumberNSIntegerType", 00423 "kCFNumberCGFloatType" 00424 }; 00425 00426 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType"; 00427 } 00428 #endif 00429 00430 void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, 00431 CheckerContext &C) const { 00432 ProgramStateRef state = C.getState(); 00433 const FunctionDecl *FD = C.getCalleeDecl(CE); 00434 if (!FD) 00435 return; 00436 00437 ASTContext &Ctx = C.getASTContext(); 00438 if (!II) 00439 II = &Ctx.Idents.get("CFNumberCreate"); 00440 00441 if (FD->getIdentifier() != II || CE->getNumArgs() != 3) 00442 return; 00443 00444 // Get the value of the "theType" argument. 00445 const LocationContext *LCtx = C.getLocationContext(); 00446 SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx); 00447 00448 // FIXME: We really should allow ranges of valid theType values, and 00449 // bifurcate the state appropriately. 00450 Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>(); 00451 if (!V) 00452 return; 00453 00454 uint64_t NumberKind = V->getValue().getLimitedValue(); 00455 Optional<uint64_t> OptTargetSize = GetCFNumberSize(Ctx, NumberKind); 00456 00457 // FIXME: In some cases we can emit an error. 00458 if (!OptTargetSize) 00459 return; 00460 00461 uint64_t TargetSize = *OptTargetSize; 00462 00463 // Look at the value of the integer being passed by reference. Essentially 00464 // we want to catch cases where the value passed in is not equal to the 00465 // size of the type being created. 00466 SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx); 00467 00468 // FIXME: Eventually we should handle arbitrary locations. We can do this 00469 // by having an enhanced memory model that does low-level typing. 00470 Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>(); 00471 if (!LV) 00472 return; 00473 00474 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts()); 00475 if (!R) 00476 return; 00477 00478 QualType T = Ctx.getCanonicalType(R->getValueType()); 00479 00480 // FIXME: If the pointee isn't an integer type, should we flag a warning? 00481 // People can do weird stuff with pointers. 00482 00483 if (!T->isIntegralOrEnumerationType()) 00484 return; 00485 00486 uint64_t SourceSize = Ctx.getTypeSize(T); 00487 00488 // CHECK: is SourceSize == TargetSize 00489 if (SourceSize == TargetSize) 00490 return; 00491 00492 // Generate an error. Only generate a sink if 'SourceSize < TargetSize'; 00493 // otherwise generate a regular node. 00494 // 00495 // FIXME: We can actually create an abstract "CFNumber" object that has 00496 // the bits initialized to the provided values. 00497 // 00498 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink() 00499 : C.addTransition()) { 00500 SmallString<128> sbuf; 00501 llvm::raw_svector_ostream os(sbuf); 00502 00503 os << (SourceSize == 8 ? "An " : "A ") 00504 << SourceSize << " bit integer is used to initialize a CFNumber " 00505 "object that represents " 00506 << (TargetSize == 8 ? "an " : "a ") 00507 << TargetSize << " bit integer. "; 00508 00509 if (SourceSize < TargetSize) 00510 os << (TargetSize - SourceSize) 00511 << " bits of the CFNumber value will be garbage." ; 00512 else 00513 os << (SourceSize - TargetSize) 00514 << " bits of the input integer will be lost."; 00515 00516 if (!BT) 00517 BT.reset(new APIMisuse(this, "Bad use of CFNumberCreate")); 00518 00519 BugReport *report = new BugReport(*BT, os.str(), N); 00520 report->addRange(CE->getArg(2)->getSourceRange()); 00521 C.emitReport(report); 00522 } 00523 } 00524 00525 //===----------------------------------------------------------------------===// 00526 // CFRetain/CFRelease/CFMakeCollectable/CFAutorelease checking for null arguments. 00527 //===----------------------------------------------------------------------===// 00528 00529 namespace { 00530 class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > { 00531 mutable std::unique_ptr<APIMisuse> BT; 00532 mutable IdentifierInfo *Retain, *Release, *MakeCollectable, *Autorelease; 00533 public: 00534 CFRetainReleaseChecker() 00535 : Retain(nullptr), Release(nullptr), MakeCollectable(nullptr), 00536 Autorelease(nullptr) {} 00537 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 00538 }; 00539 } // end anonymous namespace 00540 00541 00542 void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, 00543 CheckerContext &C) const { 00544 // If the CallExpr doesn't have exactly 1 argument just give up checking. 00545 if (CE->getNumArgs() != 1) 00546 return; 00547 00548 ProgramStateRef state = C.getState(); 00549 const FunctionDecl *FD = C.getCalleeDecl(CE); 00550 if (!FD) 00551 return; 00552 00553 if (!BT) { 00554 ASTContext &Ctx = C.getASTContext(); 00555 Retain = &Ctx.Idents.get("CFRetain"); 00556 Release = &Ctx.Idents.get("CFRelease"); 00557 MakeCollectable = &Ctx.Idents.get("CFMakeCollectable"); 00558 Autorelease = &Ctx.Idents.get("CFAutorelease"); 00559 BT.reset(new APIMisuse( 00560 this, "null passed to CF memory management function")); 00561 } 00562 00563 // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease. 00564 const IdentifierInfo *FuncII = FD->getIdentifier(); 00565 if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable || 00566 FuncII == Autorelease)) 00567 return; 00568 00569 // FIXME: The rest of this just checks that the argument is non-null. 00570 // It should probably be refactored and combined with NonNullParamChecker. 00571 00572 // Get the argument's value. 00573 const Expr *Arg = CE->getArg(0); 00574 SVal ArgVal = state->getSVal(Arg, C.getLocationContext()); 00575 Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); 00576 if (!DefArgVal) 00577 return; 00578 00579 // Get a NULL value. 00580 SValBuilder &svalBuilder = C.getSValBuilder(); 00581 DefinedSVal zero = 00582 svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>(); 00583 00584 // Make an expression asserting that they're equal. 00585 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); 00586 00587 // Are they equal? 00588 ProgramStateRef stateTrue, stateFalse; 00589 std::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); 00590 00591 if (stateTrue && !stateFalse) { 00592 ExplodedNode *N = C.generateSink(stateTrue); 00593 if (!N) 00594 return; 00595 00596 const char *description; 00597 if (FuncII == Retain) 00598 description = "Null pointer argument in call to CFRetain"; 00599 else if (FuncII == Release) 00600 description = "Null pointer argument in call to CFRelease"; 00601 else if (FuncII == MakeCollectable) 00602 description = "Null pointer argument in call to CFMakeCollectable"; 00603 else if (FuncII == Autorelease) 00604 description = "Null pointer argument in call to CFAutorelease"; 00605 else 00606 llvm_unreachable("impossible case"); 00607 00608 BugReport *report = new BugReport(*BT, description, N); 00609 report->addRange(Arg->getSourceRange()); 00610 bugreporter::trackNullOrUndefValue(N, Arg, *report); 00611 C.emitReport(report); 00612 return; 00613 } 00614 00615 // From here on, we know the argument is non-null. 00616 C.addTransition(stateFalse); 00617 } 00618 00619 //===----------------------------------------------------------------------===// 00620 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class. 00621 //===----------------------------------------------------------------------===// 00622 00623 namespace { 00624 class ClassReleaseChecker : public Checker<check::PreObjCMessage> { 00625 mutable Selector releaseS; 00626 mutable Selector retainS; 00627 mutable Selector autoreleaseS; 00628 mutable Selector drainS; 00629 mutable std::unique_ptr<BugType> BT; 00630 00631 public: 00632 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 00633 }; 00634 } 00635 00636 void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 00637 CheckerContext &C) const { 00638 00639 if (!BT) { 00640 BT.reset(new APIMisuse( 00641 this, "message incorrectly sent to class instead of class instance")); 00642 00643 ASTContext &Ctx = C.getASTContext(); 00644 releaseS = GetNullarySelector("release", Ctx); 00645 retainS = GetNullarySelector("retain", Ctx); 00646 autoreleaseS = GetNullarySelector("autorelease", Ctx); 00647 drainS = GetNullarySelector("drain", Ctx); 00648 } 00649 00650 if (msg.isInstanceMessage()) 00651 return; 00652 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 00653 assert(Class); 00654 00655 Selector S = msg.getSelector(); 00656 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) 00657 return; 00658 00659 if (ExplodedNode *N = C.addTransition()) { 00660 SmallString<200> buf; 00661 llvm::raw_svector_ostream os(buf); 00662 00663 os << "The '"; 00664 S.print(os); 00665 os << "' message should be sent to instances " 00666 "of class '" << Class->getName() 00667 << "' and not the class directly"; 00668 00669 BugReport *report = new BugReport(*BT, os.str(), N); 00670 report->addRange(msg.getSourceRange()); 00671 C.emitReport(report); 00672 } 00673 } 00674 00675 //===----------------------------------------------------------------------===// 00676 // Check for passing non-Objective-C types to variadic methods that expect 00677 // only Objective-C types. 00678 //===----------------------------------------------------------------------===// 00679 00680 namespace { 00681 class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> { 00682 mutable Selector arrayWithObjectsS; 00683 mutable Selector dictionaryWithObjectsAndKeysS; 00684 mutable Selector setWithObjectsS; 00685 mutable Selector orderedSetWithObjectsS; 00686 mutable Selector initWithObjectsS; 00687 mutable Selector initWithObjectsAndKeysS; 00688 mutable std::unique_ptr<BugType> BT; 00689 00690 bool isVariadicMessage(const ObjCMethodCall &msg) const; 00691 00692 public: 00693 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 00694 }; 00695 } 00696 00697 /// isVariadicMessage - Returns whether the given message is a variadic message, 00698 /// where all arguments must be Objective-C types. 00699 bool 00700 VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const { 00701 const ObjCMethodDecl *MD = msg.getDecl(); 00702 00703 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext())) 00704 return false; 00705 00706 Selector S = msg.getSelector(); 00707 00708 if (msg.isInstanceMessage()) { 00709 // FIXME: Ideally we'd look at the receiver interface here, but that's not 00710 // useful for init, because alloc returns 'id'. In theory, this could lead 00711 // to false positives, for example if there existed a class that had an 00712 // initWithObjects: implementation that does accept non-Objective-C pointer 00713 // types, but the chance of that happening is pretty small compared to the 00714 // gains that this analysis gives. 00715 const ObjCInterfaceDecl *Class = MD->getClassInterface(); 00716 00717 switch (findKnownClass(Class)) { 00718 case FC_NSArray: 00719 case FC_NSOrderedSet: 00720 case FC_NSSet: 00721 return S == initWithObjectsS; 00722 case FC_NSDictionary: 00723 return S == initWithObjectsAndKeysS; 00724 default: 00725 return false; 00726 } 00727 } else { 00728 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 00729 00730 switch (findKnownClass(Class)) { 00731 case FC_NSArray: 00732 return S == arrayWithObjectsS; 00733 case FC_NSOrderedSet: 00734 return S == orderedSetWithObjectsS; 00735 case FC_NSSet: 00736 return S == setWithObjectsS; 00737 case FC_NSDictionary: 00738 return S == dictionaryWithObjectsAndKeysS; 00739 default: 00740 return false; 00741 } 00742 } 00743 } 00744 00745 void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 00746 CheckerContext &C) const { 00747 if (!BT) { 00748 BT.reset(new APIMisuse(this, 00749 "Arguments passed to variadic method aren't all " 00750 "Objective-C pointer types")); 00751 00752 ASTContext &Ctx = C.getASTContext(); 00753 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); 00754 dictionaryWithObjectsAndKeysS = 00755 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); 00756 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); 00757 orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx); 00758 00759 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); 00760 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); 00761 } 00762 00763 if (!isVariadicMessage(msg)) 00764 return; 00765 00766 // We are not interested in the selector arguments since they have 00767 // well-defined types, so the compiler will issue a warning for them. 00768 unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); 00769 00770 // We're not interested in the last argument since it has to be nil or the 00771 // compiler would have issued a warning for it elsewhere. 00772 unsigned variadicArgsEnd = msg.getNumArgs() - 1; 00773 00774 if (variadicArgsEnd <= variadicArgsBegin) 00775 return; 00776 00777 // Verify that all arguments have Objective-C types. 00778 Optional<ExplodedNode*> errorNode; 00779 00780 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { 00781 QualType ArgTy = msg.getArgExpr(I)->getType(); 00782 if (ArgTy->isObjCObjectPointerType()) 00783 continue; 00784 00785 // Block pointers are treaded as Objective-C pointers. 00786 if (ArgTy->isBlockPointerType()) 00787 continue; 00788 00789 // Ignore pointer constants. 00790 if (msg.getArgSVal(I).getAs<loc::ConcreteInt>()) 00791 continue; 00792 00793 // Ignore pointer types annotated with 'NSObject' attribute. 00794 if (C.getASTContext().isObjCNSObjectType(ArgTy)) 00795 continue; 00796 00797 // Ignore CF references, which can be toll-free bridged. 00798 if (coreFoundation::isCFObjectRef(ArgTy)) 00799 continue; 00800 00801 // Generate only one error node to use for all bug reports. 00802 if (!errorNode.hasValue()) 00803 errorNode = C.addTransition(); 00804 00805 if (!errorNode.getValue()) 00806 continue; 00807 00808 SmallString<128> sbuf; 00809 llvm::raw_svector_ostream os(sbuf); 00810 00811 StringRef TypeName = GetReceiverInterfaceName(msg); 00812 if (!TypeName.empty()) 00813 os << "Argument to '" << TypeName << "' method '"; 00814 else 00815 os << "Argument to method '"; 00816 00817 msg.getSelector().print(os); 00818 os << "' should be an Objective-C pointer type, not '"; 00819 ArgTy.print(os, C.getLangOpts()); 00820 os << "'"; 00821 00822 BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue()); 00823 R->addRange(msg.getArgSourceRange(I)); 00824 C.emitReport(R); 00825 } 00826 } 00827 00828 //===----------------------------------------------------------------------===// 00829 // Improves the modeling of loops over Cocoa collections. 00830 //===----------------------------------------------------------------------===// 00831 00832 // The map from container symbol to the container count symbol. 00833 // We currently will remember the last countainer count symbol encountered. 00834 REGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap, SymbolRef, SymbolRef) 00835 REGISTER_MAP_WITH_PROGRAMSTATE(ContainerNonEmptyMap, SymbolRef, bool) 00836 00837 namespace { 00838 class ObjCLoopChecker 00839 : public Checker<check::PostStmt<ObjCForCollectionStmt>, 00840 check::PostObjCMessage, 00841 check::DeadSymbols, 00842 check::PointerEscape > { 00843 mutable IdentifierInfo *CountSelectorII; 00844 00845 bool isCollectionCountMethod(const ObjCMethodCall &M, 00846 CheckerContext &C) const; 00847 00848 public: 00849 ObjCLoopChecker() : CountSelectorII(nullptr) {} 00850 void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const; 00851 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 00852 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 00853 ProgramStateRef checkPointerEscape(ProgramStateRef State, 00854 const InvalidatedSymbols &Escaped, 00855 const CallEvent *Call, 00856 PointerEscapeKind Kind) const; 00857 }; 00858 } 00859 00860 static bool isKnownNonNilCollectionType(QualType T) { 00861 const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); 00862 if (!PT) 00863 return false; 00864 00865 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); 00866 if (!ID) 00867 return false; 00868 00869 switch (findKnownClass(ID)) { 00870 case FC_NSArray: 00871 case FC_NSDictionary: 00872 case FC_NSEnumerator: 00873 case FC_NSOrderedSet: 00874 case FC_NSSet: 00875 return true; 00876 default: 00877 return false; 00878 } 00879 } 00880 00881 /// Assumes that the collection is non-nil. 00882 /// 00883 /// If the collection is known to be nil, returns NULL to indicate an infeasible 00884 /// path. 00885 static ProgramStateRef checkCollectionNonNil(CheckerContext &C, 00886 ProgramStateRef State, 00887 const ObjCForCollectionStmt *FCS) { 00888 if (!State) 00889 return nullptr; 00890 00891 SVal CollectionVal = C.getSVal(FCS->getCollection()); 00892 Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>(); 00893 if (!KnownCollection) 00894 return State; 00895 00896 ProgramStateRef StNonNil, StNil; 00897 std::tie(StNonNil, StNil) = State->assume(*KnownCollection); 00898 if (StNil && !StNonNil) { 00899 // The collection is nil. This path is infeasible. 00900 return nullptr; 00901 } 00902 00903 return StNonNil; 00904 } 00905 00906 /// Assumes that the collection elements are non-nil. 00907 /// 00908 /// This only applies if the collection is one of those known not to contain 00909 /// nil values. 00910 static ProgramStateRef checkElementNonNil(CheckerContext &C, 00911 ProgramStateRef State, 00912 const ObjCForCollectionStmt *FCS) { 00913 if (!State) 00914 return nullptr; 00915 00916 // See if the collection is one where we /know/ the elements are non-nil. 00917 if (!isKnownNonNilCollectionType(FCS->getCollection()->getType())) 00918 return State; 00919 00920 const LocationContext *LCtx = C.getLocationContext(); 00921 const Stmt *Element = FCS->getElement(); 00922 00923 // FIXME: Copied from ExprEngineObjC. 00924 Optional<Loc> ElementLoc; 00925 if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { 00926 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); 00927 assert(ElemDecl->getInit() == nullptr); 00928 ElementLoc = State->getLValue(ElemDecl, LCtx); 00929 } else { 00930 ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>(); 00931 } 00932 00933 if (!ElementLoc) 00934 return State; 00935 00936 // Go ahead and assume the value is non-nil. 00937 SVal Val = State->getSVal(*ElementLoc); 00938 return State->assume(Val.castAs<DefinedOrUnknownSVal>(), true); 00939 } 00940 00941 /// Returns NULL state if the collection is known to contain elements 00942 /// (or is known not to contain elements if the Assumption parameter is false.) 00943 static ProgramStateRef 00944 assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, 00945 SymbolRef CollectionS, bool Assumption) { 00946 if (!State || !CollectionS) 00947 return State; 00948 00949 const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS); 00950 if (!CountS) { 00951 const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS); 00952 if (!KnownNonEmpty) 00953 return State->set<ContainerNonEmptyMap>(CollectionS, Assumption); 00954 return (Assumption == *KnownNonEmpty) ? State : nullptr; 00955 } 00956 00957 SValBuilder &SvalBuilder = C.getSValBuilder(); 00958 SVal CountGreaterThanZeroVal = 00959 SvalBuilder.evalBinOp(State, BO_GT, 00960 nonloc::SymbolVal(*CountS), 00961 SvalBuilder.makeIntVal(0, (*CountS)->getType()), 00962 SvalBuilder.getConditionType()); 00963 Optional<DefinedSVal> CountGreaterThanZero = 00964 CountGreaterThanZeroVal.getAs<DefinedSVal>(); 00965 if (!CountGreaterThanZero) { 00966 // The SValBuilder cannot construct a valid SVal for this condition. 00967 // This means we cannot properly reason about it. 00968 return State; 00969 } 00970 00971 return State->assume(*CountGreaterThanZero, Assumption); 00972 } 00973 00974 static ProgramStateRef 00975 assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, 00976 const ObjCForCollectionStmt *FCS, 00977 bool Assumption) { 00978 if (!State) 00979 return nullptr; 00980 00981 SymbolRef CollectionS = 00982 State->getSVal(FCS->getCollection(), C.getLocationContext()).getAsSymbol(); 00983 return assumeCollectionNonEmpty(C, State, CollectionS, Assumption); 00984 } 00985 00986 00987 /// If the fist block edge is a back edge, we are reentering the loop. 00988 static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N, 00989 const ObjCForCollectionStmt *FCS) { 00990 if (!N) 00991 return false; 00992 00993 ProgramPoint P = N->getLocation(); 00994 if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { 00995 if (BE->getSrc()->getLoopTarget() == FCS) 00996 return true; 00997 return false; 00998 } 00999 01000 // Keep looking for a block edge. 01001 for (ExplodedNode::const_pred_iterator I = N->pred_begin(), 01002 E = N->pred_end(); I != E; ++I) { 01003 if (alreadyExecutedAtLeastOneLoopIteration(*I, FCS)) 01004 return true; 01005 } 01006 01007 return false; 01008 } 01009 01010 void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, 01011 CheckerContext &C) const { 01012 ProgramStateRef State = C.getState(); 01013 01014 // Check if this is the branch for the end of the loop. 01015 SVal CollectionSentinel = C.getSVal(FCS); 01016 if (CollectionSentinel.isZeroConstant()) { 01017 if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS)) 01018 State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false); 01019 01020 // Otherwise, this is a branch that goes through the loop body. 01021 } else { 01022 State = checkCollectionNonNil(C, State, FCS); 01023 State = checkElementNonNil(C, State, FCS); 01024 State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true); 01025 } 01026 01027 if (!State) 01028 C.generateSink(); 01029 else if (State != C.getState()) 01030 C.addTransition(State); 01031 } 01032 01033 bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M, 01034 CheckerContext &C) const { 01035 Selector S = M.getSelector(); 01036 // Initialize the identifiers on first use. 01037 if (!CountSelectorII) 01038 CountSelectorII = &C.getASTContext().Idents.get("count"); 01039 01040 // If the method returns collection count, record the value. 01041 if (S.isUnarySelector() && 01042 (S.getIdentifierInfoForSlot(0) == CountSelectorII)) 01043 return true; 01044 01045 return false; 01046 } 01047 01048 void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M, 01049 CheckerContext &C) const { 01050 if (!M.isInstanceMessage()) 01051 return; 01052 01053 const ObjCInterfaceDecl *ClassID = M.getReceiverInterface(); 01054 if (!ClassID) 01055 return; 01056 01057 FoundationClass Class = findKnownClass(ClassID); 01058 if (Class != FC_NSDictionary && 01059 Class != FC_NSArray && 01060 Class != FC_NSSet && 01061 Class != FC_NSOrderedSet) 01062 return; 01063 01064 SymbolRef ContainerS = M.getReceiverSVal().getAsSymbol(); 01065 if (!ContainerS) 01066 return; 01067 01068 // If we are processing a call to "count", get the symbolic value returned by 01069 // a call to "count" and add it to the map. 01070 if (!isCollectionCountMethod(M, C)) 01071 return; 01072 01073 const Expr *MsgExpr = M.getOriginExpr(); 01074 SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol(); 01075 if (CountS) { 01076 ProgramStateRef State = C.getState(); 01077 01078 C.getSymbolManager().addSymbolDependency(ContainerS, CountS); 01079 State = State->set<ContainerCountMap>(ContainerS, CountS); 01080 01081 if (const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) { 01082 State = State->remove<ContainerNonEmptyMap>(ContainerS); 01083 State = assumeCollectionNonEmpty(C, State, ContainerS, *NonEmpty); 01084 } 01085 01086 C.addTransition(State); 01087 } 01088 return; 01089 } 01090 01091 static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call) { 01092 const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call); 01093 if (!Message) 01094 return nullptr; 01095 01096 const ObjCMethodDecl *MD = Message->getDecl(); 01097 if (!MD) 01098 return nullptr; 01099 01100 const ObjCInterfaceDecl *StaticClass; 01101 if (isa<ObjCProtocolDecl>(MD->getDeclContext())) { 01102 // We can't find out where the method was declared without doing more work. 01103 // Instead, see if the receiver is statically typed as a known immutable 01104 // collection. 01105 StaticClass = Message->getOriginExpr()->getReceiverInterface(); 01106 } else { 01107 StaticClass = MD->getClassInterface(); 01108 } 01109 01110 if (!StaticClass) 01111 return nullptr; 01112 01113 switch (findKnownClass(StaticClass, /*IncludeSuper=*/false)) { 01114 case FC_None: 01115 return nullptr; 01116 case FC_NSArray: 01117 case FC_NSDictionary: 01118 case FC_NSEnumerator: 01119 case FC_NSNull: 01120 case FC_NSOrderedSet: 01121 case FC_NSSet: 01122 case FC_NSString: 01123 break; 01124 } 01125 01126 return Message->getReceiverSVal().getAsSymbol(); 01127 } 01128 01129 ProgramStateRef 01130 ObjCLoopChecker::checkPointerEscape(ProgramStateRef State, 01131 const InvalidatedSymbols &Escaped, 01132 const CallEvent *Call, 01133 PointerEscapeKind Kind) const { 01134 SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call); 01135 01136 // Remove the invalidated symbols form the collection count map. 01137 for (InvalidatedSymbols::const_iterator I = Escaped.begin(), 01138 E = Escaped.end(); 01139 I != E; ++I) { 01140 SymbolRef Sym = *I; 01141 01142 // Don't invalidate this symbol's count if we know the method being called 01143 // is declared on an immutable class. This isn't completely correct if the 01144 // receiver is also passed as an argument, but in most uses of NSArray, 01145 // NSDictionary, etc. this isn't likely to happen in a dangerous way. 01146 if (Sym == ImmutableReceiver) 01147 continue; 01148 01149 // The symbol escaped. Pessimistically, assume that the count could have 01150 // changed. 01151 State = State->remove<ContainerCountMap>(Sym); 01152 State = State->remove<ContainerNonEmptyMap>(Sym); 01153 } 01154 return State; 01155 } 01156 01157 void ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper, 01158 CheckerContext &C) const { 01159 ProgramStateRef State = C.getState(); 01160 01161 // Remove the dead symbols from the collection count map. 01162 ContainerCountMapTy Tracked = State->get<ContainerCountMap>(); 01163 for (ContainerCountMapTy::iterator I = Tracked.begin(), 01164 E = Tracked.end(); I != E; ++I) { 01165 SymbolRef Sym = I->first; 01166 if (SymReaper.isDead(Sym)) { 01167 State = State->remove<ContainerCountMap>(Sym); 01168 State = State->remove<ContainerNonEmptyMap>(Sym); 01169 } 01170 } 01171 01172 C.addTransition(State); 01173 } 01174 01175 namespace { 01176 /// \class ObjCNonNilReturnValueChecker 01177 /// \brief The checker restricts the return values of APIs known to 01178 /// never (or almost never) return 'nil'. 01179 class ObjCNonNilReturnValueChecker 01180 : public Checker<check::PostObjCMessage, 01181 check::PostStmt<ObjCArrayLiteral>, 01182 check::PostStmt<ObjCDictionaryLiteral>, 01183 check::PostStmt<ObjCBoxedExpr> > { 01184 mutable bool Initialized; 01185 mutable Selector ObjectAtIndex; 01186 mutable Selector ObjectAtIndexedSubscript; 01187 mutable Selector NullSelector; 01188 01189 public: 01190 ObjCNonNilReturnValueChecker() : Initialized(false) {} 01191 01192 ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr, 01193 ProgramStateRef State, 01194 CheckerContext &C) const; 01195 void assumeExprIsNonNull(const Expr *E, CheckerContext &C) const { 01196 C.addTransition(assumeExprIsNonNull(E, C.getState(), C)); 01197 } 01198 01199 void checkPostStmt(const ObjCArrayLiteral *E, CheckerContext &C) const { 01200 assumeExprIsNonNull(E, C); 01201 } 01202 void checkPostStmt(const ObjCDictionaryLiteral *E, CheckerContext &C) const { 01203 assumeExprIsNonNull(E, C); 01204 } 01205 void checkPostStmt(const ObjCBoxedExpr *E, CheckerContext &C) const { 01206 assumeExprIsNonNull(E, C); 01207 } 01208 01209 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 01210 }; 01211 } 01212 01213 ProgramStateRef 01214 ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr, 01215 ProgramStateRef State, 01216 CheckerContext &C) const { 01217 SVal Val = State->getSVal(NonNullExpr, C.getLocationContext()); 01218 if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>()) 01219 return State->assume(*DV, true); 01220 return State; 01221 } 01222 01223 void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M, 01224 CheckerContext &C) 01225 const { 01226 ProgramStateRef State = C.getState(); 01227 01228 if (!Initialized) { 01229 ASTContext &Ctx = C.getASTContext(); 01230 ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx); 01231 ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx); 01232 NullSelector = GetNullarySelector("null", Ctx); 01233 } 01234 01235 // Check the receiver type. 01236 if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) { 01237 01238 // Assume that object returned from '[self init]' or '[super init]' is not 01239 // 'nil' if we are processing an inlined function/method. 01240 // 01241 // A defensive callee will (and should) check if the object returned by 01242 // '[super init]' is 'nil' before doing it's own initialization. However, 01243 // since 'nil' is rarely returned in practice, we should not warn when the 01244 // caller to the defensive constructor uses the object in contexts where 01245 // 'nil' is not accepted. 01246 if (!C.inTopFrame() && M.getDecl() && 01247 M.getDecl()->getMethodFamily() == OMF_init && 01248 M.isReceiverSelfOrSuper()) { 01249 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 01250 } 01251 01252 FoundationClass Cl = findKnownClass(Interface); 01253 01254 // Objects returned from 01255 // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript] 01256 // are never 'nil'. 01257 if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) { 01258 Selector Sel = M.getSelector(); 01259 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) { 01260 // Go ahead and assume the value is non-nil. 01261 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 01262 } 01263 } 01264 01265 // Objects returned from [NSNull null] are not nil. 01266 if (Cl == FC_NSNull) { 01267 if (M.getSelector() == NullSelector) { 01268 // Go ahead and assume the value is non-nil. 01269 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 01270 } 01271 } 01272 } 01273 C.addTransition(State); 01274 } 01275 01276 //===----------------------------------------------------------------------===// 01277 // Check registration. 01278 //===----------------------------------------------------------------------===// 01279 01280 void ento::registerNilArgChecker(CheckerManager &mgr) { 01281 mgr.registerChecker<NilArgChecker>(); 01282 } 01283 01284 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) { 01285 mgr.registerChecker<CFNumberCreateChecker>(); 01286 } 01287 01288 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { 01289 mgr.registerChecker<CFRetainReleaseChecker>(); 01290 } 01291 01292 void ento::registerClassReleaseChecker(CheckerManager &mgr) { 01293 mgr.registerChecker<ClassReleaseChecker>(); 01294 } 01295 01296 void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { 01297 mgr.registerChecker<VariadicMethodTypeChecker>(); 01298 } 01299 01300 void ento::registerObjCLoopChecker(CheckerManager &mgr) { 01301 mgr.registerChecker<ObjCLoopChecker>(); 01302 } 01303 01304 void 01305 ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) { 01306 mgr.registerChecker<ObjCNonNilReturnValueChecker>(); 01307 }