]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/Checker/BasicObjCFoundationChecks.cpp
Update clang to r97873.
[FreeBSD/FreeBSD.git] / lib / Checker / BasicObjCFoundationChecks.cpp
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/Checker/PathSensitive/ExplodedGraph.h"
19 #include "clang/Checker/PathSensitive/GRSimpleAPICheck.h"
20 #include "clang/Checker/PathSensitive/GRExprEngine.h"
21 #include "clang/Checker/PathSensitive/GRState.h"
22 #include "clang/Checker/BugReporter/BugReporter.h"
23 #include "clang/Checker/PathSensitive/MemRegion.h"
24 #include "clang/Checker/BugReporter/PathDiagnostic.h"
25 #include "clang/Checker/PathSensitive/CheckerVisitor.h"
26 #include "clang/Checker/Checkers/LocalCheckers.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
32 using namespace clang;
33
34 static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) {
35   const Expr* Receiver = ME->getReceiver();
36
37   if (!Receiver)
38     return NULL;
39
40   if (const ObjCObjectPointerType *PT =
41       Receiver->getType()->getAs<ObjCObjectPointerType>())
42     return PT->getInterfaceType();
43
44   return NULL;
45 }
46
47 static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
48   if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME))
49     return ReceiverType->getDecl()->getIdentifier()->getNameStart();
50   return NULL;
51 }
52
53 namespace {
54
55 class APIMisuse : public BugType {
56 public:
57   APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
58 };
59
60 class BasicObjCFoundationChecks : public GRSimpleAPICheck {
61   APIMisuse *BT;
62   BugReporter& BR;
63   ASTContext &Ctx;
64
65   bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix);
66   bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME);
67
68   void Warn(ExplodedNode* N, const Expr* E, const std::string& s);
69   void WarnNilArg(ExplodedNode* N, const Expr* E);
70
71   bool CheckNilArg(ExplodedNode* N, unsigned Arg);
72
73 public:
74   BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br)
75     : BT(0), BR(br), Ctx(ctx) {}
76
77   bool Audit(ExplodedNode* N, GRStateManager&);
78
79 private:
80   void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) {
81     std::string sbuf;
82     llvm::raw_string_ostream os(sbuf);
83     os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
84        << ME->getSelector().getAsString() << "' cannot be nil.";
85
86     // Lazily create the BugType object for NilArg.  This will be owned
87     // by the BugReporter object 'BR' once we call BR.EmitWarning.
88     if (!BT) BT = new APIMisuse("nil argument");
89
90     RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
91     R->addRange(ME->getArg(Arg)->getSourceRange());
92     BR.EmitReport(R);
93   }
94 };
95
96 } // end anonymous namespace
97
98
99 GRSimpleAPICheck*
100 clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) {
101   return new BasicObjCFoundationChecks(Ctx, BR);
102 }
103
104
105
106 bool BasicObjCFoundationChecks::Audit(ExplodedNode* N,
107                                       GRStateManager&) {
108
109   const ObjCMessageExpr* ME =
110     cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
111
112   const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
113
114   if (!ReceiverType)
115     return false;
116
117   if (isNSString(ReceiverType,
118                  ReceiverType->getDecl()->getIdentifier()->getName()))
119     return AuditNSString(N, ME);
120
121   return false;
122 }
123
124 static inline bool isNil(SVal X) {
125   return isa<loc::ConcreteInt>(X);
126 }
127
128 //===----------------------------------------------------------------------===//
129 // Error reporting.
130 //===----------------------------------------------------------------------===//
131
132 bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) {
133   const ObjCMessageExpr* ME =
134     cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
135
136   const Expr * E = ME->getArg(Arg);
137
138   if (isNil(N->getState()->getSVal(E))) {
139     WarnNilArg(N, ME, Arg);
140     return true;
141   }
142
143   return false;
144 }
145
146 //===----------------------------------------------------------------------===//
147 // NSString checking.
148 //===----------------------------------------------------------------------===//
149
150 bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T,
151                                            llvm::StringRef ClassName) {
152   return ClassName == "NSString" || ClassName == "NSMutableString";
153 }
154
155 bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N,
156                                               const ObjCMessageExpr* ME) {
157
158   Selector S = ME->getSelector();
159
160   if (S.isUnarySelector())
161     return false;
162
163   // FIXME: This is going to be really slow doing these checks with
164   //  lexical comparisons.
165
166   std::string NameStr = S.getAsString();
167   llvm::StringRef Name(NameStr);
168   assert(!Name.empty());
169
170   // FIXME: Checking for initWithFormat: will not work in most cases
171   //  yet because [NSString alloc] returns id, not NSString*.  We will
172   //  need support for tracking expected-type information in the analyzer
173   //  to find these errors.
174   if (Name == "caseInsensitiveCompare:" ||
175       Name == "compare:" ||
176       Name == "compare:options:" ||
177       Name == "compare:options:range:" ||
178       Name == "compare:options:range:locale:" ||
179       Name == "componentsSeparatedByCharactersInSet:" ||
180       Name == "initWithFormat:")
181     return CheckNilArg(N, 0);
182
183   return false;
184 }
185
186 //===----------------------------------------------------------------------===//
187 // Error reporting.
188 //===----------------------------------------------------------------------===//
189
190 namespace {
191
192 class AuditCFNumberCreate : public GRSimpleAPICheck {
193   APIMisuse* BT;
194
195   // FIXME: Either this should be refactored into GRSimpleAPICheck, or
196   //   it should always be passed with a call to Audit.  The latter
197   //   approach makes this class more stateless.
198   ASTContext& Ctx;
199   IdentifierInfo* II;
200   BugReporter& BR;
201
202 public:
203   AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
204   : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
205
206   ~AuditCFNumberCreate() {}
207
208   bool Audit(ExplodedNode* N, GRStateManager&);
209
210 private:
211   void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N,
212                 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
213 };
214 } // end anonymous namespace
215
216 enum CFNumberType {
217   kCFNumberSInt8Type = 1,
218   kCFNumberSInt16Type = 2,
219   kCFNumberSInt32Type = 3,
220   kCFNumberSInt64Type = 4,
221   kCFNumberFloat32Type = 5,
222   kCFNumberFloat64Type = 6,
223   kCFNumberCharType = 7,
224   kCFNumberShortType = 8,
225   kCFNumberIntType = 9,
226   kCFNumberLongType = 10,
227   kCFNumberLongLongType = 11,
228   kCFNumberFloatType = 12,
229   kCFNumberDoubleType = 13,
230   kCFNumberCFIndexType = 14,
231   kCFNumberNSIntegerType = 15,
232   kCFNumberCGFloatType = 16
233 };
234
235 namespace {
236   template<typename T>
237   class Optional {
238     bool IsKnown;
239     T Val;
240   public:
241     Optional() : IsKnown(false), Val(0) {}
242     Optional(const T& val) : IsKnown(true), Val(val) {}
243
244     bool isKnown() const { return IsKnown; }
245
246     const T& getValue() const {
247       assert (isKnown());
248       return Val;
249     }
250
251     operator const T&() const {
252       return getValue();
253     }
254   };
255 }
256
257 static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
258   static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
259
260   if (i < kCFNumberCharType)
261     return FixedSize[i-1];
262
263   QualType T;
264
265   switch (i) {
266     case kCFNumberCharType:     T = Ctx.CharTy;     break;
267     case kCFNumberShortType:    T = Ctx.ShortTy;    break;
268     case kCFNumberIntType:      T = Ctx.IntTy;      break;
269     case kCFNumberLongType:     T = Ctx.LongTy;     break;
270     case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
271     case kCFNumberFloatType:    T = Ctx.FloatTy;    break;
272     case kCFNumberDoubleType:   T = Ctx.DoubleTy;   break;
273     case kCFNumberCFIndexType:
274     case kCFNumberNSIntegerType:
275     case kCFNumberCGFloatType:
276       // FIXME: We need a way to map from names to Type*.
277     default:
278       return Optional<uint64_t>();
279   }
280
281   return Ctx.getTypeSize(T);
282 }
283
284 #if 0
285 static const char* GetCFNumberTypeStr(uint64_t i) {
286   static const char* Names[] = {
287     "kCFNumberSInt8Type",
288     "kCFNumberSInt16Type",
289     "kCFNumberSInt32Type",
290     "kCFNumberSInt64Type",
291     "kCFNumberFloat32Type",
292     "kCFNumberFloat64Type",
293     "kCFNumberCharType",
294     "kCFNumberShortType",
295     "kCFNumberIntType",
296     "kCFNumberLongType",
297     "kCFNumberLongLongType",
298     "kCFNumberFloatType",
299     "kCFNumberDoubleType",
300     "kCFNumberCFIndexType",
301     "kCFNumberNSIntegerType",
302     "kCFNumberCGFloatType"
303   };
304
305   return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
306 }
307 #endif
308
309 bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
310   const CallExpr* CE =
311     cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
312   const Expr* Callee = CE->getCallee();
313   SVal CallV = N->getState()->getSVal(Callee);
314   const FunctionDecl* FD = CallV.getAsFunctionDecl();
315
316   if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
317     return false;
318
319   // Get the value of the "theType" argument.
320   SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
321
322     // FIXME: We really should allow ranges of valid theType values, and
323     //   bifurcate the state appropriately.
324   nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
325
326   if (!V)
327     return false;
328
329   uint64_t NumberKind = V->getValue().getLimitedValue();
330   Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
331
332   // FIXME: In some cases we can emit an error.
333   if (!TargetSize.isKnown())
334     return false;
335
336   // Look at the value of the integer being passed by reference.  Essentially
337   // we want to catch cases where the value passed in is not equal to the
338   // size of the type being created.
339   SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
340
341   // FIXME: Eventually we should handle arbitrary locations.  We can do this
342   //  by having an enhanced memory model that does low-level typing.
343   loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
344
345   if (!LV)
346     return false;
347
348   const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
349
350   if (!R)
351     return false;
352
353   QualType T = Ctx.getCanonicalType(R->getValueType(Ctx));
354
355   // FIXME: If the pointee isn't an integer type, should we flag a warning?
356   //  People can do weird stuff with pointers.
357
358   if (!T->isIntegerType())
359     return false;
360
361   uint64_t SourceSize = Ctx.getTypeSize(T);
362
363   // CHECK: is SourceSize == TargetSize
364
365   if (SourceSize == TargetSize)
366     return false;
367
368   AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
369
370   // FIXME: We can actually create an abstract "CFNumber" object that has
371   //  the bits initialized to the provided values.
372   return SourceSize < TargetSize;
373 }
374
375 void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex,
376                                    ExplodedNode *N,
377                                    uint64_t SourceSize, uint64_t TargetSize,
378                                    uint64_t NumberKind) {
379
380   std::string sbuf;
381   llvm::raw_string_ostream os(sbuf);
382
383   os << (SourceSize == 8 ? "An " : "A ")
384      << SourceSize << " bit integer is used to initialize a CFNumber "
385         "object that represents "
386      << (TargetSize == 8 ? "an " : "a ")
387      << TargetSize << " bit integer. ";
388
389   if (SourceSize < TargetSize)
390     os << (TargetSize - SourceSize)
391        << " bits of the CFNumber value will be garbage." ;
392   else
393     os << (SourceSize - TargetSize)
394        << " bits of the input integer will be lost.";
395
396   // Lazily create the BugType object.  This will be owned
397   // by the BugReporter object 'BR' once we call BR.EmitWarning.
398   if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate");
399   RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
400   report->addRange(Ex->getSourceRange());
401   BR.EmitReport(report);
402 }
403
404 GRSimpleAPICheck*
405 clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
406   return new AuditCFNumberCreate(Ctx, BR);
407 }
408
409 //===----------------------------------------------------------------------===//
410 // CFRetain/CFRelease auditing for null arguments.
411 //===----------------------------------------------------------------------===//
412
413 namespace {
414 class AuditCFRetainRelease : public GRSimpleAPICheck {
415   APIMisuse *BT;
416
417   // FIXME: Either this should be refactored into GRSimpleAPICheck, or
418   //   it should always be passed with a call to Audit.  The latter
419   //   approach makes this class more stateless.
420   ASTContext& Ctx;
421   IdentifierInfo *Retain, *Release;
422   BugReporter& BR;
423
424 public:
425   AuditCFRetainRelease(ASTContext& ctx, BugReporter& br)
426   : BT(0), Ctx(ctx),
427     Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")),
428     BR(br){}
429
430   ~AuditCFRetainRelease() {}
431
432   bool Audit(ExplodedNode* N, GRStateManager&);
433 };
434 } // end anonymous namespace
435
436
437 bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) {
438   const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
439
440   // If the CallExpr doesn't have exactly 1 argument just give up checking.
441   if (CE->getNumArgs() != 1)
442     return false;
443
444   // Check if we called CFRetain/CFRelease.
445   const GRState* state = N->getState();
446   SVal X = state->getSVal(CE->getCallee());
447   const FunctionDecl* FD = X.getAsFunctionDecl();
448
449   if (!FD)
450     return false;
451
452   const IdentifierInfo *FuncII = FD->getIdentifier();
453   if (!(FuncII == Retain || FuncII == Release))
454     return false;
455
456   // Finally, check if the argument is NULL.
457   // FIXME: We should be able to bifurcate the state here, as a successful
458   // check will result in the value not being NULL afterwards.
459   // FIXME: Need a way to register vistors for the BugReporter.  Would like
460   // to benefit from the same diagnostics that regular null dereference
461   // reporting has.
462   if (state->getStateManager().isEqual(state, CE->getArg(0), 0)) {
463     if (!BT)
464       BT = new APIMisuse("null passed to CFRetain/CFRelease");
465
466     const char *description = (FuncII == Retain)
467                             ? "Null pointer argument in call to CFRetain"
468                             : "Null pointer argument in call to CFRelease";
469
470     RangedBugReport *report = new RangedBugReport(*BT, description, N);
471     report->addRange(CE->getArg(0)->getSourceRange());
472     BR.EmitReport(report);
473     return true;
474   }
475
476   return false;
477 }
478
479
480 GRSimpleAPICheck*
481 clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) {
482   return new AuditCFRetainRelease(Ctx, BR);
483 }
484
485 //===----------------------------------------------------------------------===//
486 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
487 //===----------------------------------------------------------------------===//
488
489 namespace {
490 class ClassReleaseChecker :
491     public CheckerVisitor<ClassReleaseChecker> {
492   Selector releaseS;
493   Selector retainS;
494   Selector autoreleaseS;
495   Selector drainS;
496   BugType *BT;
497 public:
498   ClassReleaseChecker(ASTContext &Ctx)
499     : releaseS(GetNullarySelector("release", Ctx)),
500       retainS(GetNullarySelector("retain", Ctx)),
501       autoreleaseS(GetNullarySelector("autorelease", Ctx)),
502       drainS(GetNullarySelector("drain", Ctx)),
503       BT(0) {}
504
505   static void *getTag() { static int x = 0; return &x; }
506       
507   void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);    
508 };
509 }
510
511 void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C,
512                                                   const ObjCMessageExpr *ME) {
513   
514   const IdentifierInfo *ClsName = ME->getClassName();
515   if (!ClsName)
516     return;
517   
518   Selector S = ME->getSelector();
519   if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
520     return;
521   
522   if (!BT)
523     BT = new APIMisuse("message incorrectly sent to class instead of class "
524                        "instance");
525   
526   ExplodedNode *N = C.GenerateNode();
527
528   if (!N)
529     return;
530   
531   llvm::SmallString<200> buf;
532   llvm::raw_svector_ostream os(buf);
533
534   os << "The '" << S.getAsString() << "' message should be sent to instances "
535         "of class '" << ClsName->getName()
536      << "' and not the class directly";
537   
538   RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
539   report->addRange(ME->getSourceRange());
540   C.EmitReport(report);
541 }
542
543 //===----------------------------------------------------------------------===//
544 // Check registration.
545 //===----------------------------------------------------------------------===//
546
547 void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) {
548   ASTContext& Ctx = Eng.getContext();
549   BugReporter &BR = Eng.getBugReporter();
550
551   Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
552                Stmt::ObjCMessageExprClass);
553   Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
554   Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass);
555
556   RegisterNSErrorChecks(BR, Eng, D);
557   RegisterNSAutoreleasePoolChecks(Eng);
558   Eng.registerCheck(new ClassReleaseChecker(Ctx));
559 }