xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp (revision 8e169a5f189fa7829565fd1941c9e3da0841b669)
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   if (ObjCInterfaceDecl *ID = ME->getReceiverInterface())
47     return ID->getTypeForDecl()->getAs<ObjCInterfaceType>();
48   return NULL;
49 }
50 
51 static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
52   if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME))
53     return ReceiverType->getDecl()->getIdentifier()->getNameStart();
54   return NULL;
55 }
56 
57 static bool isNSString(llvm::StringRef ClassName) {
58   return ClassName == "NSString" || ClassName == "NSMutableString";
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 CheckerVisitor<NilArgChecker> {
71     APIMisuse *BT;
72     void WarnNilArg(CheckerContext &C, const ObjCMessageExpr* ME, unsigned Arg);
73   public:
74     NilArgChecker() : BT(0) {}
75     static void *getTag() { static int x = 0; return &x; }
76     void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);
77   };
78 }
79 
80 void NilArgChecker::WarnNilArg(CheckerContext &C,
81                                const clang::ObjCMessageExpr *ME,
82                                unsigned int Arg)
83 {
84   if (!BT)
85     BT = new APIMisuse("nil argument");
86 
87   if (ExplodedNode *N = C.generateSink()) {
88     llvm::SmallString<128> sbuf;
89     llvm::raw_svector_ostream os(sbuf);
90     os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
91        << ME->getSelector().getAsString() << "' cannot be nil";
92 
93     RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
94     R->addRange(ME->getArg(Arg)->getSourceRange());
95     C.EmitReport(R);
96   }
97 }
98 
99 void NilArgChecker::PreVisitObjCMessageExpr(CheckerContext &C,
100                                             const ObjCMessageExpr *ME)
101 {
102   const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
103   if (!ReceiverType)
104     return;
105 
106   if (isNSString(ReceiverType->getDecl()->getIdentifier()->getName())) {
107     Selector S = ME->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(C.getState()->getSVal(ME->getArg(0))))
131         WarnNilArg(C, ME, 0);
132     }
133   }
134 }
135 
136 //===----------------------------------------------------------------------===//
137 // Error reporting.
138 //===----------------------------------------------------------------------===//
139 
140 namespace {
141 class CFNumberCreateChecker : public CheckerVisitor<CFNumberCreateChecker> {
142   APIMisuse* BT;
143   IdentifierInfo* II;
144 public:
145   CFNumberCreateChecker() : BT(0), II(0) {}
146   ~CFNumberCreateChecker() {}
147   static void *getTag() { static int x = 0; return &x; }
148   void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE);
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::PreVisitCallExpr(CheckerContext &C,
249                                              const CallExpr *CE)
250 {
251   const Expr* Callee = CE->getCallee();
252   const GRState *state = C.getState();
253   SVal CallV = state->getSVal(Callee);
254   const FunctionDecl* FD = CallV.getAsFunctionDecl();
255 
256   if (!FD)
257     return;
258 
259   ASTContext &Ctx = C.getASTContext();
260   if (!II)
261     II = &Ctx.Idents.get("CFNumberCreate");
262 
263   if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
264     return;
265 
266   // Get the value of the "theType" argument.
267   SVal TheTypeVal = state->getSVal(CE->getArg(1));
268 
269   // FIXME: We really should allow ranges of valid theType values, and
270   //   bifurcate the state appropriately.
271   nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
272   if (!V)
273     return;
274 
275   uint64_t NumberKind = V->getValue().getLimitedValue();
276   Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
277 
278   // FIXME: In some cases we can emit an error.
279   if (!TargetSize.isKnown())
280     return;
281 
282   // Look at the value of the integer being passed by reference.  Essentially
283   // we want to catch cases where the value passed in is not equal to the
284   // size of the type being created.
285   SVal TheValueExpr = state->getSVal(CE->getArg(2));
286 
287   // FIXME: Eventually we should handle arbitrary locations.  We can do this
288   //  by having an enhanced memory model that does low-level typing.
289   loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
290   if (!LV)
291     return;
292 
293   const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
294   if (!R)
295     return;
296 
297   QualType T = Ctx.getCanonicalType(R->getValueType());
298 
299   // FIXME: If the pointee isn't an integer type, should we flag a warning?
300   //  People can do weird stuff with pointers.
301 
302   if (!T->isIntegerType())
303     return;
304 
305   uint64_t SourceSize = Ctx.getTypeSize(T);
306 
307   // CHECK: is SourceSize == TargetSize
308   if (SourceSize == TargetSize)
309     return;
310 
311   // Generate an error.  Only generate a sink if 'SourceSize < TargetSize';
312   // otherwise generate a regular node.
313   //
314   // FIXME: We can actually create an abstract "CFNumber" object that has
315   //  the bits initialized to the provided values.
316   //
317   if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
318                                                 : C.generateNode()) {
319     llvm::SmallString<128> sbuf;
320     llvm::raw_svector_ostream os(sbuf);
321 
322     os << (SourceSize == 8 ? "An " : "A ")
323        << SourceSize << " bit integer is used to initialize a CFNumber "
324                         "object that represents "
325        << (TargetSize == 8 ? "an " : "a ")
326        << TargetSize << " bit integer. ";
327 
328     if (SourceSize < TargetSize)
329       os << (TargetSize - SourceSize)
330       << " bits of the CFNumber value will be garbage." ;
331     else
332       os << (SourceSize - TargetSize)
333       << " bits of the input integer will be lost.";
334 
335     if (!BT)
336       BT = new APIMisuse("Bad use of CFNumberCreate");
337 
338     RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
339     report->addRange(CE->getArg(2)->getSourceRange());
340     C.EmitReport(report);
341   }
342 }
343 
344 //===----------------------------------------------------------------------===//
345 // CFRetain/CFRelease checking for null arguments.
346 //===----------------------------------------------------------------------===//
347 
348 namespace {
349 class CFRetainReleaseChecker : public CheckerVisitor<CFRetainReleaseChecker> {
350   APIMisuse *BT;
351   IdentifierInfo *Retain, *Release;
352 public:
353   CFRetainReleaseChecker(): BT(0), Retain(0), Release(0) {}
354   static void *getTag() { static int x = 0; return &x; }
355   void PreVisitCallExpr(CheckerContext& C, const CallExpr* CE);
356 };
357 } // end anonymous namespace
358 
359 
360 void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C,
361                                               const CallExpr* CE) {
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 = 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 
431 namespace {
432 class ClassReleaseChecker : public CheckerVisitor<ClassReleaseChecker> {
433   Selector releaseS;
434   Selector retainS;
435   Selector autoreleaseS;
436   Selector drainS;
437   BugType *BT;
438 public:
439   ClassReleaseChecker()
440     : BT(0) {}
441 
442   static void *getTag() { static int x = 0; return &x; }
443 
444   void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);
445 };
446 }
447 
448 void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C,
449                                                   const ObjCMessageExpr *ME) {
450 
451   if (!BT) {
452     BT = new APIMisuse("message incorrectly sent to class instead of class "
453                        "instance");
454 
455     ASTContext &Ctx = C.getASTContext();
456     releaseS = GetNullarySelector("release", Ctx);
457     retainS = GetNullarySelector("retain", Ctx);
458     autoreleaseS = GetNullarySelector("autorelease", Ctx);
459     drainS = GetNullarySelector("drain", Ctx);
460   }
461 
462   ObjCInterfaceDecl *Class = 0;
463 
464   switch (ME->getReceiverKind()) {
465   case ObjCMessageExpr::Class:
466     Class = ME->getClassReceiver()->getAs<ObjCObjectType>()->getInterface();
467     break;
468   case ObjCMessageExpr::SuperClass:
469     Class = ME->getSuperType()->getAs<ObjCObjectType>()->getInterface();
470     break;
471   case ObjCMessageExpr::Instance:
472   case ObjCMessageExpr::SuperInstance:
473     return;
474   }
475 
476   Selector S = ME->getSelector();
477   if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
478     return;
479 
480   if (ExplodedNode *N = C.generateNode()) {
481     llvm::SmallString<200> buf;
482     llvm::raw_svector_ostream os(buf);
483 
484     os << "The '" << S.getAsString() << "' message should be sent to instances "
485           "of class '" << Class->getName()
486        << "' and not the class directly";
487 
488     RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
489     report->addRange(ME->getSourceRange());
490     C.EmitReport(report);
491   }
492 }
493 
494 //===----------------------------------------------------------------------===//
495 // Check registration.
496 //===----------------------------------------------------------------------===//
497 
498 void ento::RegisterAppleChecks(ExprEngine& Eng, const Decl &D) {
499   Eng.registerCheck(new NilArgChecker());
500   Eng.registerCheck(new CFNumberCreateChecker());
501   RegisterNSErrorChecks(Eng.getBugReporter(), Eng, D);
502   RegisterNSAutoreleasePoolChecks(Eng);
503   Eng.registerCheck(new CFRetainReleaseChecker());
504   Eng.registerCheck(new ClassReleaseChecker());
505 }
506