xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp (revision 85a203ebdda6977a7e75cf555c2991fe2eb21809)
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/PathSensitive/ExplodedGraph.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.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/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h"
26 #include "clang/StaticAnalyzer/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 using namespace ento;
34 
35 namespace {
36 class APIMisuse : public BugType {
37 public:
38   APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
39 };
40 } // end anonymous namespace
41 
42 //===----------------------------------------------------------------------===//
43 // Utility functions.
44 //===----------------------------------------------------------------------===//
45 
46 static const ObjCInterfaceType* GetReceiverType(const ObjCMessage &msg) {
47   if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
48     return ID->getTypeForDecl()->getAs<ObjCInterfaceType>();
49   return NULL;
50 }
51 
52 static const char* GetReceiverNameType(const ObjCMessage &msg) {
53   if (const ObjCInterfaceType *ReceiverType = GetReceiverType(msg))
54     return ReceiverType->getDecl()->getIdentifier()->getNameStart();
55   return NULL;
56 }
57 
58 static bool isNSString(llvm::StringRef ClassName) {
59   return ClassName == "NSString" || ClassName == "NSMutableString";
60 }
61 
62 static inline bool isNil(SVal X) {
63   return isa<loc::ConcreteInt>(X);
64 }
65 
66 //===----------------------------------------------------------------------===//
67 // NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
68 //===----------------------------------------------------------------------===//
69 
70 namespace {
71   class NilArgChecker : public CheckerVisitor<NilArgChecker> {
72     APIMisuse *BT;
73     void WarnNilArg(CheckerContext &C, const ObjCMessage &msg, unsigned Arg);
74   public:
75     NilArgChecker() : BT(0) {}
76     static void *getTag() { static int x = 0; return &x; }
77     void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg);
78   };
79 }
80 
81 void NilArgChecker::WarnNilArg(CheckerContext &C,
82                                const ObjCMessage &msg,
83                                unsigned int Arg)
84 {
85   if (!BT)
86     BT = 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::preVisitObjCMessage(CheckerContext &C,
101                                         ObjCMessage msg)
102 {
103   const ObjCInterfaceType *ReceiverType = GetReceiverType(msg);
104   if (!ReceiverType)
105     return;
106 
107   if (isNSString(ReceiverType->getDecl()->getIdentifier()->getName())) {
108     Selector S = msg.getSelector();
109 
110     if (S.isUnarySelector())
111       return;
112 
113     // FIXME: This is going to be really slow doing these checks with
114     //  lexical comparisons.
115 
116     std::string NameStr = S.getAsString();
117     llvm::StringRef Name(NameStr);
118     assert(!Name.empty());
119 
120     // FIXME: Checking for initWithFormat: will not work in most cases
121     //  yet because [NSString alloc] returns id, not NSString*.  We will
122     //  need support for tracking expected-type information in the analyzer
123     //  to find these errors.
124     if (Name == "caseInsensitiveCompare:" ||
125         Name == "compare:" ||
126         Name == "compare:options:" ||
127         Name == "compare:options:range:" ||
128         Name == "compare:options:range:locale:" ||
129         Name == "componentsSeparatedByCharactersInSet:" ||
130         Name == "initWithFormat:") {
131       if (isNil(msg.getArgSVal(0, C.getState())))
132         WarnNilArg(C, msg, 0);
133     }
134   }
135 }
136 
137 //===----------------------------------------------------------------------===//
138 // Error reporting.
139 //===----------------------------------------------------------------------===//
140 
141 namespace {
142 class CFNumberCreateChecker : public CheckerVisitor<CFNumberCreateChecker> {
143   APIMisuse* BT;
144   IdentifierInfo* II;
145 public:
146   CFNumberCreateChecker() : BT(0), II(0) {}
147   ~CFNumberCreateChecker() {}
148   static void *getTag() { static int x = 0; return &x; }
149   void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE);
150 private:
151   void EmitError(const TypedRegion* R, const Expr* Ex,
152                 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
153 };
154 } // end anonymous namespace
155 
156 enum CFNumberType {
157   kCFNumberSInt8Type = 1,
158   kCFNumberSInt16Type = 2,
159   kCFNumberSInt32Type = 3,
160   kCFNumberSInt64Type = 4,
161   kCFNumberFloat32Type = 5,
162   kCFNumberFloat64Type = 6,
163   kCFNumberCharType = 7,
164   kCFNumberShortType = 8,
165   kCFNumberIntType = 9,
166   kCFNumberLongType = 10,
167   kCFNumberLongLongType = 11,
168   kCFNumberFloatType = 12,
169   kCFNumberDoubleType = 13,
170   kCFNumberCFIndexType = 14,
171   kCFNumberNSIntegerType = 15,
172   kCFNumberCGFloatType = 16
173 };
174 
175 namespace {
176   template<typename T>
177   class Optional {
178     bool IsKnown;
179     T Val;
180   public:
181     Optional() : IsKnown(false), Val(0) {}
182     Optional(const T& val) : IsKnown(true), Val(val) {}
183 
184     bool isKnown() const { return IsKnown; }
185 
186     const T& getValue() const {
187       assert (isKnown());
188       return Val;
189     }
190 
191     operator const T&() const {
192       return getValue();
193     }
194   };
195 }
196 
197 static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
198   static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
199 
200   if (i < kCFNumberCharType)
201     return FixedSize[i-1];
202 
203   QualType T;
204 
205   switch (i) {
206     case kCFNumberCharType:     T = Ctx.CharTy;     break;
207     case kCFNumberShortType:    T = Ctx.ShortTy;    break;
208     case kCFNumberIntType:      T = Ctx.IntTy;      break;
209     case kCFNumberLongType:     T = Ctx.LongTy;     break;
210     case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
211     case kCFNumberFloatType:    T = Ctx.FloatTy;    break;
212     case kCFNumberDoubleType:   T = Ctx.DoubleTy;   break;
213     case kCFNumberCFIndexType:
214     case kCFNumberNSIntegerType:
215     case kCFNumberCGFloatType:
216       // FIXME: We need a way to map from names to Type*.
217     default:
218       return Optional<uint64_t>();
219   }
220 
221   return Ctx.getTypeSize(T);
222 }
223 
224 #if 0
225 static const char* GetCFNumberTypeStr(uint64_t i) {
226   static const char* Names[] = {
227     "kCFNumberSInt8Type",
228     "kCFNumberSInt16Type",
229     "kCFNumberSInt32Type",
230     "kCFNumberSInt64Type",
231     "kCFNumberFloat32Type",
232     "kCFNumberFloat64Type",
233     "kCFNumberCharType",
234     "kCFNumberShortType",
235     "kCFNumberIntType",
236     "kCFNumberLongType",
237     "kCFNumberLongLongType",
238     "kCFNumberFloatType",
239     "kCFNumberDoubleType",
240     "kCFNumberCFIndexType",
241     "kCFNumberNSIntegerType",
242     "kCFNumberCGFloatType"
243   };
244 
245   return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
246 }
247 #endif
248 
249 void CFNumberCreateChecker::PreVisitCallExpr(CheckerContext &C,
250                                              const CallExpr *CE)
251 {
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 = 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 
349 namespace {
350 class CFRetainReleaseChecker : public CheckerVisitor<CFRetainReleaseChecker> {
351   APIMisuse *BT;
352   IdentifierInfo *Retain, *Release;
353 public:
354   CFRetainReleaseChecker(): BT(0), Retain(0), Release(0) {}
355   static void *getTag() { static int x = 0; return &x; }
356   void PreVisitCallExpr(CheckerContext& C, const CallExpr* CE);
357 };
358 } // end anonymous namespace
359 
360 
361 void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C,
362                                               const CallExpr* CE) {
363   // If the CallExpr doesn't have exactly 1 argument just give up checking.
364   if (CE->getNumArgs() != 1)
365     return;
366 
367   // Get the function declaration of the callee.
368   const GRState* state = C.getState();
369   SVal X = state->getSVal(CE->getCallee());
370   const FunctionDecl* FD = X.getAsFunctionDecl();
371 
372   if (!FD)
373     return;
374 
375   if (!BT) {
376     ASTContext &Ctx = C.getASTContext();
377     Retain = &Ctx.Idents.get("CFRetain");
378     Release = &Ctx.Idents.get("CFRelease");
379     BT = new APIMisuse("null passed to CFRetain/CFRelease");
380   }
381 
382   // Check if we called CFRetain/CFRelease.
383   const IdentifierInfo *FuncII = FD->getIdentifier();
384   if (!(FuncII == Retain || FuncII == Release))
385     return;
386 
387   // FIXME: The rest of this just checks that the argument is non-null.
388   // It should probably be refactored and combined with AttrNonNullChecker.
389 
390   // Get the argument's value.
391   const Expr *Arg = CE->getArg(0);
392   SVal ArgVal = state->getSVal(Arg);
393   DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
394   if (!DefArgVal)
395     return;
396 
397   // Get a NULL value.
398   SValBuilder &svalBuilder = C.getSValBuilder();
399   DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
400 
401   // Make an expression asserting that they're equal.
402   DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
403 
404   // Are they equal?
405   const GRState *stateTrue, *stateFalse;
406   llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
407 
408   if (stateTrue && !stateFalse) {
409     ExplodedNode *N = C.generateSink(stateTrue);
410     if (!N)
411       return;
412 
413     const char *description = (FuncII == Retain)
414                             ? "Null pointer argument in call to CFRetain"
415                             : "Null pointer argument in call to CFRelease";
416 
417     EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N);
418     report->addRange(Arg->getSourceRange());
419     report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg);
420     C.EmitReport(report);
421     return;
422   }
423 
424   // From here on, we know the argument is non-null.
425   C.addTransition(stateFalse);
426 }
427 
428 //===----------------------------------------------------------------------===//
429 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
430 //===----------------------------------------------------------------------===//
431 
432 namespace {
433 class ClassReleaseChecker : public CheckerVisitor<ClassReleaseChecker> {
434   Selector releaseS;
435   Selector retainS;
436   Selector autoreleaseS;
437   Selector drainS;
438   BugType *BT;
439 public:
440   ClassReleaseChecker()
441     : BT(0) {}
442 
443   static void *getTag() { static int x = 0; return &x; }
444 
445   void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg);
446 };
447 }
448 
449 void ClassReleaseChecker::preVisitObjCMessage(CheckerContext &C,
450                                               ObjCMessage msg) {
451 
452   if (!BT) {
453     BT = new APIMisuse("message incorrectly sent to class instead of class "
454                        "instance");
455 
456     ASTContext &Ctx = C.getASTContext();
457     releaseS = GetNullarySelector("release", Ctx);
458     retainS = GetNullarySelector("retain", Ctx);
459     autoreleaseS = GetNullarySelector("autorelease", Ctx);
460     drainS = GetNullarySelector("drain", Ctx);
461   }
462 
463   if (msg.isInstanceMessage())
464     return;
465   const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
466   assert(Class);
467 
468   Selector S = msg.getSelector();
469   if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
470     return;
471 
472   if (ExplodedNode *N = C.generateNode()) {
473     llvm::SmallString<200> buf;
474     llvm::raw_svector_ostream os(buf);
475 
476     os << "The '" << S.getAsString() << "' message should be sent to instances "
477           "of class '" << Class->getName()
478        << "' and not the class directly";
479 
480     RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
481     report->addRange(msg.getSourceRange());
482     C.EmitReport(report);
483   }
484 }
485 
486 //===----------------------------------------------------------------------===//
487 // Check registration.
488 //===----------------------------------------------------------------------===//
489 
490 void ento::registerNilArgChecker(ExprEngine& Eng) {
491   Eng.registerCheck(new NilArgChecker());
492 }
493 
494 void ento::registerCFNumberCreateChecker(ExprEngine& Eng) {
495   Eng.registerCheck(new CFNumberCreateChecker());
496 }
497 
498 void ento::registerCFRetainReleaseChecker(ExprEngine& Eng) {
499   Eng.registerCheck(new CFRetainReleaseChecker());
500 }
501 
502 void ento::registerClassReleaseChecker(ExprEngine& Eng) {
503   Eng.registerCheck(new ClassReleaseChecker());
504 }
505