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/GRState.h"
24 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
25 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
26 #include "clang/AST/DeclObjC.h"
27 #include "clang/AST/Expr.h"
28 #include "clang/AST/ExprObjC.h"
29 #include "clang/AST/ASTContext.h"
31 using namespace clang;
35 class APIMisuse : public BugType {
37 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
39 } // end anonymous namespace
41 //===----------------------------------------------------------------------===//
43 //===----------------------------------------------------------------------===//
45 static const char* GetReceiverNameType(const ObjCMessage &msg) {
46 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
47 return ID->getIdentifier()->getNameStart();
51 static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID,
52 llvm::StringRef ClassName) {
53 if (ID->getIdentifier()->getName() == ClassName)
56 if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
57 return isReceiverClassOrSuperclass(Super, ClassName);
62 static inline bool isNil(SVal X) {
63 return isa<loc::ConcreteInt>(X);
66 //===----------------------------------------------------------------------===//
67 // NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
68 //===----------------------------------------------------------------------===//
71 class NilArgChecker : public Checker<check::PreObjCMessage> {
72 mutable llvm::OwningPtr<APIMisuse> BT;
74 void WarnNilArg(CheckerContext &C,
75 const ObjCMessage &msg, unsigned Arg) const;
78 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
82 void NilArgChecker::WarnNilArg(CheckerContext &C,
83 const ObjCMessage &msg,
84 unsigned int Arg) const
87 BT.reset(new APIMisuse("nil argument"));
89 if (ExplodedNode *N = C.generateSink()) {
90 llvm::SmallString<128> sbuf;
91 llvm::raw_svector_ostream os(sbuf);
92 os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
93 << msg.getSelector().getAsString() << "' cannot be nil";
95 RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
96 R->addRange(msg.getArgSourceRange(Arg));
101 void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
102 CheckerContext &C) const {
103 const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
107 if (isReceiverClassOrSuperclass(ID, "NSString")) {
108 Selector S = msg.getSelector();
110 if (S.isUnarySelector())
113 // FIXME: This is going to be really slow doing these checks with
114 // lexical comparisons.
116 std::string NameStr = S.getAsString();
117 llvm::StringRef Name(NameStr);
118 assert(!Name.empty());
120 // FIXME: Checking for initWithFormat: will not work in most cases
121 // yet because [NSString alloc] returns id, not NSString*. We will
122 // need support for tracking expected-type information in the analyzer
123 // to find these errors.
124 if (Name == "caseInsensitiveCompare:" ||
125 Name == "compare:" ||
126 Name == "compare:options:" ||
127 Name == "compare:options:range:" ||
128 Name == "compare:options:range:locale:" ||
129 Name == "componentsSeparatedByCharactersInSet:" ||
130 Name == "initWithFormat:") {
131 if (isNil(msg.getArgSVal(0, C.getState())))
132 WarnNilArg(C, msg, 0);
137 //===----------------------------------------------------------------------===//
139 //===----------------------------------------------------------------------===//
142 class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
143 mutable llvm::OwningPtr<APIMisuse> BT;
144 mutable IdentifierInfo* II;
146 CFNumberCreateChecker() : II(0) {}
148 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
151 void EmitError(const TypedRegion* R, const Expr* Ex,
152 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
154 } // end anonymous namespace
157 kCFNumberSInt8Type = 1,
158 kCFNumberSInt16Type = 2,
159 kCFNumberSInt32Type = 3,
160 kCFNumberSInt64Type = 4,
161 kCFNumberFloat32Type = 5,
162 kCFNumberFloat64Type = 6,
163 kCFNumberCharType = 7,
164 kCFNumberShortType = 8,
165 kCFNumberIntType = 9,
166 kCFNumberLongType = 10,
167 kCFNumberLongLongType = 11,
168 kCFNumberFloatType = 12,
169 kCFNumberDoubleType = 13,
170 kCFNumberCFIndexType = 14,
171 kCFNumberNSIntegerType = 15,
172 kCFNumberCGFloatType = 16
181 Optional() : IsKnown(false), Val(0) {}
182 Optional(const T& val) : IsKnown(true), Val(val) {}
184 bool isKnown() const { return IsKnown; }
186 const T& getValue() const {
191 operator const T&() const {
197 static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
198 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
200 if (i < kCFNumberCharType)
201 return FixedSize[i-1];
206 case kCFNumberCharType: T = Ctx.CharTy; break;
207 case kCFNumberShortType: T = Ctx.ShortTy; break;
208 case kCFNumberIntType: T = Ctx.IntTy; break;
209 case kCFNumberLongType: T = Ctx.LongTy; break;
210 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
211 case kCFNumberFloatType: T = Ctx.FloatTy; break;
212 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
213 case kCFNumberCFIndexType:
214 case kCFNumberNSIntegerType:
215 case kCFNumberCGFloatType:
216 // FIXME: We need a way to map from names to Type*.
218 return Optional<uint64_t>();
221 return Ctx.getTypeSize(T);
225 static const char* GetCFNumberTypeStr(uint64_t i) {
226 static const char* Names[] = {
227 "kCFNumberSInt8Type",
228 "kCFNumberSInt16Type",
229 "kCFNumberSInt32Type",
230 "kCFNumberSInt64Type",
231 "kCFNumberFloat32Type",
232 "kCFNumberFloat64Type",
234 "kCFNumberShortType",
237 "kCFNumberLongLongType",
238 "kCFNumberFloatType",
239 "kCFNumberDoubleType",
240 "kCFNumberCFIndexType",
241 "kCFNumberNSIntegerType",
242 "kCFNumberCGFloatType"
245 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
249 void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
250 CheckerContext &C) const {
251 const Expr* Callee = CE->getCallee();
252 const GRState *state = C.getState();
253 SVal CallV = state->getSVal(Callee);
254 const FunctionDecl* FD = CallV.getAsFunctionDecl();
259 ASTContext &Ctx = C.getASTContext();
261 II = &Ctx.Idents.get("CFNumberCreate");
263 if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
266 // Get the value of the "theType" argument.
267 SVal TheTypeVal = state->getSVal(CE->getArg(1));
269 // FIXME: We really should allow ranges of valid theType values, and
270 // bifurcate the state appropriately.
271 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
275 uint64_t NumberKind = V->getValue().getLimitedValue();
276 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
278 // FIXME: In some cases we can emit an error.
279 if (!TargetSize.isKnown())
282 // Look at the value of the integer being passed by reference. Essentially
283 // we want to catch cases where the value passed in is not equal to the
284 // size of the type being created.
285 SVal TheValueExpr = state->getSVal(CE->getArg(2));
287 // FIXME: Eventually we should handle arbitrary locations. We can do this
288 // by having an enhanced memory model that does low-level typing.
289 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
293 const TypedRegion* R = dyn_cast<TypedRegion>(LV->stripCasts());
297 QualType T = Ctx.getCanonicalType(R->getValueType());
299 // FIXME: If the pointee isn't an integer type, should we flag a warning?
300 // People can do weird stuff with pointers.
302 if (!T->isIntegerType())
305 uint64_t SourceSize = Ctx.getTypeSize(T);
307 // CHECK: is SourceSize == TargetSize
308 if (SourceSize == TargetSize)
311 // Generate an error. Only generate a sink if 'SourceSize < TargetSize';
312 // otherwise generate a regular node.
314 // FIXME: We can actually create an abstract "CFNumber" object that has
315 // the bits initialized to the provided values.
317 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
318 : C.generateNode()) {
319 llvm::SmallString<128> sbuf;
320 llvm::raw_svector_ostream os(sbuf);
322 os << (SourceSize == 8 ? "An " : "A ")
323 << SourceSize << " bit integer is used to initialize a CFNumber "
324 "object that represents "
325 << (TargetSize == 8 ? "an " : "a ")
326 << TargetSize << " bit integer. ";
328 if (SourceSize < TargetSize)
329 os << (TargetSize - SourceSize)
330 << " bits of the CFNumber value will be garbage." ;
332 os << (SourceSize - TargetSize)
333 << " bits of the input integer will be lost.";
336 BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
338 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
339 report->addRange(CE->getArg(2)->getSourceRange());
340 C.EmitReport(report);
344 //===----------------------------------------------------------------------===//
345 // CFRetain/CFRelease checking for null arguments.
346 //===----------------------------------------------------------------------===//
349 class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
350 mutable llvm::OwningPtr<APIMisuse> BT;
351 mutable IdentifierInfo *Retain, *Release;
353 CFRetainReleaseChecker(): Retain(0), Release(0) {}
354 void checkPreStmt(const CallExpr* CE, CheckerContext& C) const;
356 } // end anonymous namespace
359 void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE,
360 CheckerContext& C) const {
361 // If the CallExpr doesn't have exactly 1 argument just give up checking.
362 if (CE->getNumArgs() != 1)
365 // Get the function declaration of the callee.
366 const GRState* state = C.getState();
367 SVal X = state->getSVal(CE->getCallee());
368 const FunctionDecl* FD = X.getAsFunctionDecl();
374 ASTContext &Ctx = C.getASTContext();
375 Retain = &Ctx.Idents.get("CFRetain");
376 Release = &Ctx.Idents.get("CFRelease");
377 BT.reset(new APIMisuse("null passed to CFRetain/CFRelease"));
380 // Check if we called CFRetain/CFRelease.
381 const IdentifierInfo *FuncII = FD->getIdentifier();
382 if (!(FuncII == Retain || FuncII == Release))
385 // FIXME: The rest of this just checks that the argument is non-null.
386 // It should probably be refactored and combined with AttrNonNullChecker.
388 // Get the argument's value.
389 const Expr *Arg = CE->getArg(0);
390 SVal ArgVal = state->getSVal(Arg);
391 DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
396 SValBuilder &svalBuilder = C.getSValBuilder();
397 DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
399 // Make an expression asserting that they're equal.
400 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
403 const GRState *stateTrue, *stateFalse;
404 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
406 if (stateTrue && !stateFalse) {
407 ExplodedNode *N = C.generateSink(stateTrue);
411 const char *description = (FuncII == Retain)
412 ? "Null pointer argument in call to CFRetain"
413 : "Null pointer argument in call to CFRelease";
415 EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N);
416 report->addRange(Arg->getSourceRange());
417 report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg);
418 C.EmitReport(report);
422 // From here on, we know the argument is non-null.
423 C.addTransition(stateFalse);
426 //===----------------------------------------------------------------------===//
427 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
428 //===----------------------------------------------------------------------===//
431 class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
432 mutable Selector releaseS;
433 mutable Selector retainS;
434 mutable Selector autoreleaseS;
435 mutable Selector drainS;
436 mutable llvm::OwningPtr<BugType> BT;
439 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
443 void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
444 CheckerContext &C) const {
447 BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
450 ASTContext &Ctx = C.getASTContext();
451 releaseS = GetNullarySelector("release", Ctx);
452 retainS = GetNullarySelector("retain", Ctx);
453 autoreleaseS = GetNullarySelector("autorelease", Ctx);
454 drainS = GetNullarySelector("drain", Ctx);
457 if (msg.isInstanceMessage())
459 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
462 Selector S = msg.getSelector();
463 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
466 if (ExplodedNode *N = C.generateNode()) {
467 llvm::SmallString<200> buf;
468 llvm::raw_svector_ostream os(buf);
470 os << "The '" << S.getAsString() << "' message should be sent to instances "
471 "of class '" << Class->getName()
472 << "' and not the class directly";
474 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
475 report->addRange(msg.getSourceRange());
476 C.EmitReport(report);
480 //===----------------------------------------------------------------------===//
481 // Check for passing non-Objective-C types to variadic methods that expect
482 // only Objective-C types.
483 //===----------------------------------------------------------------------===//
486 class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
487 mutable Selector arrayWithObjectsS;
488 mutable Selector dictionaryWithObjectsAndKeysS;
489 mutable Selector setWithObjectsS;
490 mutable Selector initWithObjectsS;
491 mutable Selector initWithObjectsAndKeysS;
492 mutable llvm::OwningPtr<BugType> BT;
494 bool isVariadicMessage(const ObjCMessage &msg) const;
497 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
501 /// isVariadicMessage - Returns whether the given message is a variadic message,
502 /// where all arguments must be Objective-C types.
504 VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const {
505 const ObjCMethodDecl *MD = msg.getMethodDecl();
507 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
510 Selector S = msg.getSelector();
512 if (msg.isInstanceMessage()) {
513 // FIXME: Ideally we'd look at the receiver interface here, but that's not
514 // useful for init, because alloc returns 'id'. In theory, this could lead
515 // to false positives, for example if there existed a class that had an
516 // initWithObjects: implementation that does accept non-Objective-C pointer
517 // types, but the chance of that happening is pretty small compared to the
518 // gains that this analysis gives.
519 const ObjCInterfaceDecl *Class = MD->getClassInterface();
521 // -[NSArray initWithObjects:]
522 if (isReceiverClassOrSuperclass(Class, "NSArray") &&
523 S == initWithObjectsS)
526 // -[NSDictionary initWithObjectsAndKeys:]
527 if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
528 S == initWithObjectsAndKeysS)
531 // -[NSSet initWithObjects:]
532 if (isReceiverClassOrSuperclass(Class, "NSSet") &&
533 S == initWithObjectsS)
536 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
538 // -[NSArray arrayWithObjects:]
539 if (isReceiverClassOrSuperclass(Class, "NSArray") &&
540 S == arrayWithObjectsS)
543 // -[NSDictionary dictionaryWithObjectsAndKeys:]
544 if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
545 S == dictionaryWithObjectsAndKeysS)
548 // -[NSSet setWithObjects:]
549 if (isReceiverClassOrSuperclass(Class, "NSSet") &&
550 S == setWithObjectsS)
557 void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
558 CheckerContext &C) const {
560 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
561 "Objective-C pointer types"));
563 ASTContext &Ctx = C.getASTContext();
564 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
565 dictionaryWithObjectsAndKeysS =
566 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
567 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
569 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
570 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
573 if (!isVariadicMessage(msg))
576 // We are not interested in the selector arguments since they have
577 // well-defined types, so the compiler will issue a warning for them.
578 unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
580 // We're not interested in the last argument since it has to be nil or the
581 // compiler would have issued a warning for it elsewhere.
582 unsigned variadicArgsEnd = msg.getNumArgs() - 1;
584 if (variadicArgsEnd <= variadicArgsBegin)
587 // Verify that all arguments have Objective-C types.
588 llvm::Optional<ExplodedNode*> errorNode;
589 const GRState *state = C.getState();
591 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
592 QualType ArgTy = msg.getArgType(I);
593 if (ArgTy->isObjCObjectPointerType())
596 // Block pointers are treaded as Objective-C pointers.
597 if (ArgTy->isBlockPointerType())
600 // Ignore pointer constants.
601 if (isa<loc::ConcreteInt>(msg.getArgSVal(I, state)))
604 // Ignore pointer types annotated with 'NSObject' attribute.
605 if (C.getASTContext().isObjCNSObjectType(ArgTy))
608 // Ignore CF references, which can be toll-free bridged.
609 if (coreFoundation::isCFObjectRef(ArgTy))
612 // Generate only one error node to use for all bug reports.
613 if (!errorNode.hasValue()) {
614 errorNode = C.generateNode();
617 if (!errorNode.getValue())
620 llvm::SmallString<128> sbuf;
621 llvm::raw_svector_ostream os(sbuf);
623 if (const char *TypeName = GetReceiverNameType(msg))
624 os << "Argument to '" << TypeName << "' method '";
626 os << "Argument to method '";
628 os << msg.getSelector().getAsString()
629 << "' should be an Objective-C pointer type, not '"
630 << ArgTy.getAsString() << "'";
632 RangedBugReport *R = new RangedBugReport(*BT, os.str(),
633 errorNode.getValue());
634 R->addRange(msg.getArgSourceRange(I));
639 //===----------------------------------------------------------------------===//
640 // Check registration.
641 //===----------------------------------------------------------------------===//
643 void ento::registerNilArgChecker(CheckerManager &mgr) {
644 mgr.registerChecker<NilArgChecker>();
647 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
648 mgr.registerChecker<CFNumberCreateChecker>();
651 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
652 mgr.registerChecker<CFRetainReleaseChecker>();
655 void ento::registerClassReleaseChecker(CheckerManager &mgr) {
656 mgr.registerChecker<ClassReleaseChecker>();
659 void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
660 mgr.registerChecker<VariadicMethodTypeChecker>();