BasicObjCFoundationChecks.cpp revision ef202c35b37c137e32fe30f4453915b6d3b525d7
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/AST/ASTContext.h"
18#include "clang/AST/DeclObjC.h"
19#include "clang/AST/Expr.h"
20#include "clang/AST/ExprObjC.h"
21#include "clang/AST/StmtObjC.h"
22#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
23#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
24#include "clang/StaticAnalyzer/Core/Checker.h"
25#include "clang/StaticAnalyzer/Core/CheckerManager.h"
26#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
27#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
28#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
29#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
30#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
31#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
32#include "llvm/ADT/SmallString.h"
33#include "llvm/ADT/StringMap.h"
34#include "llvm/Support/raw_ostream.h"
35
36using namespace clang;
37using namespace ento;
38
39namespace {
40class APIMisuse : public BugType {
41public:
42  APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
43};
44} // end anonymous namespace
45
46//===----------------------------------------------------------------------===//
47// Utility functions.
48//===----------------------------------------------------------------------===//
49
50static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) {
51  if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
52    return ID->getIdentifier()->getName();
53  return StringRef();
54}
55
56enum FoundationClass {
57  FC_None,
58  FC_NSArray,
59  FC_NSDictionary,
60  FC_NSEnumerator,
61  FC_NSNull,
62  FC_NSOrderedSet,
63  FC_NSSet,
64  FC_NSString
65};
66
67static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID) {
68  static llvm::StringMap<FoundationClass> Classes;
69  if (Classes.empty()) {
70    Classes["NSArray"] = FC_NSArray;
71    Classes["NSDictionary"] = FC_NSDictionary;
72    Classes["NSEnumerator"] = FC_NSEnumerator;
73    Classes["NSNull"] = FC_NSNull;
74    Classes["NSOrderedSet"] = FC_NSOrderedSet;
75    Classes["NSSet"] = FC_NSSet;
76    Classes["NSString"] = FC_NSString;
77  }
78
79  // FIXME: Should we cache this at all?
80  FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());
81  if (result == FC_None)
82    if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
83      return findKnownClass(Super);
84
85  return result;
86}
87
88//===----------------------------------------------------------------------===//
89// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
90//===----------------------------------------------------------------------===//
91
92namespace {
93  class NilArgChecker : public Checker<check::PreObjCMessage,
94                                       check::PostStmt<ObjCDictionaryLiteral>,
95                                       check::PostStmt<ObjCArrayLiteral> > {
96    mutable OwningPtr<APIMisuse> BT;
97
98    void warnIfNilExpr(const Expr *E,
99                       const char *Msg,
100                       CheckerContext &C) const;
101
102    void warnIfNilArg(CheckerContext &C,
103                      const ObjCMethodCall &msg, unsigned Arg,
104                      FoundationClass Class,
105                      bool CanBeSubscript = false) const;
106
107    void generateBugReport(ExplodedNode *N,
108                           StringRef Msg,
109                           SourceRange Range,
110                           const Expr *Expr,
111                           CheckerContext &C) const;
112
113  public:
114    void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
115    void checkPostStmt(const ObjCDictionaryLiteral *DL,
116                       CheckerContext &C) const;
117    void checkPostStmt(const ObjCArrayLiteral *AL,
118                       CheckerContext &C) const;
119  };
120}
121
122void NilArgChecker::warnIfNilExpr(const Expr *E,
123                                  const char *Msg,
124                                  CheckerContext &C) const {
125  ProgramStateRef State = C.getState();
126  if (State->isNull(C.getSVal(E)).isConstrainedTrue()) {
127
128    if (ExplodedNode *N = C.generateSink()) {
129      generateBugReport(N, Msg, E->getSourceRange(), E, C);
130    }
131
132  }
133}
134
135void NilArgChecker::warnIfNilArg(CheckerContext &C,
136                                 const ObjCMethodCall &msg,
137                                 unsigned int Arg,
138                                 FoundationClass Class,
139                                 bool CanBeSubscript) const {
140  // Check if the argument is nil.
141  ProgramStateRef State = C.getState();
142  if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
143      return;
144
145  if (ExplodedNode *N = C.generateSink()) {
146    SmallString<128> sbuf;
147    llvm::raw_svector_ostream os(sbuf);
148
149    if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) {
150
151      if (Class == FC_NSArray) {
152        os << "Array element cannot be nil";
153      } else if (Class == FC_NSDictionary) {
154        if (Arg == 0) {
155          os << "Value stored into '";
156          os << GetReceiverInterfaceName(msg) << "' cannot be nil";
157        } else {
158          assert(Arg == 1);
159          os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil";
160        }
161      } else
162        llvm_unreachable("Missing foundation class for the subscript expr");
163
164    } else {
165      if (Class == FC_NSDictionary) {
166        if (Arg == 0)
167          os << "Value argument ";
168        else {
169          assert(Arg == 1);
170          os << "Key argument ";
171        }
172        os << "to '" << msg.getSelector().getAsString() << "' cannot be nil";
173      } else {
174        os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"
175        << msg.getSelector().getAsString() << "' cannot be nil";
176      }
177    }
178
179    generateBugReport(N, os.str(), msg.getArgSourceRange(Arg),
180                      msg.getArgExpr(Arg), C);
181  }
182}
183
184void NilArgChecker::generateBugReport(ExplodedNode *N,
185                                      StringRef Msg,
186                                      SourceRange Range,
187                                      const Expr *E,
188                                      CheckerContext &C) const {
189  if (!BT)
190    BT.reset(new APIMisuse("nil argument"));
191
192  BugReport *R = new BugReport(*BT, Msg, N);
193  R->addRange(Range);
194  bugreporter::trackNullOrUndefValue(N, E, *R);
195  C.emitReport(R);
196}
197
198void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
199                                        CheckerContext &C) const {
200  const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
201  if (!ID)
202    return;
203
204  FoundationClass Class = findKnownClass(ID);
205
206  static const unsigned InvalidArgIndex = UINT_MAX;
207  unsigned Arg = InvalidArgIndex;
208  bool CanBeSubscript = false;
209
210  if (Class == FC_NSString) {
211    Selector S = msg.getSelector();
212
213    if (S.isUnarySelector())
214      return;
215
216    // FIXME: This is going to be really slow doing these checks with
217    //  lexical comparisons.
218
219    std::string NameStr = S.getAsString();
220    StringRef Name(NameStr);
221    assert(!Name.empty());
222
223    // FIXME: Checking for initWithFormat: will not work in most cases
224    //  yet because [NSString alloc] returns id, not NSString*.  We will
225    //  need support for tracking expected-type information in the analyzer
226    //  to find these errors.
227    if (Name == "caseInsensitiveCompare:" ||
228        Name == "compare:" ||
229        Name == "compare:options:" ||
230        Name == "compare:options:range:" ||
231        Name == "compare:options:range:locale:" ||
232        Name == "componentsSeparatedByCharactersInSet:" ||
233        Name == "initWithFormat:") {
234      Arg = 0;
235    }
236  } else if (Class == FC_NSArray) {
237    Selector S = msg.getSelector();
238
239    if (S.isUnarySelector())
240      return;
241
242    if (S.getNameForSlot(0).equals("addObject")) {
243      Arg = 0;
244    } else if (S.getNameForSlot(0).equals("insertObject") &&
245               S.getNameForSlot(1).equals("atIndex")) {
246      Arg = 0;
247    } else if (S.getNameForSlot(0).equals("replaceObjectAtIndex") &&
248               S.getNameForSlot(1).equals("withObject")) {
249      Arg = 1;
250    } else if (S.getNameForSlot(0).equals("setObject") &&
251               S.getNameForSlot(1).equals("atIndexedSubscript")) {
252      Arg = 0;
253      CanBeSubscript = true;
254    } else if (S.getNameForSlot(0).equals("arrayByAddingObject")) {
255      Arg = 0;
256    }
257  } else if (Class == FC_NSDictionary) {
258    Selector S = msg.getSelector();
259
260    if (S.isUnarySelector())
261      return;
262
263    if (S.getNameForSlot(0).equals("dictionaryWithObject") &&
264        S.getNameForSlot(1).equals("forKey")) {
265      Arg = 0;
266      warnIfNilArg(C, msg, /* Arg */1, Class);
267    } else if (S.getNameForSlot(0).equals("setObject") &&
268               S.getNameForSlot(1).equals("forKey")) {
269      Arg = 0;
270      warnIfNilArg(C, msg, /* Arg */1, Class);
271    } else if (S.getNameForSlot(0).equals("setObject") &&
272               S.getNameForSlot(1).equals("forKeyedSubscript")) {
273      CanBeSubscript = true;
274      Arg = 0;
275      warnIfNilArg(C, msg, /* Arg */1, Class, CanBeSubscript);
276    } else if (S.getNameForSlot(0).equals("removeObjectForKey")) {
277      Arg = 0;
278    }
279  }
280
281  // If argument is '0', report a warning.
282  if ((Arg != InvalidArgIndex))
283    warnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
284
285}
286
287void NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL,
288                                  CheckerContext &C) const {
289  unsigned NumOfElements = AL->getNumElements();
290  for (unsigned i = 0; i < NumOfElements; ++i) {
291    warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C);
292  }
293}
294
295void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
296                                  CheckerContext &C) const {
297  unsigned NumOfElements = DL->getNumElements();
298  for (unsigned i = 0; i < NumOfElements; ++i) {
299    ObjCDictionaryElement Element = DL->getKeyValueElement(i);
300    warnIfNilExpr(Element.Key, "Dictionary key cannot be nil", C);
301    warnIfNilExpr(Element.Value, "Dictionary value cannot be nil", C);
302  }
303}
304
305//===----------------------------------------------------------------------===//
306// Error reporting.
307//===----------------------------------------------------------------------===//
308
309namespace {
310class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
311  mutable OwningPtr<APIMisuse> BT;
312  mutable IdentifierInfo* II;
313public:
314  CFNumberCreateChecker() : II(0) {}
315
316  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
317
318private:
319  void EmitError(const TypedRegion* R, const Expr *Ex,
320                uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
321};
322} // end anonymous namespace
323
324enum CFNumberType {
325  kCFNumberSInt8Type = 1,
326  kCFNumberSInt16Type = 2,
327  kCFNumberSInt32Type = 3,
328  kCFNumberSInt64Type = 4,
329  kCFNumberFloat32Type = 5,
330  kCFNumberFloat64Type = 6,
331  kCFNumberCharType = 7,
332  kCFNumberShortType = 8,
333  kCFNumberIntType = 9,
334  kCFNumberLongType = 10,
335  kCFNumberLongLongType = 11,
336  kCFNumberFloatType = 12,
337  kCFNumberDoubleType = 13,
338  kCFNumberCFIndexType = 14,
339  kCFNumberNSIntegerType = 15,
340  kCFNumberCGFloatType = 16
341};
342
343static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
344  static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
345
346  if (i < kCFNumberCharType)
347    return FixedSize[i-1];
348
349  QualType T;
350
351  switch (i) {
352    case kCFNumberCharType:     T = Ctx.CharTy;     break;
353    case kCFNumberShortType:    T = Ctx.ShortTy;    break;
354    case kCFNumberIntType:      T = Ctx.IntTy;      break;
355    case kCFNumberLongType:     T = Ctx.LongTy;     break;
356    case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
357    case kCFNumberFloatType:    T = Ctx.FloatTy;    break;
358    case kCFNumberDoubleType:   T = Ctx.DoubleTy;   break;
359    case kCFNumberCFIndexType:
360    case kCFNumberNSIntegerType:
361    case kCFNumberCGFloatType:
362      // FIXME: We need a way to map from names to Type*.
363    default:
364      return None;
365  }
366
367  return Ctx.getTypeSize(T);
368}
369
370#if 0
371static const char* GetCFNumberTypeStr(uint64_t i) {
372  static const char* Names[] = {
373    "kCFNumberSInt8Type",
374    "kCFNumberSInt16Type",
375    "kCFNumberSInt32Type",
376    "kCFNumberSInt64Type",
377    "kCFNumberFloat32Type",
378    "kCFNumberFloat64Type",
379    "kCFNumberCharType",
380    "kCFNumberShortType",
381    "kCFNumberIntType",
382    "kCFNumberLongType",
383    "kCFNumberLongLongType",
384    "kCFNumberFloatType",
385    "kCFNumberDoubleType",
386    "kCFNumberCFIndexType",
387    "kCFNumberNSIntegerType",
388    "kCFNumberCGFloatType"
389  };
390
391  return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
392}
393#endif
394
395void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
396                                         CheckerContext &C) const {
397  ProgramStateRef state = C.getState();
398  const FunctionDecl *FD = C.getCalleeDecl(CE);
399  if (!FD)
400    return;
401
402  ASTContext &Ctx = C.getASTContext();
403  if (!II)
404    II = &Ctx.Idents.get("CFNumberCreate");
405
406  if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
407    return;
408
409  // Get the value of the "theType" argument.
410  const LocationContext *LCtx = C.getLocationContext();
411  SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx);
412
413  // FIXME: We really should allow ranges of valid theType values, and
414  //   bifurcate the state appropriately.
415  Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>();
416  if (!V)
417    return;
418
419  uint64_t NumberKind = V->getValue().getLimitedValue();
420  Optional<uint64_t> OptTargetSize = GetCFNumberSize(Ctx, NumberKind);
421
422  // FIXME: In some cases we can emit an error.
423  if (!OptTargetSize)
424    return;
425
426  uint64_t TargetSize = *OptTargetSize;
427
428  // Look at the value of the integer being passed by reference.  Essentially
429  // we want to catch cases where the value passed in is not equal to the
430  // size of the type being created.
431  SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx);
432
433  // FIXME: Eventually we should handle arbitrary locations.  We can do this
434  //  by having an enhanced memory model that does low-level typing.
435  Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
436  if (!LV)
437    return;
438
439  const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
440  if (!R)
441    return;
442
443  QualType T = Ctx.getCanonicalType(R->getValueType());
444
445  // FIXME: If the pointee isn't an integer type, should we flag a warning?
446  //  People can do weird stuff with pointers.
447
448  if (!T->isIntegralOrEnumerationType())
449    return;
450
451  uint64_t SourceSize = Ctx.getTypeSize(T);
452
453  // CHECK: is SourceSize == TargetSize
454  if (SourceSize == TargetSize)
455    return;
456
457  // Generate an error.  Only generate a sink if 'SourceSize < TargetSize';
458  // otherwise generate a regular node.
459  //
460  // FIXME: We can actually create an abstract "CFNumber" object that has
461  //  the bits initialized to the provided values.
462  //
463  if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
464                                                : C.addTransition()) {
465    SmallString<128> sbuf;
466    llvm::raw_svector_ostream os(sbuf);
467
468    os << (SourceSize == 8 ? "An " : "A ")
469       << SourceSize << " bit integer is used to initialize a CFNumber "
470                        "object that represents "
471       << (TargetSize == 8 ? "an " : "a ")
472       << TargetSize << " bit integer. ";
473
474    if (SourceSize < TargetSize)
475      os << (TargetSize - SourceSize)
476      << " bits of the CFNumber value will be garbage." ;
477    else
478      os << (SourceSize - TargetSize)
479      << " bits of the input integer will be lost.";
480
481    if (!BT)
482      BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
483
484    BugReport *report = new BugReport(*BT, os.str(), N);
485    report->addRange(CE->getArg(2)->getSourceRange());
486    C.emitReport(report);
487  }
488}
489
490//===----------------------------------------------------------------------===//
491// CFRetain/CFRelease/CFMakeCollectable checking for null arguments.
492//===----------------------------------------------------------------------===//
493
494namespace {
495class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
496  mutable OwningPtr<APIMisuse> BT;
497  mutable IdentifierInfo *Retain, *Release, *MakeCollectable;
498public:
499  CFRetainReleaseChecker(): Retain(0), Release(0), MakeCollectable(0) {}
500  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
501};
502} // end anonymous namespace
503
504
505void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
506                                          CheckerContext &C) const {
507  // If the CallExpr doesn't have exactly 1 argument just give up checking.
508  if (CE->getNumArgs() != 1)
509    return;
510
511  ProgramStateRef state = C.getState();
512  const FunctionDecl *FD = C.getCalleeDecl(CE);
513  if (!FD)
514    return;
515
516  if (!BT) {
517    ASTContext &Ctx = C.getASTContext();
518    Retain = &Ctx.Idents.get("CFRetain");
519    Release = &Ctx.Idents.get("CFRelease");
520    MakeCollectable = &Ctx.Idents.get("CFMakeCollectable");
521    BT.reset(
522      new APIMisuse("null passed to CFRetain/CFRelease/CFMakeCollectable"));
523  }
524
525  // Check if we called CFRetain/CFRelease/CFMakeCollectable.
526  const IdentifierInfo *FuncII = FD->getIdentifier();
527  if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable))
528    return;
529
530  // FIXME: The rest of this just checks that the argument is non-null.
531  // It should probably be refactored and combined with NonNullParamChecker.
532
533  // Get the argument's value.
534  const Expr *Arg = CE->getArg(0);
535  SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
536  Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
537  if (!DefArgVal)
538    return;
539
540  // Get a NULL value.
541  SValBuilder &svalBuilder = C.getSValBuilder();
542  DefinedSVal zero =
543      svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>();
544
545  // Make an expression asserting that they're equal.
546  DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
547
548  // Are they equal?
549  ProgramStateRef stateTrue, stateFalse;
550  llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
551
552  if (stateTrue && !stateFalse) {
553    ExplodedNode *N = C.generateSink(stateTrue);
554    if (!N)
555      return;
556
557    const char *description;
558    if (FuncII == Retain)
559      description = "Null pointer argument in call to CFRetain";
560    else if (FuncII == Release)
561      description = "Null pointer argument in call to CFRelease";
562    else if (FuncII == MakeCollectable)
563      description = "Null pointer argument in call to CFMakeCollectable";
564    else
565      llvm_unreachable("impossible case");
566
567    BugReport *report = new BugReport(*BT, description, N);
568    report->addRange(Arg->getSourceRange());
569    bugreporter::trackNullOrUndefValue(N, Arg, *report);
570    C.emitReport(report);
571    return;
572  }
573
574  // From here on, we know the argument is non-null.
575  C.addTransition(stateFalse);
576}
577
578//===----------------------------------------------------------------------===//
579// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
580//===----------------------------------------------------------------------===//
581
582namespace {
583class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
584  mutable Selector releaseS;
585  mutable Selector retainS;
586  mutable Selector autoreleaseS;
587  mutable Selector drainS;
588  mutable OwningPtr<BugType> BT;
589
590public:
591  void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
592};
593}
594
595void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
596                                              CheckerContext &C) const {
597
598  if (!BT) {
599    BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
600                           "instance"));
601
602    ASTContext &Ctx = C.getASTContext();
603    releaseS = GetNullarySelector("release", Ctx);
604    retainS = GetNullarySelector("retain", Ctx);
605    autoreleaseS = GetNullarySelector("autorelease", Ctx);
606    drainS = GetNullarySelector("drain", Ctx);
607  }
608
609  if (msg.isInstanceMessage())
610    return;
611  const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
612  assert(Class);
613
614  Selector S = msg.getSelector();
615  if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
616    return;
617
618  if (ExplodedNode *N = C.addTransition()) {
619    SmallString<200> buf;
620    llvm::raw_svector_ostream os(buf);
621
622    os << "The '" << S.getAsString() << "' message should be sent to instances "
623          "of class '" << Class->getName()
624       << "' and not the class directly";
625
626    BugReport *report = new BugReport(*BT, os.str(), N);
627    report->addRange(msg.getSourceRange());
628    C.emitReport(report);
629  }
630}
631
632//===----------------------------------------------------------------------===//
633// Check for passing non-Objective-C types to variadic methods that expect
634// only Objective-C types.
635//===----------------------------------------------------------------------===//
636
637namespace {
638class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
639  mutable Selector arrayWithObjectsS;
640  mutable Selector dictionaryWithObjectsAndKeysS;
641  mutable Selector setWithObjectsS;
642  mutable Selector orderedSetWithObjectsS;
643  mutable Selector initWithObjectsS;
644  mutable Selector initWithObjectsAndKeysS;
645  mutable OwningPtr<BugType> BT;
646
647  bool isVariadicMessage(const ObjCMethodCall &msg) const;
648
649public:
650  void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
651};
652}
653
654/// isVariadicMessage - Returns whether the given message is a variadic message,
655/// where all arguments must be Objective-C types.
656bool
657VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
658  const ObjCMethodDecl *MD = msg.getDecl();
659
660  if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
661    return false;
662
663  Selector S = msg.getSelector();
664
665  if (msg.isInstanceMessage()) {
666    // FIXME: Ideally we'd look at the receiver interface here, but that's not
667    // useful for init, because alloc returns 'id'. In theory, this could lead
668    // to false positives, for example if there existed a class that had an
669    // initWithObjects: implementation that does accept non-Objective-C pointer
670    // types, but the chance of that happening is pretty small compared to the
671    // gains that this analysis gives.
672    const ObjCInterfaceDecl *Class = MD->getClassInterface();
673
674    switch (findKnownClass(Class)) {
675    case FC_NSArray:
676    case FC_NSOrderedSet:
677    case FC_NSSet:
678      return S == initWithObjectsS;
679    case FC_NSDictionary:
680      return S == initWithObjectsAndKeysS;
681    default:
682      return false;
683    }
684  } else {
685    const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
686
687    switch (findKnownClass(Class)) {
688      case FC_NSArray:
689        return S == arrayWithObjectsS;
690      case FC_NSOrderedSet:
691        return S == orderedSetWithObjectsS;
692      case FC_NSSet:
693        return S == setWithObjectsS;
694      case FC_NSDictionary:
695        return S == dictionaryWithObjectsAndKeysS;
696      default:
697        return false;
698    }
699  }
700}
701
702void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
703                                                    CheckerContext &C) const {
704  if (!BT) {
705    BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
706                           "Objective-C pointer types"));
707
708    ASTContext &Ctx = C.getASTContext();
709    arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
710    dictionaryWithObjectsAndKeysS =
711      GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
712    setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
713    orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
714
715    initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
716    initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
717  }
718
719  if (!isVariadicMessage(msg))
720      return;
721
722  // We are not interested in the selector arguments since they have
723  // well-defined types, so the compiler will issue a warning for them.
724  unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
725
726  // We're not interested in the last argument since it has to be nil or the
727  // compiler would have issued a warning for it elsewhere.
728  unsigned variadicArgsEnd = msg.getNumArgs() - 1;
729
730  if (variadicArgsEnd <= variadicArgsBegin)
731    return;
732
733  // Verify that all arguments have Objective-C types.
734  Optional<ExplodedNode*> errorNode;
735  ProgramStateRef state = C.getState();
736
737  for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
738    QualType ArgTy = msg.getArgExpr(I)->getType();
739    if (ArgTy->isObjCObjectPointerType())
740      continue;
741
742    // Block pointers are treaded as Objective-C pointers.
743    if (ArgTy->isBlockPointerType())
744      continue;
745
746    // Ignore pointer constants.
747    if (msg.getArgSVal(I).getAs<loc::ConcreteInt>())
748      continue;
749
750    // Ignore pointer types annotated with 'NSObject' attribute.
751    if (C.getASTContext().isObjCNSObjectType(ArgTy))
752      continue;
753
754    // Ignore CF references, which can be toll-free bridged.
755    if (coreFoundation::isCFObjectRef(ArgTy))
756      continue;
757
758    // Generate only one error node to use for all bug reports.
759    if (!errorNode.hasValue())
760      errorNode = C.addTransition();
761
762    if (!errorNode.getValue())
763      continue;
764
765    SmallString<128> sbuf;
766    llvm::raw_svector_ostream os(sbuf);
767
768    StringRef TypeName = GetReceiverInterfaceName(msg);
769    if (!TypeName.empty())
770      os << "Argument to '" << TypeName << "' method '";
771    else
772      os << "Argument to method '";
773
774    os << msg.getSelector().getAsString()
775       << "' should be an Objective-C pointer type, not '";
776    ArgTy.print(os, C.getLangOpts());
777    os << "'";
778
779    BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue());
780    R->addRange(msg.getArgSourceRange(I));
781    C.emitReport(R);
782  }
783}
784
785//===----------------------------------------------------------------------===//
786// Improves the modeling of loops over Cocoa collections.
787//===----------------------------------------------------------------------===//
788
789namespace {
790class ObjCLoopChecker
791  : public Checker<check::PostStmt<ObjCForCollectionStmt> > {
792
793public:
794  void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
795};
796}
797
798static bool isKnownNonNilCollectionType(QualType T) {
799  const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
800  if (!PT)
801    return false;
802
803  const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
804  if (!ID)
805    return false;
806
807  switch (findKnownClass(ID)) {
808  case FC_NSArray:
809  case FC_NSDictionary:
810  case FC_NSEnumerator:
811  case FC_NSOrderedSet:
812  case FC_NSSet:
813    return true;
814  default:
815    return false;
816  }
817}
818
819/// Assumes that the collection is non-nil.
820///
821/// If the collection is known to be nil, returns NULL to indicate an infeasible
822/// path.
823static ProgramStateRef checkCollectionNonNil(CheckerContext &C,
824                                             ProgramStateRef State,
825                                             const ObjCForCollectionStmt *FCS) {
826  if (!State)
827    return NULL;
828
829  SVal CollectionVal = C.getSVal(FCS->getCollection());
830  Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>();
831  if (!KnownCollection)
832    return State;
833
834  ProgramStateRef StNonNil, StNil;
835  llvm::tie(StNonNil, StNil) = State->assume(*KnownCollection);
836  if (StNil && !StNonNil) {
837    // The collection is nil. This path is infeasible.
838    return NULL;
839  }
840
841  return StNonNil;
842}
843
844/// Assumes that the collection elements are non-nil.
845///
846/// This only applies if the collection is one of those known not to contain
847/// nil values.
848static ProgramStateRef checkElementNonNil(CheckerContext &C,
849                                          ProgramStateRef State,
850                                          const ObjCForCollectionStmt *FCS) {
851  if (!State)
852    return NULL;
853
854  // See if the collection is one where we /know/ the elements are non-nil.
855  if (!isKnownNonNilCollectionType(FCS->getCollection()->getType()))
856    return State;
857
858  const LocationContext *LCtx = C.getLocationContext();
859  const Stmt *Element = FCS->getElement();
860
861  // FIXME: Copied from ExprEngineObjC.
862  Optional<Loc> ElementLoc;
863  if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
864    const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
865    assert(ElemDecl->getInit() == 0);
866    ElementLoc = State->getLValue(ElemDecl, LCtx);
867  } else {
868    ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>();
869  }
870
871  if (!ElementLoc)
872    return State;
873
874  // Go ahead and assume the value is non-nil.
875  SVal Val = State->getSVal(*ElementLoc);
876  return State->assume(Val.castAs<DefinedOrUnknownSVal>(), true);
877}
878
879void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
880                                    CheckerContext &C) const {
881  // Check if this is the branch for the end of the loop.
882  SVal CollectionSentinel = C.getSVal(FCS);
883  if (CollectionSentinel.isZeroConstant())
884    return;
885
886  ProgramStateRef State = C.getState();
887  State = checkCollectionNonNil(C, State, FCS);
888  State = checkElementNonNil(C, State, FCS);
889
890  if (!State)
891    C.generateSink();
892  else if (State != C.getState())
893    C.addTransition(State);
894}
895
896namespace {
897/// \class ObjCNonNilReturnValueChecker
898/// \brief The checker restricts the return values of APIs known to
899/// never (or almost never) return 'nil'.
900class ObjCNonNilReturnValueChecker
901  : public Checker<check::PostObjCMessage> {
902    mutable bool Initialized;
903    mutable Selector ObjectAtIndex;
904    mutable Selector ObjectAtIndexedSubscript;
905    mutable Selector NullSelector;
906
907public:
908  ObjCNonNilReturnValueChecker() : Initialized(false) {}
909  void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
910};
911}
912
913static ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
914                                           ProgramStateRef State,
915                                           CheckerContext &C) {
916  SVal Val = State->getSVal(NonNullExpr, C.getLocationContext());
917  if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>())
918    return State->assume(*DV, true);
919  return State;
920}
921
922void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
923                                                        CheckerContext &C)
924                                                        const {
925  ProgramStateRef State = C.getState();
926
927  if (!Initialized) {
928    ASTContext &Ctx = C.getASTContext();
929    ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
930    ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
931    NullSelector = GetNullarySelector("null", Ctx);
932  }
933
934  // Check the receiver type.
935  if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
936
937    // Assume that object returned from '[self init]' or '[super init]' is not
938    // 'nil' if we are processing an inlined function/method.
939    //
940    // A defensive callee will (and should) check if the object returned by
941    // '[super init]' is 'nil' before doing it's own initialization. However,
942    // since 'nil' is rarely returned in practice, we should not warn when the
943    // caller to the defensive constructor uses the object in contexts where
944    // 'nil' is not accepted.
945    if (!C.inTopFrame() && M.getDecl() &&
946        M.getDecl()->getMethodFamily() == OMF_init &&
947        M.isReceiverSelfOrSuper()) {
948      State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
949    }
950
951    FoundationClass Cl = findKnownClass(Interface);
952
953    // Objects returned from
954    // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
955    // are never 'nil'.
956    if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) {
957      Selector Sel = M.getSelector();
958      if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
959        // Go ahead and assume the value is non-nil.
960        State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
961      }
962    }
963
964    // Objects returned from [NSNull null] are not nil.
965    if (Cl == FC_NSNull) {
966      if (M.getSelector() == NullSelector) {
967        // Go ahead and assume the value is non-nil.
968        State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
969      }
970    }
971  }
972  C.addTransition(State);
973}
974
975//===----------------------------------------------------------------------===//
976// Check registration.
977//===----------------------------------------------------------------------===//
978
979void ento::registerNilArgChecker(CheckerManager &mgr) {
980  mgr.registerChecker<NilArgChecker>();
981}
982
983void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
984  mgr.registerChecker<CFNumberCreateChecker>();
985}
986
987void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
988  mgr.registerChecker<CFRetainReleaseChecker>();
989}
990
991void ento::registerClassReleaseChecker(CheckerManager &mgr) {
992  mgr.registerChecker<ClassReleaseChecker>();
993}
994
995void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
996  mgr.registerChecker<VariadicMethodTypeChecker>();
997}
998
999void ento::registerObjCLoopChecker(CheckerManager &mgr) {
1000  mgr.registerChecker<ObjCLoopChecker>();
1001}
1002
1003void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
1004  mgr.registerChecker<ObjCNonNilReturnValueChecker>();
1005}
1006