xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp (revision dff865d10b7ebb489eabb4495b6535c3eee45176)
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/CheckerV2.h"
20 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
24 #include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
25 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.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 CheckerV2<check::PreObjCMessage> {
73     mutable llvm::OwningPtr<APIMisuse> BT;
74 
75     void WarnNilArg(CheckerContext &C,
76                     const ObjCMessage &msg, unsigned Arg) const;
77 
78   public:
79     void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
80   };
81 }
82 
83 void NilArgChecker::WarnNilArg(CheckerContext &C,
84                                const ObjCMessage &msg,
85                                unsigned int Arg) const
86 {
87   if (!BT)
88     BT.reset(new APIMisuse("nil argument"));
89 
90   if (ExplodedNode *N = C.generateSink()) {
91     llvm::SmallString<128> sbuf;
92     llvm::raw_svector_ostream os(sbuf);
93     os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
94        << msg.getSelector().getAsString() << "' cannot be nil";
95 
96     RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
97     R->addRange(msg.getArgSourceRange(Arg));
98     C.EmitReport(R);
99   }
100 }
101 
102 void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
103                                         CheckerContext &C) const {
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 CheckerV2< check::PreStmt<CallExpr> > {
144   mutable llvm::OwningPtr<APIMisuse> BT;
145   mutable IdentifierInfo* II;
146 public:
147   CFNumberCreateChecker() : II(0) {}
148 
149   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
150 
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::checkPreStmt(const CallExpr *CE,
251                                          CheckerContext &C) const {
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.reset(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 CheckerV2< check::PreStmt<CallExpr> > {
351   mutable llvm::OwningPtr<APIMisuse> BT;
352   mutable IdentifierInfo *Retain, *Release;
353 public:
354   CFRetainReleaseChecker(): Retain(0), Release(0) {}
355   void checkPreStmt(const CallExpr* CE, CheckerContext& C) const;
356 };
357 } // end anonymous namespace
358 
359 
360 void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE,
361                                           CheckerContext& C) const {
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.reset(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 CheckerV2<check::PreObjCMessage> {
433   mutable Selector releaseS;
434   mutable Selector retainS;
435   mutable Selector autoreleaseS;
436   mutable Selector drainS;
437   mutable llvm::OwningPtr<BugType> BT;
438 
439 public:
440   void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
441 };
442 }
443 
444 void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
445                                               CheckerContext &C) const {
446 
447   if (!BT) {
448     BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
449                            "instance"));
450 
451     ASTContext &Ctx = C.getASTContext();
452     releaseS = GetNullarySelector("release", Ctx);
453     retainS = GetNullarySelector("retain", Ctx);
454     autoreleaseS = GetNullarySelector("autorelease", Ctx);
455     drainS = GetNullarySelector("drain", Ctx);
456   }
457 
458   if (msg.isInstanceMessage())
459     return;
460   const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
461   assert(Class);
462 
463   Selector S = msg.getSelector();
464   if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
465     return;
466 
467   if (ExplodedNode *N = C.generateNode()) {
468     llvm::SmallString<200> buf;
469     llvm::raw_svector_ostream os(buf);
470 
471     os << "The '" << S.getAsString() << "' message should be sent to instances "
472           "of class '" << Class->getName()
473        << "' and not the class directly";
474 
475     RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
476     report->addRange(msg.getSourceRange());
477     C.EmitReport(report);
478   }
479 }
480 
481 //===----------------------------------------------------------------------===//
482 // Check registration.
483 //===----------------------------------------------------------------------===//
484 
485 void ento::registerNilArgChecker(CheckerManager &mgr) {
486   mgr.registerChecker<NilArgChecker>();
487 }
488 
489 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
490   mgr.registerChecker<CFNumberCreateChecker>();
491 }
492 
493 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
494   mgr.registerChecker<CFRetainReleaseChecker>();
495 }
496 
497 void ento::registerClassReleaseChecker(CheckerManager &mgr) {
498   mgr.registerChecker<ClassReleaseChecker>();
499 }
500