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