BasicObjCFoundationChecks.cpp revision 695fb502825a53ccd178ec1c85c77929d88acb71
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 "ClangSACheckers.h"
19#include "clang/StaticAnalyzer/Core/CheckerManager.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
21#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h"
22#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
23#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
24#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
25#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
26#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h"
27#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
28#include "clang/AST/DeclObjC.h"
29#include "clang/AST/Expr.h"
30#include "clang/AST/ExprObjC.h"
31#include "clang/AST/ASTContext.h"
32
33using namespace clang;
34using namespace ento;
35
36namespace {
37class APIMisuse : public BugType {
38public:
39  APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
40};
41} // end anonymous namespace
42
43//===----------------------------------------------------------------------===//
44// Utility functions.
45//===----------------------------------------------------------------------===//
46
47static const ObjCInterfaceType* GetReceiverType(const ObjCMessage &msg) {
48  if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
49    return ID->getTypeForDecl()->getAs<ObjCInterfaceType>();
50  return NULL;
51}
52
53static const char* GetReceiverNameType(const ObjCMessage &msg) {
54  if (const ObjCInterfaceType *ReceiverType = GetReceiverType(msg))
55    return ReceiverType->getDecl()->getIdentifier()->getNameStart();
56  return NULL;
57}
58
59static bool isNSString(llvm::StringRef ClassName) {
60  return ClassName == "NSString" || ClassName == "NSMutableString";
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 CheckerVisitor<NilArgChecker> {
73    APIMisuse *BT;
74    void WarnNilArg(CheckerContext &C, const ObjCMessage &msg, unsigned Arg);
75  public:
76    NilArgChecker() : BT(0) {}
77    static void *getTag() { static int x = 0; return &x; }
78    void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg);
79  };
80}
81
82void NilArgChecker::WarnNilArg(CheckerContext &C,
83                               const ObjCMessage &msg,
84                               unsigned int Arg)
85{
86  if (!BT)
87    BT = new APIMisuse("nil argument");
88
89  if (ExplodedNode *N = C.generateSink()) {
90    llvm::SmallString<128> sbuf;
91    llvm::raw_svector_ostream os(sbuf);
92    os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
93       << msg.getSelector().getAsString() << "' cannot be nil";
94
95    RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
96    R->addRange(msg.getArgSourceRange(Arg));
97    C.EmitReport(R);
98  }
99}
100
101void NilArgChecker::preVisitObjCMessage(CheckerContext &C,
102                                        ObjCMessage msg)
103{
104  const ObjCInterfaceType *ReceiverType = GetReceiverType(msg);
105  if (!ReceiverType)
106    return;
107
108  if (isNSString(ReceiverType->getDecl()->getIdentifier()->getName())) {
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    llvm::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 CheckerVisitor<CFNumberCreateChecker> {
144  APIMisuse* BT;
145  IdentifierInfo* II;
146public:
147  CFNumberCreateChecker() : BT(0), II(0) {}
148  ~CFNumberCreateChecker() {}
149  static void *getTag() { static int x = 0; return &x; }
150  void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE);
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::PreVisitCallExpr(CheckerContext &C,
251                                             const CallExpr *CE)
252{
253  const Expr* Callee = CE->getCallee();
254  const GRState *state = C.getState();
255  SVal CallV = state->getSVal(Callee);
256  const FunctionDecl* FD = CallV.getAsFunctionDecl();
257
258  if (!FD)
259    return;
260
261  ASTContext &Ctx = C.getASTContext();
262  if (!II)
263    II = &Ctx.Idents.get("CFNumberCreate");
264
265  if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
266    return;
267
268  // Get the value of the "theType" argument.
269  SVal TheTypeVal = state->getSVal(CE->getArg(1));
270
271  // FIXME: We really should allow ranges of valid theType values, and
272  //   bifurcate the state appropriately.
273  nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
274  if (!V)
275    return;
276
277  uint64_t NumberKind = V->getValue().getLimitedValue();
278  Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
279
280  // FIXME: In some cases we can emit an error.
281  if (!TargetSize.isKnown())
282    return;
283
284  // Look at the value of the integer being passed by reference.  Essentially
285  // we want to catch cases where the value passed in is not equal to the
286  // size of the type being created.
287  SVal TheValueExpr = state->getSVal(CE->getArg(2));
288
289  // FIXME: Eventually we should handle arbitrary locations.  We can do this
290  //  by having an enhanced memory model that does low-level typing.
291  loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
292  if (!LV)
293    return;
294
295  const TypedRegion* R = dyn_cast<TypedRegion>(LV->stripCasts());
296  if (!R)
297    return;
298
299  QualType T = Ctx.getCanonicalType(R->getValueType());
300
301  // FIXME: If the pointee isn't an integer type, should we flag a warning?
302  //  People can do weird stuff with pointers.
303
304  if (!T->isIntegerType())
305    return;
306
307  uint64_t SourceSize = Ctx.getTypeSize(T);
308
309  // CHECK: is SourceSize == TargetSize
310  if (SourceSize == TargetSize)
311    return;
312
313  // Generate an error.  Only generate a sink if 'SourceSize < TargetSize';
314  // otherwise generate a regular node.
315  //
316  // FIXME: We can actually create an abstract "CFNumber" object that has
317  //  the bits initialized to the provided values.
318  //
319  if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
320                                                : C.generateNode()) {
321    llvm::SmallString<128> sbuf;
322    llvm::raw_svector_ostream os(sbuf);
323
324    os << (SourceSize == 8 ? "An " : "A ")
325       << SourceSize << " bit integer is used to initialize a CFNumber "
326                        "object that represents "
327       << (TargetSize == 8 ? "an " : "a ")
328       << TargetSize << " bit integer. ";
329
330    if (SourceSize < TargetSize)
331      os << (TargetSize - SourceSize)
332      << " bits of the CFNumber value will be garbage." ;
333    else
334      os << (SourceSize - TargetSize)
335      << " bits of the input integer will be lost.";
336
337    if (!BT)
338      BT = new APIMisuse("Bad use of CFNumberCreate");
339
340    RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
341    report->addRange(CE->getArg(2)->getSourceRange());
342    C.EmitReport(report);
343  }
344}
345
346//===----------------------------------------------------------------------===//
347// CFRetain/CFRelease checking for null arguments.
348//===----------------------------------------------------------------------===//
349
350namespace {
351class CFRetainReleaseChecker : public CheckerVisitor<CFRetainReleaseChecker> {
352  APIMisuse *BT;
353  IdentifierInfo *Retain, *Release;
354public:
355  CFRetainReleaseChecker(): BT(0), Retain(0), Release(0) {}
356  static void *getTag() { static int x = 0; return &x; }
357  void PreVisitCallExpr(CheckerContext& C, const CallExpr* CE);
358};
359} // end anonymous namespace
360
361
362void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C,
363                                              const CallExpr* CE) {
364  // If the CallExpr doesn't have exactly 1 argument just give up checking.
365  if (CE->getNumArgs() != 1)
366    return;
367
368  // Get the function declaration of the callee.
369  const GRState* state = C.getState();
370  SVal X = state->getSVal(CE->getCallee());
371  const FunctionDecl* FD = X.getAsFunctionDecl();
372
373  if (!FD)
374    return;
375
376  if (!BT) {
377    ASTContext &Ctx = C.getASTContext();
378    Retain = &Ctx.Idents.get("CFRetain");
379    Release = &Ctx.Idents.get("CFRelease");
380    BT = new APIMisuse("null passed to CFRetain/CFRelease");
381  }
382
383  // Check if we called CFRetain/CFRelease.
384  const IdentifierInfo *FuncII = FD->getIdentifier();
385  if (!(FuncII == Retain || FuncII == Release))
386    return;
387
388  // FIXME: The rest of this just checks that the argument is non-null.
389  // It should probably be refactored and combined with AttrNonNullChecker.
390
391  // Get the argument's value.
392  const Expr *Arg = CE->getArg(0);
393  SVal ArgVal = state->getSVal(Arg);
394  DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
395  if (!DefArgVal)
396    return;
397
398  // Get a NULL value.
399  SValBuilder &svalBuilder = C.getSValBuilder();
400  DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
401
402  // Make an expression asserting that they're equal.
403  DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
404
405  // Are they equal?
406  const GRState *stateTrue, *stateFalse;
407  llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
408
409  if (stateTrue && !stateFalse) {
410    ExplodedNode *N = C.generateSink(stateTrue);
411    if (!N)
412      return;
413
414    const char *description = (FuncII == Retain)
415                            ? "Null pointer argument in call to CFRetain"
416                            : "Null pointer argument in call to CFRelease";
417
418    EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N);
419    report->addRange(Arg->getSourceRange());
420    report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg);
421    C.EmitReport(report);
422    return;
423  }
424
425  // From here on, we know the argument is non-null.
426  C.addTransition(stateFalse);
427}
428
429//===----------------------------------------------------------------------===//
430// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
431//===----------------------------------------------------------------------===//
432
433namespace {
434class ClassReleaseChecker : public CheckerVisitor<ClassReleaseChecker> {
435  Selector releaseS;
436  Selector retainS;
437  Selector autoreleaseS;
438  Selector drainS;
439  BugType *BT;
440public:
441  ClassReleaseChecker()
442    : BT(0) {}
443
444  static void *getTag() { static int x = 0; return &x; }
445
446  void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg);
447};
448}
449
450void ClassReleaseChecker::preVisitObjCMessage(CheckerContext &C,
451                                              ObjCMessage msg) {
452
453  if (!BT) {
454    BT = new APIMisuse("message incorrectly sent to class instead of class "
455                       "instance");
456
457    ASTContext &Ctx = C.getASTContext();
458    releaseS = GetNullarySelector("release", Ctx);
459    retainS = GetNullarySelector("retain", Ctx);
460    autoreleaseS = GetNullarySelector("autorelease", Ctx);
461    drainS = GetNullarySelector("drain", Ctx);
462  }
463
464  if (msg.isInstanceMessage())
465    return;
466  const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
467  assert(Class);
468
469  Selector S = msg.getSelector();
470  if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
471    return;
472
473  if (ExplodedNode *N = C.generateNode()) {
474    llvm::SmallString<200> buf;
475    llvm::raw_svector_ostream os(buf);
476
477    os << "The '" << S.getAsString() << "' message should be sent to instances "
478          "of class '" << Class->getName()
479       << "' and not the class directly";
480
481    RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
482    report->addRange(msg.getSourceRange());
483    C.EmitReport(report);
484  }
485}
486
487//===----------------------------------------------------------------------===//
488// Check registration.
489//===----------------------------------------------------------------------===//
490
491static void RegisterNilArgChecker(ExprEngine& Eng) {
492  Eng.registerCheck(new NilArgChecker());
493}
494
495void ento::registerNilArgChecker(CheckerManager &mgr) {
496  mgr.addCheckerRegisterFunction(RegisterNilArgChecker);
497}
498
499static void RegisterCFNumberCreateChecker(ExprEngine& Eng) {
500  Eng.registerCheck(new CFNumberCreateChecker());
501}
502
503void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
504  mgr.addCheckerRegisterFunction(RegisterCFNumberCreateChecker);
505}
506
507static void RegisterCFRetainReleaseChecker(ExprEngine& Eng) {
508  Eng.registerCheck(new CFRetainReleaseChecker());
509}
510
511void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
512  mgr.addCheckerRegisterFunction(RegisterCFRetainReleaseChecker);
513}
514
515static void RegisterClassReleaseChecker(ExprEngine& Eng) {
516  Eng.registerCheck(new ClassReleaseChecker());
517}
518
519void ento::registerClassReleaseChecker(CheckerManager &mgr) {
520  mgr.addCheckerRegisterFunction(RegisterClassReleaseChecker);
521}
522