BasicObjCFoundationChecks.cpp revision 50e837b3cbc9315b6808daabb96c5c7cccf11ea7
1dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*-- 2dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block// 3dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block// The LLVM Compiler Infrastructure 4dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block// 5dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block// This file is distributed under the University of Illinois Open Source 6dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block// License. See LICENSE.TXT for details. 7dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block// 8dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block//===----------------------------------------------------------------------===// 9dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block// 10dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block// This file defines BasicObjCFoundationChecks, a class that encapsulates 11dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block// a set of simple checks to run on Objective-C code using Apple's Foundation 12dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block// classes. 13dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block// 14dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block//===----------------------------------------------------------------------===// 15dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 16dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "BasicObjCFoundationChecks.h" 17dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 18dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "clang/Analysis/PathSensitive/ExplodedGraph.h" 19dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h" 20dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "clang/Analysis/PathSensitive/GRExprEngine.h" 21dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "clang/Analysis/PathSensitive/GRState.h" 22dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "clang/Analysis/PathSensitive/BugReporter.h" 23dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "clang/Analysis/PathSensitive/MemRegion.h" 24dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "clang/Analysis/PathDiagnostic.h" 25dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "clang/Analysis/PathSensitive/CheckerVisitor.h" 26dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "clang/Analysis/LocalCheckers.h" 27dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "clang/AST/DeclObjC.h" 28dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "clang/AST/Expr.h" 29f486d19d62f1bc33246748b14b14a9dfa617b57fIain Merrick#include "clang/AST/ExprObjC.h" 30dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "clang/AST/ASTContext.h" 31dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#include "llvm/Support/Compiler.h" 32dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 33dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockusing namespace clang; 34dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 35dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockstatic const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) { 36f486d19d62f1bc33246748b14b14a9dfa617b57fIain Merrick const Expr* Receiver = ME->getReceiver(); 37dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 38dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block if (!Receiver) 39dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block return NULL; 40dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 41dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block if (const ObjCObjectPointerType *PT = 42dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block Receiver->getType()->getAs<ObjCObjectPointerType>()) 43dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block return PT->getInterfaceType(); 44dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 45dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block return NULL; 46dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block} 47dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 48dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockstatic const char* GetReceiverNameType(const ObjCMessageExpr* ME) { 49dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME)) 50dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block return ReceiverType->getDecl()->getIdentifier()->getNameStart(); 51dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block return NULL; 52dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block} 53dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 54dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blocknamespace { 55dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 56dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockclass VISIBILITY_HIDDEN APIMisuse : public BugType { 57dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockpublic: 58dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} 59f486d19d62f1bc33246748b14b14a9dfa617b57fIain Merrick}; 60dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 61dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockclass VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck { 626c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen APIMisuse *BT; 636c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen BugReporter& BR; 646c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen ASTContext &Ctx; 656c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen 666c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix); 676c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME); 686c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen 696c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen void Warn(ExplodedNode* N, const Expr* E, const std::string& s); 706c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen void WarnNilArg(ExplodedNode* N, const Expr* E); 716c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen 726c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen bool CheckNilArg(ExplodedNode* N, unsigned Arg); 736c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen 746c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsenpublic: 756c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br) 766c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen : BT(0), BR(br), Ctx(ctx) {} 776c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen 786c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen bool Audit(ExplodedNode* N, GRStateManager&); 79dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 806c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsenprivate: 816c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) { 826c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen std::string sbuf; 836c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen llvm::raw_string_ostream os(sbuf); 846c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen os << "Argument to '" << GetReceiverNameType(ME) << "' method '" 85dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block << ME->getSelector().getAsString() << "' cannot be nil."; 86dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 87dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block // Lazily create the BugType object for NilArg. This will be owned 88dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block // by the BugReporter object 'BR' once we call BR.EmitWarning. 89dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block if (!BT) BT = new APIMisuse("nil argument"); 90dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 916c2af9490927c3c5959b5cb07461b646f8b32f6cKristian Monsen RangedBugReport *R = new RangedBugReport(*BT, os.str().c_str(), N); 92dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block R->addRange(ME->getArg(Arg)->getSourceRange()); 93dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block BR.EmitReport(R); 94dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block } 95dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block}; 96dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block 97dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block} // end anonymous namespace 98 99 100GRSimpleAPICheck* 101clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) { 102 return new BasicObjCFoundationChecks(Ctx, BR); 103} 104 105 106 107bool BasicObjCFoundationChecks::Audit(ExplodedNode* N, 108 GRStateManager&) { 109 110 const ObjCMessageExpr* ME = 111 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt()); 112 113 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME); 114 115 if (!ReceiverType) 116 return false; 117 118 if (isNSString(ReceiverType, 119 ReceiverType->getDecl()->getIdentifier()->getName())) 120 return AuditNSString(N, ME); 121 122 return false; 123} 124 125static inline bool isNil(SVal X) { 126 return isa<loc::ConcreteInt>(X); 127} 128 129//===----------------------------------------------------------------------===// 130// Error reporting. 131//===----------------------------------------------------------------------===// 132 133bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) { 134 const ObjCMessageExpr* ME = 135 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt()); 136 137 const Expr * E = ME->getArg(Arg); 138 139 if (isNil(N->getState()->getSVal(E))) { 140 WarnNilArg(N, ME, Arg); 141 return true; 142 } 143 144 return false; 145} 146 147//===----------------------------------------------------------------------===// 148// NSString checking. 149//===----------------------------------------------------------------------===// 150 151bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T, 152 llvm::StringRef ClassName) { 153 return ClassName == "NSString" || ClassName == "NSMutableString"; 154} 155 156bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N, 157 const ObjCMessageExpr* ME) { 158 159 Selector S = ME->getSelector(); 160 161 if (S.isUnarySelector()) 162 return false; 163 164 // FIXME: This is going to be really slow doing these checks with 165 // lexical comparisons. 166 167 std::string name = S.getAsString(); 168 assert (!name.empty()); 169 const char* cstr = &name[0]; 170 unsigned len = name.size(); 171 172 switch (len) { 173 default: 174 break; 175 case 8: 176 if (!strcmp(cstr, "compare:")) 177 return CheckNilArg(N, 0); 178 179 break; 180 181 case 15: 182 // FIXME: Checking for initWithFormat: will not work in most cases 183 // yet because [NSString alloc] returns id, not NSString*. We will 184 // need support for tracking expected-type information in the analyzer 185 // to find these errors. 186 if (!strcmp(cstr, "initWithFormat:")) 187 return CheckNilArg(N, 0); 188 189 break; 190 191 case 16: 192 if (!strcmp(cstr, "compare:options:")) 193 return CheckNilArg(N, 0); 194 195 break; 196 197 case 22: 198 if (!strcmp(cstr, "compare:options:range:")) 199 return CheckNilArg(N, 0); 200 201 break; 202 203 case 23: 204 205 if (!strcmp(cstr, "caseInsensitiveCompare:")) 206 return CheckNilArg(N, 0); 207 208 break; 209 210 case 29: 211 if (!strcmp(cstr, "compare:options:range:locale:")) 212 return CheckNilArg(N, 0); 213 214 break; 215 216 case 37: 217 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:")) 218 return CheckNilArg(N, 0); 219 220 break; 221 } 222 223 return false; 224} 225 226//===----------------------------------------------------------------------===// 227// Error reporting. 228//===----------------------------------------------------------------------===// 229 230namespace { 231 232class VISIBILITY_HIDDEN AuditCFNumberCreate : public GRSimpleAPICheck { 233 APIMisuse* BT; 234 235 // FIXME: Either this should be refactored into GRSimpleAPICheck, or 236 // it should always be passed with a call to Audit. The latter 237 // approach makes this class more stateless. 238 ASTContext& Ctx; 239 IdentifierInfo* II; 240 BugReporter& BR; 241 242public: 243 AuditCFNumberCreate(ASTContext& ctx, BugReporter& br) 244 : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){} 245 246 ~AuditCFNumberCreate() {} 247 248 bool Audit(ExplodedNode* N, GRStateManager&); 249 250private: 251 void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N, 252 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); 253}; 254} // end anonymous namespace 255 256enum CFNumberType { 257 kCFNumberSInt8Type = 1, 258 kCFNumberSInt16Type = 2, 259 kCFNumberSInt32Type = 3, 260 kCFNumberSInt64Type = 4, 261 kCFNumberFloat32Type = 5, 262 kCFNumberFloat64Type = 6, 263 kCFNumberCharType = 7, 264 kCFNumberShortType = 8, 265 kCFNumberIntType = 9, 266 kCFNumberLongType = 10, 267 kCFNumberLongLongType = 11, 268 kCFNumberFloatType = 12, 269 kCFNumberDoubleType = 13, 270 kCFNumberCFIndexType = 14, 271 kCFNumberNSIntegerType = 15, 272 kCFNumberCGFloatType = 16 273}; 274 275namespace { 276 template<typename T> 277 class Optional { 278 bool IsKnown; 279 T Val; 280 public: 281 Optional() : IsKnown(false), Val(0) {} 282 Optional(const T& val) : IsKnown(true), Val(val) {} 283 284 bool isKnown() const { return IsKnown; } 285 286 const T& getValue() const { 287 assert (isKnown()); 288 return Val; 289 } 290 291 operator const T&() const { 292 return getValue(); 293 } 294 }; 295} 296 297static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) { 298 static unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; 299 300 if (i < kCFNumberCharType) 301 return FixedSize[i-1]; 302 303 QualType T; 304 305 switch (i) { 306 case kCFNumberCharType: T = Ctx.CharTy; break; 307 case kCFNumberShortType: T = Ctx.ShortTy; break; 308 case kCFNumberIntType: T = Ctx.IntTy; break; 309 case kCFNumberLongType: T = Ctx.LongTy; break; 310 case kCFNumberLongLongType: T = Ctx.LongLongTy; break; 311 case kCFNumberFloatType: T = Ctx.FloatTy; break; 312 case kCFNumberDoubleType: T = Ctx.DoubleTy; break; 313 case kCFNumberCFIndexType: 314 case kCFNumberNSIntegerType: 315 case kCFNumberCGFloatType: 316 // FIXME: We need a way to map from names to Type*. 317 default: 318 return Optional<uint64_t>(); 319 } 320 321 return Ctx.getTypeSize(T); 322} 323 324#if 0 325static const char* GetCFNumberTypeStr(uint64_t i) { 326 static const char* Names[] = { 327 "kCFNumberSInt8Type", 328 "kCFNumberSInt16Type", 329 "kCFNumberSInt32Type", 330 "kCFNumberSInt64Type", 331 "kCFNumberFloat32Type", 332 "kCFNumberFloat64Type", 333 "kCFNumberCharType", 334 "kCFNumberShortType", 335 "kCFNumberIntType", 336 "kCFNumberLongType", 337 "kCFNumberLongLongType", 338 "kCFNumberFloatType", 339 "kCFNumberDoubleType", 340 "kCFNumberCFIndexType", 341 "kCFNumberNSIntegerType", 342 "kCFNumberCGFloatType" 343 }; 344 345 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType"; 346} 347#endif 348 349bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){ 350 const CallExpr* CE = 351 cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt()); 352 const Expr* Callee = CE->getCallee(); 353 SVal CallV = N->getState()->getSVal(Callee); 354 const FunctionDecl* FD = CallV.getAsFunctionDecl(); 355 356 if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3) 357 return false; 358 359 // Get the value of the "theType" argument. 360 SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1)); 361 362 // FIXME: We really should allow ranges of valid theType values, and 363 // bifurcate the state appropriately. 364 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal); 365 366 if (!V) 367 return false; 368 369 uint64_t NumberKind = V->getValue().getLimitedValue(); 370 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind); 371 372 // FIXME: In some cases we can emit an error. 373 if (!TargetSize.isKnown()) 374 return false; 375 376 // Look at the value of the integer being passed by reference. Essentially 377 // we want to catch cases where the value passed in is not equal to the 378 // size of the type being created. 379 SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2)); 380 381 // FIXME: Eventually we should handle arbitrary locations. We can do this 382 // by having an enhanced memory model that does low-level typing. 383 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr); 384 385 if (!LV) 386 return false; 387 388 const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts()); 389 390 if (!R) 391 return false; 392 393 QualType T = Ctx.getCanonicalType(R->getValueType(Ctx)); 394 395 // FIXME: If the pointee isn't an integer type, should we flag a warning? 396 // People can do weird stuff with pointers. 397 398 if (!T->isIntegerType()) 399 return false; 400 401 uint64_t SourceSize = Ctx.getTypeSize(T); 402 403 // CHECK: is SourceSize == TargetSize 404 405 if (SourceSize == TargetSize) 406 return false; 407 408 AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind); 409 410 // FIXME: We can actually create an abstract "CFNumber" object that has 411 // the bits initialized to the provided values. 412 return SourceSize < TargetSize; 413} 414 415void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex, 416 ExplodedNode *N, 417 uint64_t SourceSize, uint64_t TargetSize, 418 uint64_t NumberKind) { 419 420 std::string sbuf; 421 llvm::raw_string_ostream os(sbuf); 422 423 os << (SourceSize == 8 ? "An " : "A ") 424 << SourceSize << " bit integer is used to initialize a CFNumber " 425 "object that represents " 426 << (TargetSize == 8 ? "an " : "a ") 427 << TargetSize << " bit integer. "; 428 429 if (SourceSize < TargetSize) 430 os << (TargetSize - SourceSize) 431 << " bits of the CFNumber value will be garbage." ; 432 else 433 os << (SourceSize - TargetSize) 434 << " bits of the input integer will be lost."; 435 436 // Lazily create the BugType object. This will be owned 437 // by the BugReporter object 'BR' once we call BR.EmitWarning. 438 if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate"); 439 RangedBugReport *report = new RangedBugReport(*BT, os.str().c_str(), N); 440 report->addRange(Ex->getSourceRange()); 441 BR.EmitReport(report); 442} 443 444GRSimpleAPICheck* 445clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) { 446 return new AuditCFNumberCreate(Ctx, BR); 447} 448 449//===----------------------------------------------------------------------===// 450// CFRetain/CFRelease auditing for null arguments. 451//===----------------------------------------------------------------------===// 452 453namespace { 454class VISIBILITY_HIDDEN AuditCFRetainRelease : public GRSimpleAPICheck { 455 APIMisuse *BT; 456 457 // FIXME: Either this should be refactored into GRSimpleAPICheck, or 458 // it should always be passed with a call to Audit. The latter 459 // approach makes this class more stateless. 460 ASTContext& Ctx; 461 IdentifierInfo *Retain, *Release; 462 BugReporter& BR; 463 464public: 465 AuditCFRetainRelease(ASTContext& ctx, BugReporter& br) 466 : BT(0), Ctx(ctx), 467 Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")), 468 BR(br){} 469 470 ~AuditCFRetainRelease() {} 471 472 bool Audit(ExplodedNode* N, GRStateManager&); 473}; 474} // end anonymous namespace 475 476 477bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) { 478 const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt()); 479 480 // If the CallExpr doesn't have exactly 1 argument just give up checking. 481 if (CE->getNumArgs() != 1) 482 return false; 483 484 // Check if we called CFRetain/CFRelease. 485 const GRState* state = N->getState(); 486 SVal X = state->getSVal(CE->getCallee()); 487 const FunctionDecl* FD = X.getAsFunctionDecl(); 488 489 if (!FD) 490 return false; 491 492 const IdentifierInfo *FuncII = FD->getIdentifier(); 493 if (!(FuncII == Retain || FuncII == Release)) 494 return false; 495 496 // Finally, check if the argument is NULL. 497 // FIXME: We should be able to bifurcate the state here, as a successful 498 // check will result in the value not being NULL afterwards. 499 // FIXME: Need a way to register vistors for the BugReporter. Would like 500 // to benefit from the same diagnostics that regular null dereference 501 // reporting has. 502 if (state->getStateManager().isEqual(state, CE->getArg(0), 0)) { 503 if (!BT) 504 BT = new APIMisuse("null passed to CFRetain/CFRelease"); 505 506 const char *description = (FuncII == Retain) 507 ? "Null pointer argument in call to CFRetain" 508 : "Null pointer argument in call to CFRelease"; 509 510 RangedBugReport *report = new RangedBugReport(*BT, description, N); 511 report->addRange(CE->getArg(0)->getSourceRange()); 512 BR.EmitReport(report); 513 return true; 514 } 515 516 return false; 517} 518 519 520GRSimpleAPICheck* 521clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) { 522 return new AuditCFRetainRelease(Ctx, BR); 523} 524 525//===----------------------------------------------------------------------===// 526// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. 527//===----------------------------------------------------------------------===// 528 529namespace { 530class VISIBILITY_HIDDEN ClassReleaseChecker : 531 public CheckerVisitor<ClassReleaseChecker> { 532 Selector releaseS; 533 Selector retainS; 534 Selector autoreleaseS; 535 Selector drainS; 536 BugType *BT; 537public: 538 ClassReleaseChecker(ASTContext &Ctx) 539 : releaseS(GetNullarySelector("release", Ctx)), 540 retainS(GetNullarySelector("retain", Ctx)), 541 autoreleaseS(GetNullarySelector("autorelease", Ctx)), 542 drainS(GetNullarySelector("drain", Ctx)), 543 BT(0) {} 544 545 static void *getTag() { static int x = 0; return &x; } 546 547 void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); 548}; 549} 550 551void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C, 552 const ObjCMessageExpr *ME) { 553 554 const IdentifierInfo *ClsName = ME->getClassName(); 555 if (!ClsName) 556 return; 557 558 Selector S = ME->getSelector(); 559 if (!(S == releaseS || S == retainS || S == autoreleaseS | S == drainS)) 560 return; 561 562 if (!BT) 563 BT = new APIMisuse("message incorrectly sent to class instead of class " 564 "instance"); 565 566 ExplodedNode *N = C.GenerateNode(ME, C.getState(), false); 567 if (!N) 568 return; 569 570 C.addTransition(N); 571 572 llvm::SmallString<200> buf; 573 llvm::raw_svector_ostream os(buf); 574 575 os << "The '" << S.getAsString() << "' message should be sent to instances " 576 "of class '" << ClsName->getName() 577 << "' and not the class directly"; 578 579 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); 580 report->addRange(ME->getSourceRange()); 581 C.EmitReport(report); 582} 583 584//===----------------------------------------------------------------------===// 585// Check registration. 586//===----------------------------------------------------------------------===// 587 588void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) { 589 ASTContext& Ctx = Eng.getContext(); 590 BugReporter &BR = Eng.getBugReporter(); 591 592 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR), 593 Stmt::ObjCMessageExprClass); 594 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass); 595 Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass); 596 597 RegisterNSErrorChecks(BR, Eng, D); 598 RegisterNSAutoreleasePoolChecks(Eng); 599 Eng.registerCheck(new ClassReleaseChecker(Ctx)); 600} 601