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"
31 #include "llvm/ADT/SmallString.h"
33 using namespace clang;
37 class APIMisuse : public BugType {
39 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
41 } // end anonymous namespace
43 //===----------------------------------------------------------------------===//
45 //===----------------------------------------------------------------------===//
47 static const char* GetReceiverNameType(const ObjCMessage &msg) {
48 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
49 return ID->getIdentifier()->getNameStart();
53 static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID,
54 StringRef ClassName) {
55 if (ID->getIdentifier()->getName() == ClassName)
58 if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
59 return isReceiverClassOrSuperclass(Super, ClassName);
64 static inline bool isNil(SVal X) {
65 return isa<loc::ConcreteInt>(X);
68 //===----------------------------------------------------------------------===//
69 // NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
70 //===----------------------------------------------------------------------===//
73 class NilArgChecker : public Checker<check::PreObjCMessage> {
74 mutable OwningPtr<APIMisuse> BT;
76 void WarnNilArg(CheckerContext &C,
77 const ObjCMessage &msg, unsigned Arg) const;
80 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
84 void NilArgChecker::WarnNilArg(CheckerContext &C,
85 const ObjCMessage &msg,
86 unsigned int Arg) const
89 BT.reset(new APIMisuse("nil argument"));
91 if (ExplodedNode *N = C.generateSink()) {
92 SmallString<128> sbuf;
93 llvm::raw_svector_ostream os(sbuf);
94 os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
95 << msg.getSelector().getAsString() << "' cannot be nil";
97 BugReport *R = new BugReport(*BT, os.str(), N);
98 R->addRange(msg.getArgSourceRange(Arg));
103 void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
104 CheckerContext &C) const {
105 const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
109 if (isReceiverClassOrSuperclass(ID, "NSString")) {
110 Selector S = msg.getSelector();
112 if (S.isUnarySelector())
115 // FIXME: This is going to be really slow doing these checks with
116 // lexical comparisons.
118 std::string NameStr = S.getAsString();
119 StringRef Name(NameStr);
120 assert(!Name.empty());
122 // FIXME: Checking for initWithFormat: will not work in most cases
123 // yet because [NSString alloc] returns id, not NSString*. We will
124 // need support for tracking expected-type information in the analyzer
125 // to find these errors.
126 if (Name == "caseInsensitiveCompare:" ||
127 Name == "compare:" ||
128 Name == "compare:options:" ||
129 Name == "compare:options:range:" ||
130 Name == "compare:options:range:locale:" ||
131 Name == "componentsSeparatedByCharactersInSet:" ||
132 Name == "initWithFormat:") {
133 if (isNil(msg.getArgSVal(0, C.getLocationContext(), C.getState())))
134 WarnNilArg(C, msg, 0);
139 //===----------------------------------------------------------------------===//
141 //===----------------------------------------------------------------------===//
144 class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
145 mutable OwningPtr<APIMisuse> BT;
146 mutable IdentifierInfo* II;
148 CFNumberCreateChecker() : II(0) {}
150 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
153 void EmitError(const TypedRegion* R, const Expr *Ex,
154 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
156 } // end anonymous namespace
159 kCFNumberSInt8Type = 1,
160 kCFNumberSInt16Type = 2,
161 kCFNumberSInt32Type = 3,
162 kCFNumberSInt64Type = 4,
163 kCFNumberFloat32Type = 5,
164 kCFNumberFloat64Type = 6,
165 kCFNumberCharType = 7,
166 kCFNumberShortType = 8,
167 kCFNumberIntType = 9,
168 kCFNumberLongType = 10,
169 kCFNumberLongLongType = 11,
170 kCFNumberFloatType = 12,
171 kCFNumberDoubleType = 13,
172 kCFNumberCFIndexType = 14,
173 kCFNumberNSIntegerType = 15,
174 kCFNumberCGFloatType = 16
183 Optional() : IsKnown(false), Val(0) {}
184 Optional(const T& val) : IsKnown(true), Val(val) {}
186 bool isKnown() const { return IsKnown; }
188 const T& getValue() const {
193 operator const T&() const {
199 static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
200 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
202 if (i < kCFNumberCharType)
203 return FixedSize[i-1];
208 case kCFNumberCharType: T = Ctx.CharTy; break;
209 case kCFNumberShortType: T = Ctx.ShortTy; break;
210 case kCFNumberIntType: T = Ctx.IntTy; break;
211 case kCFNumberLongType: T = Ctx.LongTy; break;
212 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
213 case kCFNumberFloatType: T = Ctx.FloatTy; break;
214 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
215 case kCFNumberCFIndexType:
216 case kCFNumberNSIntegerType:
217 case kCFNumberCGFloatType:
218 // FIXME: We need a way to map from names to Type*.
220 return Optional<uint64_t>();
223 return Ctx.getTypeSize(T);
227 static const char* GetCFNumberTypeStr(uint64_t i) {
228 static const char* Names[] = {
229 "kCFNumberSInt8Type",
230 "kCFNumberSInt16Type",
231 "kCFNumberSInt32Type",
232 "kCFNumberSInt64Type",
233 "kCFNumberFloat32Type",
234 "kCFNumberFloat64Type",
236 "kCFNumberShortType",
239 "kCFNumberLongLongType",
240 "kCFNumberFloatType",
241 "kCFNumberDoubleType",
242 "kCFNumberCFIndexType",
243 "kCFNumberNSIntegerType",
244 "kCFNumberCGFloatType"
247 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
251 void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
252 CheckerContext &C) const {
253 ProgramStateRef state = C.getState();
254 const FunctionDecl *FD = C.getCalleeDecl(CE);
258 ASTContext &Ctx = C.getASTContext();
260 II = &Ctx.Idents.get("CFNumberCreate");
262 if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
265 // Get the value of the "theType" argument.
266 const LocationContext *LCtx = C.getLocationContext();
267 SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx);
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), LCtx);
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 TypedValueRegion* R = dyn_cast<TypedValueRegion>(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.addTransition()) {
319 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 BugReport *report = new BugReport(*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 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 ProgramStateRef state = C.getState();
366 const FunctionDecl *FD = C.getCalleeDecl(CE);
371 ASTContext &Ctx = C.getASTContext();
372 Retain = &Ctx.Idents.get("CFRetain");
373 Release = &Ctx.Idents.get("CFRelease");
374 BT.reset(new APIMisuse("null passed to CFRetain/CFRelease"));
377 // Check if we called CFRetain/CFRelease.
378 const IdentifierInfo *FuncII = FD->getIdentifier();
379 if (!(FuncII == Retain || FuncII == Release))
382 // FIXME: The rest of this just checks that the argument is non-null.
383 // It should probably be refactored and combined with AttrNonNullChecker.
385 // Get the argument's value.
386 const Expr *Arg = CE->getArg(0);
387 SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
388 DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
393 SValBuilder &svalBuilder = C.getSValBuilder();
394 DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
396 // Make an expression asserting that they're equal.
397 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
400 ProgramStateRef stateTrue, stateFalse;
401 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
403 if (stateTrue && !stateFalse) {
404 ExplodedNode *N = C.generateSink(stateTrue);
408 const char *description = (FuncII == Retain)
409 ? "Null pointer argument in call to CFRetain"
410 : "Null pointer argument in call to CFRelease";
412 BugReport *report = new BugReport(*BT, description, N);
413 report->addRange(Arg->getSourceRange());
414 report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg,
416 C.EmitReport(report);
420 // From here on, we know the argument is non-null.
421 C.addTransition(stateFalse);
424 //===----------------------------------------------------------------------===//
425 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
426 //===----------------------------------------------------------------------===//
429 class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
430 mutable Selector releaseS;
431 mutable Selector retainS;
432 mutable Selector autoreleaseS;
433 mutable Selector drainS;
434 mutable OwningPtr<BugType> BT;
437 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
441 void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
442 CheckerContext &C) const {
445 BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
448 ASTContext &Ctx = C.getASTContext();
449 releaseS = GetNullarySelector("release", Ctx);
450 retainS = GetNullarySelector("retain", Ctx);
451 autoreleaseS = GetNullarySelector("autorelease", Ctx);
452 drainS = GetNullarySelector("drain", Ctx);
455 if (msg.isInstanceMessage())
457 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
460 Selector S = msg.getSelector();
461 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
464 if (ExplodedNode *N = C.addTransition()) {
465 SmallString<200> buf;
466 llvm::raw_svector_ostream os(buf);
468 os << "The '" << S.getAsString() << "' message should be sent to instances "
469 "of class '" << Class->getName()
470 << "' and not the class directly";
472 BugReport *report = new BugReport(*BT, os.str(), N);
473 report->addRange(msg.getSourceRange());
474 C.EmitReport(report);
478 //===----------------------------------------------------------------------===//
479 // Check for passing non-Objective-C types to variadic methods that expect
480 // only Objective-C types.
481 //===----------------------------------------------------------------------===//
484 class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
485 mutable Selector arrayWithObjectsS;
486 mutable Selector dictionaryWithObjectsAndKeysS;
487 mutable Selector setWithObjectsS;
488 mutable Selector orderedSetWithObjectsS;
489 mutable Selector initWithObjectsS;
490 mutable Selector initWithObjectsAndKeysS;
491 mutable OwningPtr<BugType> BT;
493 bool isVariadicMessage(const ObjCMessage &msg) const;
496 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
500 /// isVariadicMessage - Returns whether the given message is a variadic message,
501 /// where all arguments must be Objective-C types.
503 VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const {
504 const ObjCMethodDecl *MD = msg.getMethodDecl();
506 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
509 Selector S = msg.getSelector();
511 if (msg.isInstanceMessage()) {
512 // FIXME: Ideally we'd look at the receiver interface here, but that's not
513 // useful for init, because alloc returns 'id'. In theory, this could lead
514 // to false positives, for example if there existed a class that had an
515 // initWithObjects: implementation that does accept non-Objective-C pointer
516 // types, but the chance of that happening is pretty small compared to the
517 // gains that this analysis gives.
518 const ObjCInterfaceDecl *Class = MD->getClassInterface();
520 // -[NSArray initWithObjects:]
521 if (isReceiverClassOrSuperclass(Class, "NSArray") &&
522 S == initWithObjectsS)
525 // -[NSDictionary initWithObjectsAndKeys:]
526 if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
527 S == initWithObjectsAndKeysS)
530 // -[NSSet initWithObjects:]
531 if (isReceiverClassOrSuperclass(Class, "NSSet") &&
532 S == initWithObjectsS)
535 // -[NSOrderedSet initWithObjects:]
536 if (isReceiverClassOrSuperclass(Class, "NSOrderedSet") &&
537 S == initWithObjectsS)
540 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
542 // -[NSArray arrayWithObjects:]
543 if (isReceiverClassOrSuperclass(Class, "NSArray") &&
544 S == arrayWithObjectsS)
547 // -[NSDictionary dictionaryWithObjectsAndKeys:]
548 if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
549 S == dictionaryWithObjectsAndKeysS)
552 // -[NSSet setWithObjects:]
553 if (isReceiverClassOrSuperclass(Class, "NSSet") &&
554 S == setWithObjectsS)
557 // -[NSOrderedSet orderedSetWithObjects:]
558 if (isReceiverClassOrSuperclass(Class, "NSOrderedSet") &&
559 S == orderedSetWithObjectsS)
566 void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
567 CheckerContext &C) const {
569 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
570 "Objective-C pointer types"));
572 ASTContext &Ctx = C.getASTContext();
573 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
574 dictionaryWithObjectsAndKeysS =
575 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
576 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
577 orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
579 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
580 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
583 if (!isVariadicMessage(msg))
586 // We are not interested in the selector arguments since they have
587 // well-defined types, so the compiler will issue a warning for them.
588 unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
590 // We're not interested in the last argument since it has to be nil or the
591 // compiler would have issued a warning for it elsewhere.
592 unsigned variadicArgsEnd = msg.getNumArgs() - 1;
594 if (variadicArgsEnd <= variadicArgsBegin)
597 // Verify that all arguments have Objective-C types.
598 llvm::Optional<ExplodedNode*> errorNode;
599 ProgramStateRef state = C.getState();
601 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
602 QualType ArgTy = msg.getArgType(I);
603 if (ArgTy->isObjCObjectPointerType())
606 // Block pointers are treaded as Objective-C pointers.
607 if (ArgTy->isBlockPointerType())
610 // Ignore pointer constants.
611 if (isa<loc::ConcreteInt>(msg.getArgSVal(I, C.getLocationContext(),
615 // Ignore pointer types annotated with 'NSObject' attribute.
616 if (C.getASTContext().isObjCNSObjectType(ArgTy))
619 // Ignore CF references, which can be toll-free bridged.
620 if (coreFoundation::isCFObjectRef(ArgTy))
623 // Generate only one error node to use for all bug reports.
624 if (!errorNode.hasValue()) {
625 errorNode = C.addTransition();
628 if (!errorNode.getValue())
631 SmallString<128> sbuf;
632 llvm::raw_svector_ostream os(sbuf);
634 if (const char *TypeName = GetReceiverNameType(msg))
635 os << "Argument to '" << TypeName << "' method '";
637 os << "Argument to method '";
639 os << msg.getSelector().getAsString()
640 << "' should be an Objective-C pointer type, not '"
641 << ArgTy.getAsString() << "'";
643 BugReport *R = new BugReport(*BT, os.str(),
644 errorNode.getValue());
645 R->addRange(msg.getArgSourceRange(I));
650 //===----------------------------------------------------------------------===//
651 // Check registration.
652 //===----------------------------------------------------------------------===//
654 void ento::registerNilArgChecker(CheckerManager &mgr) {
655 mgr.registerChecker<NilArgChecker>();
658 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
659 mgr.registerChecker<CFNumberCreateChecker>();
662 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
663 mgr.registerChecker<CFRetainReleaseChecker>();
666 void ento::registerClassReleaseChecker(CheckerManager &mgr) {
667 mgr.registerChecker<ClassReleaseChecker>();
670 void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
671 mgr.registerChecker<VariadicMethodTypeChecker>();