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