BasicObjCFoundationChecks.cpp revision 983326f32c746f5e47161a73758e4d363263dd2c
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/CheckerV2.h"
20#include "clang/StaticAnalyzer/Core/CheckerManager.h"
21#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
23#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
24#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
25#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
26#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.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 CheckerV2<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    RangedBugReport *R = new RangedBugReport(*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 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 CheckerV2< 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 Expr* Callee = CE->getCallee();
253  const GRState *state = C.getState();
254  SVal CallV = state->getSVal(Callee);
255  const FunctionDecl* FD = CallV.getAsFunctionDecl();
256
257  if (!FD)
258    return;
259
260  ASTContext &Ctx = C.getASTContext();
261  if (!II)
262    II = &Ctx.Idents.get("CFNumberCreate");
263
264  if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
265    return;
266
267  // Get the value of the "theType" argument.
268  SVal TheTypeVal = state->getSVal(CE->getArg(1));
269
270  // FIXME: We really should allow ranges of valid theType values, and
271  //   bifurcate the state appropriately.
272  nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
273  if (!V)
274    return;
275
276  uint64_t NumberKind = V->getValue().getLimitedValue();
277  Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
278
279  // FIXME: In some cases we can emit an error.
280  if (!TargetSize.isKnown())
281    return;
282
283  // Look at the value of the integer being passed by reference.  Essentially
284  // we want to catch cases where the value passed in is not equal to the
285  // size of the type being created.
286  SVal TheValueExpr = state->getSVal(CE->getArg(2));
287
288  // FIXME: Eventually we should handle arbitrary locations.  We can do this
289  //  by having an enhanced memory model that does low-level typing.
290  loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
291  if (!LV)
292    return;
293
294  const TypedRegion* R = dyn_cast<TypedRegion>(LV->stripCasts());
295  if (!R)
296    return;
297
298  QualType T = Ctx.getCanonicalType(R->getValueType());
299
300  // FIXME: If the pointee isn't an integer type, should we flag a warning?
301  //  People can do weird stuff with pointers.
302
303  if (!T->isIntegerType())
304    return;
305
306  uint64_t SourceSize = Ctx.getTypeSize(T);
307
308  // CHECK: is SourceSize == TargetSize
309  if (SourceSize == TargetSize)
310    return;
311
312  // Generate an error.  Only generate a sink if 'SourceSize < TargetSize';
313  // otherwise generate a regular node.
314  //
315  // FIXME: We can actually create an abstract "CFNumber" object that has
316  //  the bits initialized to the provided values.
317  //
318  if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
319                                                : C.generateNode()) {
320    llvm::SmallString<128> sbuf;
321    llvm::raw_svector_ostream os(sbuf);
322
323    os << (SourceSize == 8 ? "An " : "A ")
324       << SourceSize << " bit integer is used to initialize a CFNumber "
325                        "object that represents "
326       << (TargetSize == 8 ? "an " : "a ")
327       << TargetSize << " bit integer. ";
328
329    if (SourceSize < TargetSize)
330      os << (TargetSize - SourceSize)
331      << " bits of the CFNumber value will be garbage." ;
332    else
333      os << (SourceSize - TargetSize)
334      << " bits of the input integer will be lost.";
335
336    if (!BT)
337      BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
338
339    RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
340    report->addRange(CE->getArg(2)->getSourceRange());
341    C.EmitReport(report);
342  }
343}
344
345//===----------------------------------------------------------------------===//
346// CFRetain/CFRelease checking for null arguments.
347//===----------------------------------------------------------------------===//
348
349namespace {
350class CFRetainReleaseChecker : public CheckerV2< check::PreStmt<CallExpr> > {
351  mutable llvm::OwningPtr<APIMisuse> BT;
352  mutable IdentifierInfo *Retain, *Release;
353public:
354  CFRetainReleaseChecker(): Retain(0), Release(0) {}
355  void checkPreStmt(const CallExpr* CE, CheckerContext& C) const;
356};
357} // end anonymous namespace
358
359
360void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE,
361                                          CheckerContext& C) const {
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.reset(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 CheckerV2<check::PreObjCMessage> {
433  mutable Selector releaseS;
434  mutable Selector retainS;
435  mutable Selector autoreleaseS;
436  mutable Selector drainS;
437  mutable llvm::OwningPtr<BugType> BT;
438
439public:
440  void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
441};
442}
443
444void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
445                                              CheckerContext &C) const {
446
447  if (!BT) {
448    BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
449                           "instance"));
450
451    ASTContext &Ctx = C.getASTContext();
452    releaseS = GetNullarySelector("release", Ctx);
453    retainS = GetNullarySelector("retain", Ctx);
454    autoreleaseS = GetNullarySelector("autorelease", Ctx);
455    drainS = GetNullarySelector("drain", Ctx);
456  }
457
458  if (msg.isInstanceMessage())
459    return;
460  const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
461  assert(Class);
462
463  Selector S = msg.getSelector();
464  if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
465    return;
466
467  if (ExplodedNode *N = C.generateNode()) {
468    llvm::SmallString<200> buf;
469    llvm::raw_svector_ostream os(buf);
470
471    os << "The '" << S.getAsString() << "' message should be sent to instances "
472          "of class '" << Class->getName()
473       << "' and not the class directly";
474
475    RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
476    report->addRange(msg.getSourceRange());
477    C.EmitReport(report);
478  }
479}
480
481//===----------------------------------------------------------------------===//
482// Check registration.
483//===----------------------------------------------------------------------===//
484
485void ento::registerNilArgChecker(CheckerManager &mgr) {
486  mgr.registerChecker<NilArgChecker>();
487}
488
489void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
490  mgr.registerChecker<CFNumberCreateChecker>();
491}
492
493void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
494  mgr.registerChecker<CFRetainReleaseChecker>();
495}
496
497void ento::registerClassReleaseChecker(CheckerManager &mgr) {
498  mgr.registerChecker<ClassReleaseChecker>();
499}
500