BasicObjCFoundationChecks.cpp revision 432424d67641d609e4990d791baa782fc161027e
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 "BasicObjCFoundationChecks.h"
17
18#include "clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h"
19#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h"
20#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h"
21#include "clang/StaticAnalyzer/PathSensitive/GRState.h"
22#include "clang/StaticAnalyzer/BugReporter/BugType.h"
23#include "clang/StaticAnalyzer/PathSensitive/MemRegion.h"
24#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h"
25#include "clang/StaticAnalyzer/Checkers/LocalCheckers.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"
30
31using namespace clang;
32using namespace ento;
33
34namespace {
35class APIMisuse : public BugType {
36public:
37  APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
38};
39} // end anonymous namespace
40
41//===----------------------------------------------------------------------===//
42// Utility functions.
43//===----------------------------------------------------------------------===//
44
45static const ObjCInterfaceType* GetReceiverType(const ObjCMessage &msg) {
46  if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
47    return ID->getTypeForDecl()->getAs<ObjCInterfaceType>();
48  return NULL;
49}
50
51static const char* GetReceiverNameType(const ObjCMessage &msg) {
52  if (const ObjCInterfaceType *ReceiverType = GetReceiverType(msg))
53    return ReceiverType->getDecl()->getIdentifier()->getNameStart();
54  return NULL;
55}
56
57static bool isNSString(llvm::StringRef ClassName) {
58  return ClassName == "NSString" || ClassName == "NSMutableString";
59}
60
61static inline bool isNil(SVal X) {
62  return isa<loc::ConcreteInt>(X);
63}
64
65//===----------------------------------------------------------------------===//
66// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
67//===----------------------------------------------------------------------===//
68
69namespace {
70  class NilArgChecker : public CheckerVisitor<NilArgChecker> {
71    APIMisuse *BT;
72    void WarnNilArg(CheckerContext &C, const ObjCMessage &msg, unsigned Arg);
73  public:
74    NilArgChecker() : BT(0) {}
75    static void *getTag() { static int x = 0; return &x; }
76    void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg);
77  };
78}
79
80void NilArgChecker::WarnNilArg(CheckerContext &C,
81                               const ObjCMessage &msg,
82                               unsigned int Arg)
83{
84  if (!BT)
85    BT = new APIMisuse("nil argument");
86
87  if (ExplodedNode *N = C.generateSink()) {
88    llvm::SmallString<128> sbuf;
89    llvm::raw_svector_ostream os(sbuf);
90    os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
91       << msg.getSelector().getAsString() << "' cannot be nil";
92
93    RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
94    R->addRange(msg.getArgSourceRange(Arg));
95    C.EmitReport(R);
96  }
97}
98
99void NilArgChecker::preVisitObjCMessage(CheckerContext &C,
100                                        ObjCMessage msg)
101{
102  const ObjCInterfaceType *ReceiverType = GetReceiverType(msg);
103  if (!ReceiverType)
104    return;
105
106  if (isNSString(ReceiverType->getDecl()->getIdentifier()->getName())) {
107    Selector S = msg.getSelector();
108
109    if (S.isUnarySelector())
110      return;
111
112    // FIXME: This is going to be really slow doing these checks with
113    //  lexical comparisons.
114
115    std::string NameStr = S.getAsString();
116    llvm::StringRef Name(NameStr);
117    assert(!Name.empty());
118
119    // FIXME: Checking for initWithFormat: will not work in most cases
120    //  yet because [NSString alloc] returns id, not NSString*.  We will
121    //  need support for tracking expected-type information in the analyzer
122    //  to find these errors.
123    if (Name == "caseInsensitiveCompare:" ||
124        Name == "compare:" ||
125        Name == "compare:options:" ||
126        Name == "compare:options:range:" ||
127        Name == "compare:options:range:locale:" ||
128        Name == "componentsSeparatedByCharactersInSet:" ||
129        Name == "initWithFormat:") {
130      if (isNil(msg.getArgSVal(0, C.getState())))
131        WarnNilArg(C, msg, 0);
132    }
133  }
134}
135
136//===----------------------------------------------------------------------===//
137// Error reporting.
138//===----------------------------------------------------------------------===//
139
140namespace {
141class CFNumberCreateChecker : public CheckerVisitor<CFNumberCreateChecker> {
142  APIMisuse* BT;
143  IdentifierInfo* II;
144public:
145  CFNumberCreateChecker() : BT(0), II(0) {}
146  ~CFNumberCreateChecker() {}
147  static void *getTag() { static int x = 0; return &x; }
148  void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE);
149private:
150  void EmitError(const TypedRegion* R, const Expr* Ex,
151                uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
152};
153} // end anonymous namespace
154
155enum CFNumberType {
156  kCFNumberSInt8Type = 1,
157  kCFNumberSInt16Type = 2,
158  kCFNumberSInt32Type = 3,
159  kCFNumberSInt64Type = 4,
160  kCFNumberFloat32Type = 5,
161  kCFNumberFloat64Type = 6,
162  kCFNumberCharType = 7,
163  kCFNumberShortType = 8,
164  kCFNumberIntType = 9,
165  kCFNumberLongType = 10,
166  kCFNumberLongLongType = 11,
167  kCFNumberFloatType = 12,
168  kCFNumberDoubleType = 13,
169  kCFNumberCFIndexType = 14,
170  kCFNumberNSIntegerType = 15,
171  kCFNumberCGFloatType = 16
172};
173
174namespace {
175  template<typename T>
176  class Optional {
177    bool IsKnown;
178    T Val;
179  public:
180    Optional() : IsKnown(false), Val(0) {}
181    Optional(const T& val) : IsKnown(true), Val(val) {}
182
183    bool isKnown() const { return IsKnown; }
184
185    const T& getValue() const {
186      assert (isKnown());
187      return Val;
188    }
189
190    operator const T&() const {
191      return getValue();
192    }
193  };
194}
195
196static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
197  static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
198
199  if (i < kCFNumberCharType)
200    return FixedSize[i-1];
201
202  QualType T;
203
204  switch (i) {
205    case kCFNumberCharType:     T = Ctx.CharTy;     break;
206    case kCFNumberShortType:    T = Ctx.ShortTy;    break;
207    case kCFNumberIntType:      T = Ctx.IntTy;      break;
208    case kCFNumberLongType:     T = Ctx.LongTy;     break;
209    case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
210    case kCFNumberFloatType:    T = Ctx.FloatTy;    break;
211    case kCFNumberDoubleType:   T = Ctx.DoubleTy;   break;
212    case kCFNumberCFIndexType:
213    case kCFNumberNSIntegerType:
214    case kCFNumberCGFloatType:
215      // FIXME: We need a way to map from names to Type*.
216    default:
217      return Optional<uint64_t>();
218  }
219
220  return Ctx.getTypeSize(T);
221}
222
223#if 0
224static const char* GetCFNumberTypeStr(uint64_t i) {
225  static const char* Names[] = {
226    "kCFNumberSInt8Type",
227    "kCFNumberSInt16Type",
228    "kCFNumberSInt32Type",
229    "kCFNumberSInt64Type",
230    "kCFNumberFloat32Type",
231    "kCFNumberFloat64Type",
232    "kCFNumberCharType",
233    "kCFNumberShortType",
234    "kCFNumberIntType",
235    "kCFNumberLongType",
236    "kCFNumberLongLongType",
237    "kCFNumberFloatType",
238    "kCFNumberDoubleType",
239    "kCFNumberCFIndexType",
240    "kCFNumberNSIntegerType",
241    "kCFNumberCGFloatType"
242  };
243
244  return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
245}
246#endif
247
248void CFNumberCreateChecker::PreVisitCallExpr(CheckerContext &C,
249                                             const CallExpr *CE)
250{
251  const Expr* Callee = CE->getCallee();
252  const GRState *state = C.getState();
253  SVal CallV = state->getSVal(Callee);
254  const FunctionDecl* FD = CallV.getAsFunctionDecl();
255
256  if (!FD)
257    return;
258
259  ASTContext &Ctx = C.getASTContext();
260  if (!II)
261    II = &Ctx.Idents.get("CFNumberCreate");
262
263  if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
264    return;
265
266  // Get the value of the "theType" argument.
267  SVal TheTypeVal = state->getSVal(CE->getArg(1));
268
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);
272  if (!V)
273    return;
274
275  uint64_t NumberKind = V->getValue().getLimitedValue();
276  Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
277
278  // FIXME: In some cases we can emit an error.
279  if (!TargetSize.isKnown())
280    return;
281
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));
286
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);
290  if (!LV)
291    return;
292
293  const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
294  if (!R)
295    return;
296
297  QualType T = Ctx.getCanonicalType(R->getValueType());
298
299  // FIXME: If the pointee isn't an integer type, should we flag a warning?
300  //  People can do weird stuff with pointers.
301
302  if (!T->isIntegerType())
303    return;
304
305  uint64_t SourceSize = Ctx.getTypeSize(T);
306
307  // CHECK: is SourceSize == TargetSize
308  if (SourceSize == TargetSize)
309    return;
310
311  // Generate an error.  Only generate a sink if 'SourceSize < TargetSize';
312  // otherwise generate a regular node.
313  //
314  // FIXME: We can actually create an abstract "CFNumber" object that has
315  //  the bits initialized to the provided values.
316  //
317  if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
318                                                : C.generateNode()) {
319    llvm::SmallString<128> sbuf;
320    llvm::raw_svector_ostream os(sbuf);
321
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. ";
327
328    if (SourceSize < TargetSize)
329      os << (TargetSize - SourceSize)
330      << " bits of the CFNumber value will be garbage." ;
331    else
332      os << (SourceSize - TargetSize)
333      << " bits of the input integer will be lost.";
334
335    if (!BT)
336      BT = new APIMisuse("Bad use of CFNumberCreate");
337
338    RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
339    report->addRange(CE->getArg(2)->getSourceRange());
340    C.EmitReport(report);
341  }
342}
343
344//===----------------------------------------------------------------------===//
345// CFRetain/CFRelease checking for null arguments.
346//===----------------------------------------------------------------------===//
347
348namespace {
349class CFRetainReleaseChecker : public CheckerVisitor<CFRetainReleaseChecker> {
350  APIMisuse *BT;
351  IdentifierInfo *Retain, *Release;
352public:
353  CFRetainReleaseChecker(): BT(0), Retain(0), Release(0) {}
354  static void *getTag() { static int x = 0; return &x; }
355  void PreVisitCallExpr(CheckerContext& C, const CallExpr* CE);
356};
357} // end anonymous namespace
358
359
360void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C,
361                                              const CallExpr* CE) {
362  // If the CallExpr doesn't have exactly 1 argument just give up checking.
363  if (CE->getNumArgs() != 1)
364    return;
365
366  // Get the function declaration of the callee.
367  const GRState* state = C.getState();
368  SVal X = state->getSVal(CE->getCallee());
369  const FunctionDecl* FD = X.getAsFunctionDecl();
370
371  if (!FD)
372    return;
373
374  if (!BT) {
375    ASTContext &Ctx = C.getASTContext();
376    Retain = &Ctx.Idents.get("CFRetain");
377    Release = &Ctx.Idents.get("CFRelease");
378    BT = new APIMisuse("null passed to CFRetain/CFRelease");
379  }
380
381  // Check if we called CFRetain/CFRelease.
382  const IdentifierInfo *FuncII = FD->getIdentifier();
383  if (!(FuncII == Retain || FuncII == Release))
384    return;
385
386  // FIXME: The rest of this just checks that the argument is non-null.
387  // It should probably be refactored and combined with AttrNonNullChecker.
388
389  // Get the argument's value.
390  const Expr *Arg = CE->getArg(0);
391  SVal ArgVal = state->getSVal(Arg);
392  DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
393  if (!DefArgVal)
394    return;
395
396  // Get a NULL value.
397  SValBuilder &svalBuilder = C.getSValBuilder();
398  DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
399
400  // Make an expression asserting that they're equal.
401  DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
402
403  // Are they equal?
404  const GRState *stateTrue, *stateFalse;
405  llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
406
407  if (stateTrue && !stateFalse) {
408    ExplodedNode *N = C.generateSink(stateTrue);
409    if (!N)
410      return;
411
412    const char *description = (FuncII == Retain)
413                            ? "Null pointer argument in call to CFRetain"
414                            : "Null pointer argument in call to CFRelease";
415
416    EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N);
417    report->addRange(Arg->getSourceRange());
418    report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg);
419    C.EmitReport(report);
420    return;
421  }
422
423  // From here on, we know the argument is non-null.
424  C.addTransition(stateFalse);
425}
426
427//===----------------------------------------------------------------------===//
428// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
429//===----------------------------------------------------------------------===//
430
431namespace {
432class ClassReleaseChecker : public CheckerVisitor<ClassReleaseChecker> {
433  Selector releaseS;
434  Selector retainS;
435  Selector autoreleaseS;
436  Selector drainS;
437  BugType *BT;
438public:
439  ClassReleaseChecker()
440    : BT(0) {}
441
442  static void *getTag() { static int x = 0; return &x; }
443
444  void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg);
445};
446}
447
448void ClassReleaseChecker::preVisitObjCMessage(CheckerContext &C,
449                                              ObjCMessage msg) {
450
451  if (!BT) {
452    BT = new APIMisuse("message incorrectly sent to class instead of class "
453                       "instance");
454
455    ASTContext &Ctx = C.getASTContext();
456    releaseS = GetNullarySelector("release", Ctx);
457    retainS = GetNullarySelector("retain", Ctx);
458    autoreleaseS = GetNullarySelector("autorelease", Ctx);
459    drainS = GetNullarySelector("drain", Ctx);
460  }
461
462  if (msg.isInstanceMessage())
463    return;
464  const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
465  assert(Class);
466
467  Selector S = msg.getSelector();
468  if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
469    return;
470
471  if (ExplodedNode *N = C.generateNode()) {
472    llvm::SmallString<200> buf;
473    llvm::raw_svector_ostream os(buf);
474
475    os << "The '" << S.getAsString() << "' message should be sent to instances "
476          "of class '" << Class->getName()
477       << "' and not the class directly";
478
479    RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
480    report->addRange(msg.getSourceRange());
481    C.EmitReport(report);
482  }
483}
484
485//===----------------------------------------------------------------------===//
486// Check registration.
487//===----------------------------------------------------------------------===//
488
489void ento::RegisterAppleChecks(ExprEngine& Eng, const Decl &D) {
490  Eng.registerCheck(new NilArgChecker());
491  Eng.registerCheck(new CFNumberCreateChecker());
492  RegisterNSErrorChecks(Eng.getBugReporter(), Eng, D);
493  RegisterNSAutoreleasePoolChecks(Eng);
494  Eng.registerCheck(new CFRetainReleaseChecker());
495  Eng.registerCheck(new ClassReleaseChecker());
496}
497