xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp (revision d91d5f162f099eea10054ef3b083a18ca1558e0c)
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/StaticAnalyzer/Core/Checker.h"
18 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
23 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
24 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
25 #include "clang/AST/DeclObjC.h"
26 #include "clang/AST/Expr.h"
27 #include "clang/AST/ExprObjC.h"
28 #include "clang/AST/ASTContext.h"
29 
30 using namespace clang;
31 using namespace ento;
32 
33 namespace {
34 class APIMisuse : public BugType {
35 public:
36   APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
37 };
38 } // end anonymous namespace
39 
40 //===----------------------------------------------------------------------===//
41 // Utility functions.
42 //===----------------------------------------------------------------------===//
43 
44 static const char* GetReceiverNameType(const ObjCMessage &msg) {
45   if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
46     return ID->getIdentifier()->getNameStart();
47   return 0;
48 }
49 
50 static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID,
51                                         llvm::StringRef ClassName) {
52   if (ID->getIdentifier()->getName() == ClassName)
53     return true;
54 
55   if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
56     return isReceiverClassOrSuperclass(Super, ClassName);
57 
58   return false;
59 }
60 
61 static 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 
69 namespace {
70   class NilArgChecker : public Checker<check::PreObjCMessage> {
71     mutable llvm::OwningPtr<APIMisuse> BT;
72 
73     void WarnNilArg(CheckerContext &C,
74                     const ObjCMessage &msg, unsigned Arg) const;
75 
76   public:
77     void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
78   };
79 }
80 
81 void NilArgChecker::WarnNilArg(CheckerContext &C,
82                                const ObjCMessage &msg,
83                                unsigned int Arg) const
84 {
85   if (!BT)
86     BT.reset(new APIMisuse("nil argument"));
87 
88   if (ExplodedNode *N = C.generateSink()) {
89     llvm::SmallString<128> sbuf;
90     llvm::raw_svector_ostream os(sbuf);
91     os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
92        << msg.getSelector().getAsString() << "' cannot be nil";
93 
94     RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
95     R->addRange(msg.getArgSourceRange(Arg));
96     C.EmitReport(R);
97   }
98 }
99 
100 void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
101                                         CheckerContext &C) const {
102   const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
103   if (!ID)
104     return;
105 
106   if (isReceiverClassOrSuperclass(ID, "NSString")) {
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 
140 namespace {
141 class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
142   mutable llvm::OwningPtr<APIMisuse> BT;
143   mutable IdentifierInfo* II;
144 public:
145   CFNumberCreateChecker() : II(0) {}
146 
147   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
148 
149 private:
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 
155 enum 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 
174 namespace {
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 
196 static 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
224 static 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 
248 void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
249                                          CheckerContext &C) const {
250   const Expr* Callee = CE->getCallee();
251   const GRState *state = C.getState();
252   SVal CallV = state->getSVal(Callee);
253   const FunctionDecl* FD = CallV.getAsFunctionDecl();
254 
255   if (!FD)
256     return;
257 
258   ASTContext &Ctx = C.getASTContext();
259   if (!II)
260     II = &Ctx.Idents.get("CFNumberCreate");
261 
262   if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
263     return;
264 
265   // Get the value of the "theType" argument.
266   SVal TheTypeVal = state->getSVal(CE->getArg(1));
267 
268   // FIXME: We really should allow ranges of valid theType values, and
269   //   bifurcate the state appropriately.
270   nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
271   if (!V)
272     return;
273 
274   uint64_t NumberKind = V->getValue().getLimitedValue();
275   Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
276 
277   // FIXME: In some cases we can emit an error.
278   if (!TargetSize.isKnown())
279     return;
280 
281   // Look at the value of the integer being passed by reference.  Essentially
282   // we want to catch cases where the value passed in is not equal to the
283   // size of the type being created.
284   SVal TheValueExpr = state->getSVal(CE->getArg(2));
285 
286   // FIXME: Eventually we should handle arbitrary locations.  We can do this
287   //  by having an enhanced memory model that does low-level typing.
288   loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
289   if (!LV)
290     return;
291 
292   const TypedRegion* R = dyn_cast<TypedRegion>(LV->stripCasts());
293   if (!R)
294     return;
295 
296   QualType T = Ctx.getCanonicalType(R->getValueType());
297 
298   // FIXME: If the pointee isn't an integer type, should we flag a warning?
299   //  People can do weird stuff with pointers.
300 
301   if (!T->isIntegerType())
302     return;
303 
304   uint64_t SourceSize = Ctx.getTypeSize(T);
305 
306   // CHECK: is SourceSize == TargetSize
307   if (SourceSize == TargetSize)
308     return;
309 
310   // Generate an error.  Only generate a sink if 'SourceSize < TargetSize';
311   // otherwise generate a regular node.
312   //
313   // FIXME: We can actually create an abstract "CFNumber" object that has
314   //  the bits initialized to the provided values.
315   //
316   if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
317                                                 : C.generateNode()) {
318     llvm::SmallString<128> sbuf;
319     llvm::raw_svector_ostream os(sbuf);
320 
321     os << (SourceSize == 8 ? "An " : "A ")
322        << SourceSize << " bit integer is used to initialize a CFNumber "
323                         "object that represents "
324        << (TargetSize == 8 ? "an " : "a ")
325        << TargetSize << " bit integer. ";
326 
327     if (SourceSize < TargetSize)
328       os << (TargetSize - SourceSize)
329       << " bits of the CFNumber value will be garbage." ;
330     else
331       os << (SourceSize - TargetSize)
332       << " bits of the input integer will be lost.";
333 
334     if (!BT)
335       BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
336 
337     RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
338     report->addRange(CE->getArg(2)->getSourceRange());
339     C.EmitReport(report);
340   }
341 }
342 
343 //===----------------------------------------------------------------------===//
344 // CFRetain/CFRelease checking for null arguments.
345 //===----------------------------------------------------------------------===//
346 
347 namespace {
348 class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
349   mutable llvm::OwningPtr<APIMisuse> BT;
350   mutable IdentifierInfo *Retain, *Release;
351 public:
352   CFRetainReleaseChecker(): Retain(0), Release(0) {}
353   void checkPreStmt(const CallExpr* CE, CheckerContext& C) const;
354 };
355 } // end anonymous namespace
356 
357 
358 void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE,
359                                           CheckerContext& C) const {
360   // If the CallExpr doesn't have exactly 1 argument just give up checking.
361   if (CE->getNumArgs() != 1)
362     return;
363 
364   // Get the function declaration of the callee.
365   const GRState* state = C.getState();
366   SVal X = state->getSVal(CE->getCallee());
367   const FunctionDecl* FD = X.getAsFunctionDecl();
368 
369   if (!FD)
370     return;
371 
372   if (!BT) {
373     ASTContext &Ctx = C.getASTContext();
374     Retain = &Ctx.Idents.get("CFRetain");
375     Release = &Ctx.Idents.get("CFRelease");
376     BT.reset(new APIMisuse("null passed to CFRetain/CFRelease"));
377   }
378 
379   // Check if we called CFRetain/CFRelease.
380   const IdentifierInfo *FuncII = FD->getIdentifier();
381   if (!(FuncII == Retain || FuncII == Release))
382     return;
383 
384   // FIXME: The rest of this just checks that the argument is non-null.
385   // It should probably be refactored and combined with AttrNonNullChecker.
386 
387   // Get the argument's value.
388   const Expr *Arg = CE->getArg(0);
389   SVal ArgVal = state->getSVal(Arg);
390   DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
391   if (!DefArgVal)
392     return;
393 
394   // Get a NULL value.
395   SValBuilder &svalBuilder = C.getSValBuilder();
396   DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
397 
398   // Make an expression asserting that they're equal.
399   DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
400 
401   // Are they equal?
402   const GRState *stateTrue, *stateFalse;
403   llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
404 
405   if (stateTrue && !stateFalse) {
406     ExplodedNode *N = C.generateSink(stateTrue);
407     if (!N)
408       return;
409 
410     const char *description = (FuncII == Retain)
411                             ? "Null pointer argument in call to CFRetain"
412                             : "Null pointer argument in call to CFRelease";
413 
414     EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N);
415     report->addRange(Arg->getSourceRange());
416     report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg);
417     C.EmitReport(report);
418     return;
419   }
420 
421   // From here on, we know the argument is non-null.
422   C.addTransition(stateFalse);
423 }
424 
425 //===----------------------------------------------------------------------===//
426 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
427 //===----------------------------------------------------------------------===//
428 
429 namespace {
430 class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
431   mutable Selector releaseS;
432   mutable Selector retainS;
433   mutable Selector autoreleaseS;
434   mutable Selector drainS;
435   mutable llvm::OwningPtr<BugType> BT;
436 
437 public:
438   void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
439 };
440 }
441 
442 void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
443                                               CheckerContext &C) const {
444 
445   if (!BT) {
446     BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
447                            "instance"));
448 
449     ASTContext &Ctx = C.getASTContext();
450     releaseS = GetNullarySelector("release", Ctx);
451     retainS = GetNullarySelector("retain", Ctx);
452     autoreleaseS = GetNullarySelector("autorelease", Ctx);
453     drainS = GetNullarySelector("drain", Ctx);
454   }
455 
456   if (msg.isInstanceMessage())
457     return;
458   const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
459   assert(Class);
460 
461   Selector S = msg.getSelector();
462   if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
463     return;
464 
465   if (ExplodedNode *N = C.generateNode()) {
466     llvm::SmallString<200> buf;
467     llvm::raw_svector_ostream os(buf);
468 
469     os << "The '" << S.getAsString() << "' message should be sent to instances "
470           "of class '" << Class->getName()
471        << "' and not the class directly";
472 
473     RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
474     report->addRange(msg.getSourceRange());
475     C.EmitReport(report);
476   }
477 }
478 
479 //===----------------------------------------------------------------------===//
480 // Check for passing non-Objective-C types to variadic methods that expect
481 // only Objective-C types.
482 //===----------------------------------------------------------------------===//
483 
484 namespace {
485 class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
486   mutable Selector arrayWithObjectsS;
487   mutable Selector dictionaryWithObjectsAndKeysS;
488   mutable Selector setWithObjectsS;
489   mutable Selector initWithObjectsS;
490   mutable Selector initWithObjectsAndKeysS;
491   mutable llvm::OwningPtr<BugType> BT;
492 
493   bool isVariadicMessage(const ObjCMessage &msg) const;
494 
495 public:
496   void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
497 };
498 }
499 
500 /// isVariadicMessage - Returns whether the given message is a variadic message,
501 /// where all arguments must be Objective-C types.
502 bool
503 VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const {
504   const ObjCMethodDecl *MD = msg.getMethodDecl();
505   if (!MD)
506     return false;
507 
508   if (!MD->isVariadic())
509     return false;
510 
511   Selector S = msg.getSelector();
512 
513   if (msg.isInstanceMessage()) {
514     // FIXME: Ideally we'd look at the receiver interface here, but that's not
515     // useful for init, because alloc returns 'id'. In theory, this could lead
516     // to false positives, for example if there existed a class that had an
517     // initWithObjects: implementation that does accept non-Objective-C pointer
518     // types, but the chance of that happening is pretty small compared to the
519     // gains that this analysis gives.
520     const ObjCInterfaceDecl *Class = MD->getClassInterface();
521 
522     // -[NSArray initWithObjects:]
523     if (isReceiverClassOrSuperclass(Class, "NSArray") &&
524         S == initWithObjectsS)
525       return true;
526 
527     // -[NSDictionary initWithObjectsAndKeys:]
528     if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
529         S == initWithObjectsAndKeysS)
530       return true;
531 
532     // -[NSSet initWithObjects:]
533     if (isReceiverClassOrSuperclass(Class, "NSSet") &&
534         S == initWithObjectsS)
535       return true;
536   } else {
537     const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
538 
539     // -[NSArray arrayWithObjects:]
540     if (isReceiverClassOrSuperclass(Class, "NSArray") &&
541         S == arrayWithObjectsS)
542       return true;
543 
544     // -[NSDictionary dictionaryWithObjectsAndKeys:]
545     if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
546         S == dictionaryWithObjectsAndKeysS)
547       return true;
548 
549     // -[NSSet setWithObjects:]
550     if (isReceiverClassOrSuperclass(Class, "NSSet") &&
551         S == setWithObjectsS)
552       return true;
553   }
554 
555   return false;
556 }
557 
558 void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
559                                                     CheckerContext &C) const {
560   if (!BT) {
561     BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
562                            "Objective-C pointer types"));
563 
564     ASTContext &Ctx = C.getASTContext();
565     arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
566     dictionaryWithObjectsAndKeysS =
567       GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
568     setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
569 
570     initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
571     initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
572   }
573 
574   if (!isVariadicMessage(msg))
575       return;
576 
577   // We are not interested in the selector arguments since they have
578   // well-defined types, so the compiler will issue a warning for them.
579   unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
580 
581   // We're not interested in the last argument since it has to be nil or the
582   // compiler would have issued a warning for it elsewhere.
583   unsigned variadicArgsEnd = msg.getNumArgs() - 1;
584 
585   if (variadicArgsEnd <= variadicArgsBegin)
586     return;
587 
588   // Verify that all arguments have Objective-C types.
589   for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
590     QualType ArgTy = msg.getArgType(I);
591     if (ArgTy->isObjCObjectPointerType())
592       continue;
593 
594     ExplodedNode *N = C.generateNode();
595     if (!N)
596       continue;
597 
598     llvm::SmallString<128> sbuf;
599     llvm::raw_svector_ostream os(sbuf);
600 
601     if (const char *TypeName = GetReceiverNameType(msg))
602       os << "Argument to '" << TypeName << "' method '";
603     else
604       os << "Argument to method '";
605 
606     os << msg.getSelector().getAsString()
607       << "' should be an Objective-C pointer type, not '"
608       << ArgTy.getAsString() << "'";
609 
610     RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
611     R->addRange(msg.getArgSourceRange(I));
612     C.EmitReport(R);
613   }
614 }
615 
616 //===----------------------------------------------------------------------===//
617 // Check registration.
618 //===----------------------------------------------------------------------===//
619 
620 void ento::registerNilArgChecker(CheckerManager &mgr) {
621   mgr.registerChecker<NilArgChecker>();
622 }
623 
624 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
625   mgr.registerChecker<CFNumberCreateChecker>();
626 }
627 
628 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
629   mgr.registerChecker<CFRetainReleaseChecker>();
630 }
631 
632 void ento::registerClassReleaseChecker(CheckerManager &mgr) {
633   mgr.registerChecker<ClassReleaseChecker>();
634 }
635 
636 void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
637   mgr.registerChecker<VariadicMethodTypeChecker>();
638 }
639