BasicObjCFoundationChecks.cpp revision b805c8ff133ef0c62df032fa711d6b13c5afd7f4
1//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
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
12//  classes.
13//
14//===----------------------------------------------------------------------===//
15
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
32using namespace clang;
33using namespace ento;
34
35namespace {
36class APIMisuse : public BugType {
37public:
38  APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
39};
40} // end anonymous namespace
41
42//===----------------------------------------------------------------------===//
43// Utility functions.
44//===----------------------------------------------------------------------===//
45
46static const char* GetReceiverNameType(const ObjCMessage &msg) {
47  if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
48    return ID->getIdentifier()->getNameStart();
49  return 0;
50}
51
52static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID,
53                                        StringRef ClassName) {
54  if (ID->getIdentifier()->getName() == ClassName)
55    return true;
56
57  if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
58    return isReceiverClassOrSuperclass(Super, ClassName);
59
60  return false;
61}
62
63static inline bool isNil(SVal X) {
64  return isa<loc::ConcreteInt>(X);
65}
66
67//===----------------------------------------------------------------------===//
68// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
69//===----------------------------------------------------------------------===//
70
71namespace {
72  class NilArgChecker : public Checker<check::PreObjCMessage> {
73    mutable llvm::OwningPtr<APIMisuse> BT;
74
75    void WarnNilArg(CheckerContext &C,
76                    const ObjCMessage &msg, unsigned Arg) const;
77
78  public:
79    void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
80  };
81}
82
83void NilArgChecker::WarnNilArg(CheckerContext &C,
84                               const ObjCMessage &msg,
85                               unsigned int Arg) const
86{
87  if (!BT)
88    BT.reset(new APIMisuse("nil argument"));
89
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";
95
96    BugReport *R = new BugReport(*BT, os.str(), N);
97    R->addRange(msg.getArgSourceRange(Arg));
98    C.EmitReport(R);
99  }
100}
101
102void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
103                                        CheckerContext &C) const {
104  const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
105  if (!ID)
106    return;
107
108  if (isReceiverClassOrSuperclass(ID, "NSString")) {
109    Selector S = msg.getSelector();
110
111    if (S.isUnarySelector())
112      return;
113
114    // FIXME: This is going to be really slow doing these checks with
115    //  lexical comparisons.
116
117    std::string NameStr = S.getAsString();
118    StringRef Name(NameStr);
119    assert(!Name.empty());
120
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);
134    }
135  }
136}
137
138//===----------------------------------------------------------------------===//
139// Error reporting.
140//===----------------------------------------------------------------------===//
141
142namespace {
143class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
144  mutable llvm::OwningPtr<APIMisuse> BT;
145  mutable IdentifierInfo* II;
146public:
147  CFNumberCreateChecker() : II(0) {}
148
149  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
150
151private:
152  void EmitError(const TypedRegion* R, const Expr *Ex,
153                uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
154};
155} // end anonymous namespace
156
157enum CFNumberType {
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
174};
175
176namespace {
177  template<typename T>
178  class Optional {
179    bool IsKnown;
180    T Val;
181  public:
182    Optional() : IsKnown(false), Val(0) {}
183    Optional(const T& val) : IsKnown(true), Val(val) {}
184
185    bool isKnown() const { return IsKnown; }
186
187    const T& getValue() const {
188      assert (isKnown());
189      return Val;
190    }
191
192    operator const T&() const {
193      return getValue();
194    }
195  };
196}
197
198static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
199  static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
200
201  if (i < kCFNumberCharType)
202    return FixedSize[i-1];
203
204  QualType T;
205
206  switch (i) {
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*.
218    default:
219      return Optional<uint64_t>();
220  }
221
222  return Ctx.getTypeSize(T);
223}
224
225#if 0
226static const char* GetCFNumberTypeStr(uint64_t i) {
227  static const char* Names[] = {
228    "kCFNumberSInt8Type",
229    "kCFNumberSInt16Type",
230    "kCFNumberSInt32Type",
231    "kCFNumberSInt64Type",
232    "kCFNumberFloat32Type",
233    "kCFNumberFloat64Type",
234    "kCFNumberCharType",
235    "kCFNumberShortType",
236    "kCFNumberIntType",
237    "kCFNumberLongType",
238    "kCFNumberLongLongType",
239    "kCFNumberFloatType",
240    "kCFNumberDoubleType",
241    "kCFNumberCFIndexType",
242    "kCFNumberNSIntegerType",
243    "kCFNumberCGFloatType"
244  };
245
246  return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
247}
248#endif
249
250void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
251                                         CheckerContext &C) const {
252  const ProgramState *state = C.getState();
253  const FunctionDecl *FD = C.getCalleeDecl(CE);
254  if (!FD)
255    return;
256
257  ASTContext &Ctx = C.getASTContext();
258  if (!II)
259    II = &Ctx.Idents.get("CFNumberCreate");
260
261  if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
262    return;
263
264  // Get the value of the "theType" argument.
265  SVal TheTypeVal = state->getSVal(CE->getArg(1));
266
267  // FIXME: We really should allow ranges of valid theType values, and
268  //   bifurcate the state appropriately.
269  nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
270  if (!V)
271    return;
272
273  uint64_t NumberKind = V->getValue().getLimitedValue();
274  Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
275
276  // FIXME: In some cases we can emit an error.
277  if (!TargetSize.isKnown())
278    return;
279
280  // Look at the value of the integer being passed by reference.  Essentially
281  // we want to catch cases where the value passed in is not equal to the
282  // size of the type being created.
283  SVal TheValueExpr = state->getSVal(CE->getArg(2));
284
285  // FIXME: Eventually we should handle arbitrary locations.  We can do this
286  //  by having an enhanced memory model that does low-level typing.
287  loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
288  if (!LV)
289    return;
290
291  const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
292  if (!R)
293    return;
294
295  QualType T = Ctx.getCanonicalType(R->getValueType());
296
297  // FIXME: If the pointee isn't an integer type, should we flag a warning?
298  //  People can do weird stuff with pointers.
299
300  if (!T->isIntegerType())
301    return;
302
303  uint64_t SourceSize = Ctx.getTypeSize(T);
304
305  // CHECK: is SourceSize == TargetSize
306  if (SourceSize == TargetSize)
307    return;
308
309  // Generate an error.  Only generate a sink if 'SourceSize < TargetSize';
310  // otherwise generate a regular node.
311  //
312  // FIXME: We can actually create an abstract "CFNumber" object that has
313  //  the bits initialized to the provided values.
314  //
315  if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
316                                                : C.addTransition()) {
317    llvm::SmallString<128> sbuf;
318    llvm::raw_svector_ostream os(sbuf);
319
320    os << (SourceSize == 8 ? "An " : "A ")
321       << SourceSize << " bit integer is used to initialize a CFNumber "
322                        "object that represents "
323       << (TargetSize == 8 ? "an " : "a ")
324       << TargetSize << " bit integer. ";
325
326    if (SourceSize < TargetSize)
327      os << (TargetSize - SourceSize)
328      << " bits of the CFNumber value will be garbage." ;
329    else
330      os << (SourceSize - TargetSize)
331      << " bits of the input integer will be lost.";
332
333    if (!BT)
334      BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
335
336    BugReport *report = new BugReport(*BT, os.str(), N);
337    report->addRange(CE->getArg(2)->getSourceRange());
338    C.EmitReport(report);
339  }
340}
341
342//===----------------------------------------------------------------------===//
343// CFRetain/CFRelease checking for null arguments.
344//===----------------------------------------------------------------------===//
345
346namespace {
347class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
348  mutable llvm::OwningPtr<APIMisuse> BT;
349  mutable IdentifierInfo *Retain, *Release;
350public:
351  CFRetainReleaseChecker(): Retain(0), Release(0) {}
352  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
353};
354} // end anonymous namespace
355
356
357void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
358                                          CheckerContext &C) const {
359  // If the CallExpr doesn't have exactly 1 argument just give up checking.
360  if (CE->getNumArgs() != 1)
361    return;
362
363  const ProgramState *state = C.getState();
364  const FunctionDecl *FD = C.getCalleeDecl(CE);
365  if (!FD)
366    return;
367
368  if (!BT) {
369    ASTContext &Ctx = C.getASTContext();
370    Retain = &Ctx.Idents.get("CFRetain");
371    Release = &Ctx.Idents.get("CFRelease");
372    BT.reset(new APIMisuse("null passed to CFRetain/CFRelease"));
373  }
374
375  // Check if we called CFRetain/CFRelease.
376  const IdentifierInfo *FuncII = FD->getIdentifier();
377  if (!(FuncII == Retain || FuncII == Release))
378    return;
379
380  // FIXME: The rest of this just checks that the argument is non-null.
381  // It should probably be refactored and combined with AttrNonNullChecker.
382
383  // Get the argument's value.
384  const Expr *Arg = CE->getArg(0);
385  SVal ArgVal = state->getSVal(Arg);
386  DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
387  if (!DefArgVal)
388    return;
389
390  // Get a NULL value.
391  SValBuilder &svalBuilder = C.getSValBuilder();
392  DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
393
394  // Make an expression asserting that they're equal.
395  DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
396
397  // Are they equal?
398  const ProgramState *stateTrue, *stateFalse;
399  llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
400
401  if (stateTrue && !stateFalse) {
402    ExplodedNode *N = C.generateSink(stateTrue);
403    if (!N)
404      return;
405
406    const char *description = (FuncII == Retain)
407                            ? "Null pointer argument in call to CFRetain"
408                            : "Null pointer argument in call to CFRelease";
409
410    BugReport *report = new BugReport(*BT, description, N);
411    report->addRange(Arg->getSourceRange());
412    report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg));
413    C.EmitReport(report);
414    return;
415  }
416
417  // From here on, we know the argument is non-null.
418  C.addTransition(stateFalse);
419}
420
421//===----------------------------------------------------------------------===//
422// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
423//===----------------------------------------------------------------------===//
424
425namespace {
426class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
427  mutable Selector releaseS;
428  mutable Selector retainS;
429  mutable Selector autoreleaseS;
430  mutable Selector drainS;
431  mutable llvm::OwningPtr<BugType> BT;
432
433public:
434  void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
435};
436}
437
438void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
439                                              CheckerContext &C) const {
440
441  if (!BT) {
442    BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
443                           "instance"));
444
445    ASTContext &Ctx = C.getASTContext();
446    releaseS = GetNullarySelector("release", Ctx);
447    retainS = GetNullarySelector("retain", Ctx);
448    autoreleaseS = GetNullarySelector("autorelease", Ctx);
449    drainS = GetNullarySelector("drain", Ctx);
450  }
451
452  if (msg.isInstanceMessage())
453    return;
454  const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
455  assert(Class);
456
457  Selector S = msg.getSelector();
458  if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
459    return;
460
461  if (ExplodedNode *N = C.addTransition()) {
462    llvm::SmallString<200> buf;
463    llvm::raw_svector_ostream os(buf);
464
465    os << "The '" << S.getAsString() << "' message should be sent to instances "
466          "of class '" << Class->getName()
467       << "' and not the class directly";
468
469    BugReport *report = new BugReport(*BT, os.str(), N);
470    report->addRange(msg.getSourceRange());
471    C.EmitReport(report);
472  }
473}
474
475//===----------------------------------------------------------------------===//
476// Check for passing non-Objective-C types to variadic methods that expect
477// only Objective-C types.
478//===----------------------------------------------------------------------===//
479
480namespace {
481class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
482  mutable Selector arrayWithObjectsS;
483  mutable Selector dictionaryWithObjectsAndKeysS;
484  mutable Selector setWithObjectsS;
485  mutable Selector initWithObjectsS;
486  mutable Selector initWithObjectsAndKeysS;
487  mutable llvm::OwningPtr<BugType> BT;
488
489  bool isVariadicMessage(const ObjCMessage &msg) const;
490
491public:
492  void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
493};
494}
495
496/// isVariadicMessage - Returns whether the given message is a variadic message,
497/// where all arguments must be Objective-C types.
498bool
499VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const {
500  const ObjCMethodDecl *MD = msg.getMethodDecl();
501
502  if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
503    return false;
504
505  Selector S = msg.getSelector();
506
507  if (msg.isInstanceMessage()) {
508    // FIXME: Ideally we'd look at the receiver interface here, but that's not
509    // useful for init, because alloc returns 'id'. In theory, this could lead
510    // to false positives, for example if there existed a class that had an
511    // initWithObjects: implementation that does accept non-Objective-C pointer
512    // types, but the chance of that happening is pretty small compared to the
513    // gains that this analysis gives.
514    const ObjCInterfaceDecl *Class = MD->getClassInterface();
515
516    // -[NSArray initWithObjects:]
517    if (isReceiverClassOrSuperclass(Class, "NSArray") &&
518        S == initWithObjectsS)
519      return true;
520
521    // -[NSDictionary initWithObjectsAndKeys:]
522    if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
523        S == initWithObjectsAndKeysS)
524      return true;
525
526    // -[NSSet initWithObjects:]
527    if (isReceiverClassOrSuperclass(Class, "NSSet") &&
528        S == initWithObjectsS)
529      return true;
530  } else {
531    const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
532
533    // -[NSArray arrayWithObjects:]
534    if (isReceiverClassOrSuperclass(Class, "NSArray") &&
535        S == arrayWithObjectsS)
536      return true;
537
538    // -[NSDictionary dictionaryWithObjectsAndKeys:]
539    if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
540        S == dictionaryWithObjectsAndKeysS)
541      return true;
542
543    // -[NSSet setWithObjects:]
544    if (isReceiverClassOrSuperclass(Class, "NSSet") &&
545        S == setWithObjectsS)
546      return true;
547  }
548
549  return false;
550}
551
552void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
553                                                    CheckerContext &C) const {
554  if (!BT) {
555    BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
556                           "Objective-C pointer types"));
557
558    ASTContext &Ctx = C.getASTContext();
559    arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
560    dictionaryWithObjectsAndKeysS =
561      GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
562    setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
563
564    initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
565    initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
566  }
567
568  if (!isVariadicMessage(msg))
569      return;
570
571  // We are not interested in the selector arguments since they have
572  // well-defined types, so the compiler will issue a warning for them.
573  unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
574
575  // We're not interested in the last argument since it has to be nil or the
576  // compiler would have issued a warning for it elsewhere.
577  unsigned variadicArgsEnd = msg.getNumArgs() - 1;
578
579  if (variadicArgsEnd <= variadicArgsBegin)
580    return;
581
582  // Verify that all arguments have Objective-C types.
583  llvm::Optional<ExplodedNode*> errorNode;
584  const ProgramState *state = C.getState();
585
586  for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
587    QualType ArgTy = msg.getArgType(I);
588    if (ArgTy->isObjCObjectPointerType())
589      continue;
590
591    // Block pointers are treaded as Objective-C pointers.
592    if (ArgTy->isBlockPointerType())
593      continue;
594
595    // Ignore pointer constants.
596    if (isa<loc::ConcreteInt>(msg.getArgSVal(I, state)))
597      continue;
598
599    // Ignore pointer types annotated with 'NSObject' attribute.
600    if (C.getASTContext().isObjCNSObjectType(ArgTy))
601      continue;
602
603    // Ignore CF references, which can be toll-free bridged.
604    if (coreFoundation::isCFObjectRef(ArgTy))
605      continue;
606
607    // Generate only one error node to use for all bug reports.
608    if (!errorNode.hasValue()) {
609      errorNode = C.addTransition();
610    }
611
612    if (!errorNode.getValue())
613      continue;
614
615    llvm::SmallString<128> sbuf;
616    llvm::raw_svector_ostream os(sbuf);
617
618    if (const char *TypeName = GetReceiverNameType(msg))
619      os << "Argument to '" << TypeName << "' method '";
620    else
621      os << "Argument to method '";
622
623    os << msg.getSelector().getAsString()
624      << "' should be an Objective-C pointer type, not '"
625      << ArgTy.getAsString() << "'";
626
627    BugReport *R = new BugReport(*BT, os.str(),
628                                             errorNode.getValue());
629    R->addRange(msg.getArgSourceRange(I));
630    C.EmitReport(R);
631  }
632}
633
634//===----------------------------------------------------------------------===//
635// Check registration.
636//===----------------------------------------------------------------------===//
637
638void ento::registerNilArgChecker(CheckerManager &mgr) {
639  mgr.registerChecker<NilArgChecker>();
640}
641
642void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
643  mgr.registerChecker<CFNumberCreateChecker>();
644}
645
646void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
647  mgr.registerChecker<CFRetainReleaseChecker>();
648}
649
650void ento::registerClassReleaseChecker(CheckerManager &mgr) {
651  mgr.registerChecker<ClassReleaseChecker>();
652}
653
654void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
655  mgr.registerChecker<VariadicMethodTypeChecker>();
656}
657