1 //== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines BasicObjCFoundationChecks, a class that encapsulates
11 // a set of simple checks to run on Objective-C code using Apple's Foundation
14 //===----------------------------------------------------------------------===//
16 #include "ClangSACheckers.h"
17 #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
18 #include "clang/StaticAnalyzer/Core/Checker.h"
19 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
24 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
25 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
27 #include "clang/AST/DeclObjC.h"
28 #include "clang/AST/Expr.h"
29 #include "clang/AST/ExprObjC.h"
30 #include "clang/AST/ASTContext.h"
32 using namespace clang;
36 class APIMisuse : public BugType {
38 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
40 } // end anonymous namespace
42 //===----------------------------------------------------------------------===//
44 //===----------------------------------------------------------------------===//
46 static const char* GetReceiverNameType(const ObjCMessage &msg) {
47 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
48 return ID->getIdentifier()->getNameStart();
52 static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID,
53 StringRef ClassName) {
54 if (ID->getIdentifier()->getName() == ClassName)
57 if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
58 return isReceiverClassOrSuperclass(Super, ClassName);
63 static inline bool isNil(SVal X) {
64 return isa<loc::ConcreteInt>(X);
67 //===----------------------------------------------------------------------===//
68 // NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
69 //===----------------------------------------------------------------------===//
72 class NilArgChecker : public Checker<check::PreObjCMessage> {
73 mutable llvm::OwningPtr<APIMisuse> BT;
75 void WarnNilArg(CheckerContext &C,
76 const ObjCMessage &msg, unsigned Arg) const;
79 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
83 void NilArgChecker::WarnNilArg(CheckerContext &C,
84 const ObjCMessage &msg,
85 unsigned int Arg) const
88 BT.reset(new APIMisuse("nil argument"));
90 if (ExplodedNode *N = C.generateSink()) {
91 llvm::SmallString<128> sbuf;
92 llvm::raw_svector_ostream os(sbuf);
93 os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
94 << msg.getSelector().getAsString() << "' cannot be nil";
96 BugReport *R = new BugReport(*BT, os.str(), N);
97 R->addRange(msg.getArgSourceRange(Arg));
102 void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
103 CheckerContext &C) const {
104 const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
108 if (isReceiverClassOrSuperclass(ID, "NSString")) {
109 Selector S = msg.getSelector();
111 if (S.isUnarySelector())
114 // FIXME: This is going to be really slow doing these checks with
115 // lexical comparisons.
117 std::string NameStr = S.getAsString();
118 StringRef Name(NameStr);
119 assert(!Name.empty());
121 // FIXME: Checking for initWithFormat: will not work in most cases
122 // yet because [NSString alloc] returns id, not NSString*. We will
123 // need support for tracking expected-type information in the analyzer
124 // to find these errors.
125 if (Name == "caseInsensitiveCompare:" ||
126 Name == "compare:" ||
127 Name == "compare:options:" ||
128 Name == "compare:options:range:" ||
129 Name == "compare:options:range:locale:" ||
130 Name == "componentsSeparatedByCharactersInSet:" ||
131 Name == "initWithFormat:") {
132 if (isNil(msg.getArgSVal(0, C.getState())))
133 WarnNilArg(C, msg, 0);
138 //===----------------------------------------------------------------------===//
140 //===----------------------------------------------------------------------===//
143 class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
144 mutable llvm::OwningPtr<APIMisuse> BT;
145 mutable IdentifierInfo* II;
147 CFNumberCreateChecker() : II(0) {}
149 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
152 void EmitError(const TypedRegion* R, const Expr *Ex,
153 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
155 } // end anonymous namespace
158 kCFNumberSInt8Type = 1,
159 kCFNumberSInt16Type = 2,
160 kCFNumberSInt32Type = 3,
161 kCFNumberSInt64Type = 4,
162 kCFNumberFloat32Type = 5,
163 kCFNumberFloat64Type = 6,
164 kCFNumberCharType = 7,
165 kCFNumberShortType = 8,
166 kCFNumberIntType = 9,
167 kCFNumberLongType = 10,
168 kCFNumberLongLongType = 11,
169 kCFNumberFloatType = 12,
170 kCFNumberDoubleType = 13,
171 kCFNumberCFIndexType = 14,
172 kCFNumberNSIntegerType = 15,
173 kCFNumberCGFloatType = 16
182 Optional() : IsKnown(false), Val(0) {}
183 Optional(const T& val) : IsKnown(true), Val(val) {}
185 bool isKnown() const { return IsKnown; }
187 const T& getValue() const {
192 operator const T&() const {
198 static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
199 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
201 if (i < kCFNumberCharType)
202 return FixedSize[i-1];
207 case kCFNumberCharType: T = Ctx.CharTy; break;
208 case kCFNumberShortType: T = Ctx.ShortTy; break;
209 case kCFNumberIntType: T = Ctx.IntTy; break;
210 case kCFNumberLongType: T = Ctx.LongTy; break;
211 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
212 case kCFNumberFloatType: T = Ctx.FloatTy; break;
213 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
214 case kCFNumberCFIndexType:
215 case kCFNumberNSIntegerType:
216 case kCFNumberCGFloatType:
217 // FIXME: We need a way to map from names to Type*.
219 return Optional<uint64_t>();
222 return Ctx.getTypeSize(T);
226 static const char* GetCFNumberTypeStr(uint64_t i) {
227 static const char* Names[] = {
228 "kCFNumberSInt8Type",
229 "kCFNumberSInt16Type",
230 "kCFNumberSInt32Type",
231 "kCFNumberSInt64Type",
232 "kCFNumberFloat32Type",
233 "kCFNumberFloat64Type",
235 "kCFNumberShortType",
238 "kCFNumberLongLongType",
239 "kCFNumberFloatType",
240 "kCFNumberDoubleType",
241 "kCFNumberCFIndexType",
242 "kCFNumberNSIntegerType",
243 "kCFNumberCGFloatType"
246 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
250 void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
251 CheckerContext &C) const {
252 const Expr *Callee = CE->getCallee();
253 const ProgramState *state = C.getState();
254 SVal CallV = state->getSVal(Callee);
255 const FunctionDecl *FD = CallV.getAsFunctionDecl();
260 ASTContext &Ctx = C.getASTContext();
262 II = &Ctx.Idents.get("CFNumberCreate");
264 if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
267 // Get the value of the "theType" argument.
268 SVal TheTypeVal = state->getSVal(CE->getArg(1));
270 // FIXME: We really should allow ranges of valid theType values, and
271 // bifurcate the state appropriately.
272 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
276 uint64_t NumberKind = V->getValue().getLimitedValue();
277 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
279 // FIXME: In some cases we can emit an error.
280 if (!TargetSize.isKnown())
283 // Look at the value of the integer being passed by reference. Essentially
284 // we want to catch cases where the value passed in is not equal to the
285 // size of the type being created.
286 SVal TheValueExpr = state->getSVal(CE->getArg(2));
288 // FIXME: Eventually we should handle arbitrary locations. We can do this
289 // by having an enhanced memory model that does low-level typing.
290 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
294 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
298 QualType T = Ctx.getCanonicalType(R->getValueType());
300 // FIXME: If the pointee isn't an integer type, should we flag a warning?
301 // People can do weird stuff with pointers.
303 if (!T->isIntegerType())
306 uint64_t SourceSize = Ctx.getTypeSize(T);
308 // CHECK: is SourceSize == TargetSize
309 if (SourceSize == TargetSize)
312 // Generate an error. Only generate a sink if 'SourceSize < TargetSize';
313 // otherwise generate a regular node.
315 // FIXME: We can actually create an abstract "CFNumber" object that has
316 // the bits initialized to the provided values.
318 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
319 : C.generateNode()) {
320 llvm::SmallString<128> sbuf;
321 llvm::raw_svector_ostream os(sbuf);
323 os << (SourceSize == 8 ? "An " : "A ")
324 << SourceSize << " bit integer is used to initialize a CFNumber "
325 "object that represents "
326 << (TargetSize == 8 ? "an " : "a ")
327 << TargetSize << " bit integer. ";
329 if (SourceSize < TargetSize)
330 os << (TargetSize - SourceSize)
331 << " bits of the CFNumber value will be garbage." ;
333 os << (SourceSize - TargetSize)
334 << " bits of the input integer will be lost.";
337 BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
339 BugReport *report = new BugReport(*BT, os.str(), N);
340 report->addRange(CE->getArg(2)->getSourceRange());
341 C.EmitReport(report);
345 //===----------------------------------------------------------------------===//
346 // CFRetain/CFRelease checking for null arguments.
347 //===----------------------------------------------------------------------===//
350 class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
351 mutable llvm::OwningPtr<APIMisuse> BT;
352 mutable IdentifierInfo *Retain, *Release;
354 CFRetainReleaseChecker(): Retain(0), Release(0) {}
355 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
357 } // end anonymous namespace
360 void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
361 CheckerContext &C) const {
362 // If the CallExpr doesn't have exactly 1 argument just give up checking.
363 if (CE->getNumArgs() != 1)
366 // Get the function declaration of the callee.
367 const ProgramState *state = C.getState();
368 SVal X = state->getSVal(CE->getCallee());
369 const FunctionDecl *FD = X.getAsFunctionDecl();
375 ASTContext &Ctx = C.getASTContext();
376 Retain = &Ctx.Idents.get("CFRetain");
377 Release = &Ctx.Idents.get("CFRelease");
378 BT.reset(new APIMisuse("null passed to CFRetain/CFRelease"));
381 // Check if we called CFRetain/CFRelease.
382 const IdentifierInfo *FuncII = FD->getIdentifier();
383 if (!(FuncII == Retain || FuncII == Release))
386 // FIXME: The rest of this just checks that the argument is non-null.
387 // It should probably be refactored and combined with AttrNonNullChecker.
389 // Get the argument's value.
390 const Expr *Arg = CE->getArg(0);
391 SVal ArgVal = state->getSVal(Arg);
392 DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
397 SValBuilder &svalBuilder = C.getSValBuilder();
398 DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
400 // Make an expression asserting that they're equal.
401 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
404 const ProgramState *stateTrue, *stateFalse;
405 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
407 if (stateTrue && !stateFalse) {
408 ExplodedNode *N = C.generateSink(stateTrue);
412 const char *description = (FuncII == Retain)
413 ? "Null pointer argument in call to CFRetain"
414 : "Null pointer argument in call to CFRelease";
416 BugReport *report = new BugReport(*BT, description, N);
417 report->addRange(Arg->getSourceRange());
418 report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg));
419 C.EmitReport(report);
423 // From here on, we know the argument is non-null.
424 C.addTransition(stateFalse);
427 //===----------------------------------------------------------------------===//
428 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
429 //===----------------------------------------------------------------------===//
432 class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
433 mutable Selector releaseS;
434 mutable Selector retainS;
435 mutable Selector autoreleaseS;
436 mutable Selector drainS;
437 mutable llvm::OwningPtr<BugType> BT;
440 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
444 void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
445 CheckerContext &C) const {
448 BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
451 ASTContext &Ctx = C.getASTContext();
452 releaseS = GetNullarySelector("release", Ctx);
453 retainS = GetNullarySelector("retain", Ctx);
454 autoreleaseS = GetNullarySelector("autorelease", Ctx);
455 drainS = GetNullarySelector("drain", Ctx);
458 if (msg.isInstanceMessage())
460 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
463 Selector S = msg.getSelector();
464 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
467 if (ExplodedNode *N = C.generateNode()) {
468 llvm::SmallString<200> buf;
469 llvm::raw_svector_ostream os(buf);
471 os << "The '" << S.getAsString() << "' message should be sent to instances "
472 "of class '" << Class->getName()
473 << "' and not the class directly";
475 BugReport *report = new BugReport(*BT, os.str(), N);
476 report->addRange(msg.getSourceRange());
477 C.EmitReport(report);
481 //===----------------------------------------------------------------------===//
482 // Check for passing non-Objective-C types to variadic methods that expect
483 // only Objective-C types.
484 //===----------------------------------------------------------------------===//
487 class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
488 mutable Selector arrayWithObjectsS;
489 mutable Selector dictionaryWithObjectsAndKeysS;
490 mutable Selector setWithObjectsS;
491 mutable Selector initWithObjectsS;
492 mutable Selector initWithObjectsAndKeysS;
493 mutable llvm::OwningPtr<BugType> BT;
495 bool isVariadicMessage(const ObjCMessage &msg) const;
498 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
502 /// isVariadicMessage - Returns whether the given message is a variadic message,
503 /// where all arguments must be Objective-C types.
505 VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const {
506 const ObjCMethodDecl *MD = msg.getMethodDecl();
508 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
511 Selector S = msg.getSelector();
513 if (msg.isInstanceMessage()) {
514 // FIXME: Ideally we'd look at the receiver interface here, but that's not
515 // useful for init, because alloc returns 'id'. In theory, this could lead
516 // to false positives, for example if there existed a class that had an
517 // initWithObjects: implementation that does accept non-Objective-C pointer
518 // types, but the chance of that happening is pretty small compared to the
519 // gains that this analysis gives.
520 const ObjCInterfaceDecl *Class = MD->getClassInterface();
522 // -[NSArray initWithObjects:]
523 if (isReceiverClassOrSuperclass(Class, "NSArray") &&
524 S == initWithObjectsS)
527 // -[NSDictionary initWithObjectsAndKeys:]
528 if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
529 S == initWithObjectsAndKeysS)
532 // -[NSSet initWithObjects:]
533 if (isReceiverClassOrSuperclass(Class, "NSSet") &&
534 S == initWithObjectsS)
537 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
539 // -[NSArray arrayWithObjects:]
540 if (isReceiverClassOrSuperclass(Class, "NSArray") &&
541 S == arrayWithObjectsS)
544 // -[NSDictionary dictionaryWithObjectsAndKeys:]
545 if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
546 S == dictionaryWithObjectsAndKeysS)
549 // -[NSSet setWithObjects:]
550 if (isReceiverClassOrSuperclass(Class, "NSSet") &&
551 S == setWithObjectsS)
558 void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
559 CheckerContext &C) const {
561 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
562 "Objective-C pointer types"));
564 ASTContext &Ctx = C.getASTContext();
565 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
566 dictionaryWithObjectsAndKeysS =
567 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
568 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
570 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
571 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
574 if (!isVariadicMessage(msg))
577 // We are not interested in the selector arguments since they have
578 // well-defined types, so the compiler will issue a warning for them.
579 unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
581 // We're not interested in the last argument since it has to be nil or the
582 // compiler would have issued a warning for it elsewhere.
583 unsigned variadicArgsEnd = msg.getNumArgs() - 1;
585 if (variadicArgsEnd <= variadicArgsBegin)
588 // Verify that all arguments have Objective-C types.
589 llvm::Optional<ExplodedNode*> errorNode;
590 const ProgramState *state = C.getState();
592 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
593 QualType ArgTy = msg.getArgType(I);
594 if (ArgTy->isObjCObjectPointerType())
597 // Block pointers are treaded as Objective-C pointers.
598 if (ArgTy->isBlockPointerType())
601 // Ignore pointer constants.
602 if (isa<loc::ConcreteInt>(msg.getArgSVal(I, state)))
605 // Ignore pointer types annotated with 'NSObject' attribute.
606 if (C.getASTContext().isObjCNSObjectType(ArgTy))
609 // Ignore CF references, which can be toll-free bridged.
610 if (coreFoundation::isCFObjectRef(ArgTy))
613 // Generate only one error node to use for all bug reports.
614 if (!errorNode.hasValue()) {
615 errorNode = C.generateNode();
618 if (!errorNode.getValue())
621 llvm::SmallString<128> sbuf;
622 llvm::raw_svector_ostream os(sbuf);
624 if (const char *TypeName = GetReceiverNameType(msg))
625 os << "Argument to '" << TypeName << "' method '";
627 os << "Argument to method '";
629 os << msg.getSelector().getAsString()
630 << "' should be an Objective-C pointer type, not '"
631 << ArgTy.getAsString() << "'";
633 BugReport *R = new BugReport(*BT, os.str(),
634 errorNode.getValue());
635 R->addRange(msg.getArgSourceRange(I));
640 //===----------------------------------------------------------------------===//
641 // Check registration.
642 //===----------------------------------------------------------------------===//
644 void ento::registerNilArgChecker(CheckerManager &mgr) {
645 mgr.registerChecker<NilArgChecker>();
648 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
649 mgr.registerChecker<CFNumberCreateChecker>();
652 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
653 mgr.registerChecker<CFRetainReleaseChecker>();
656 void ento::registerClassReleaseChecker(CheckerManager &mgr) {
657 mgr.registerChecker<ClassReleaseChecker>();
660 void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
661 mgr.registerChecker<VariadicMethodTypeChecker>();