xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp (revision 3c50aea73fee98d6236f7022685685fd9c4b32d4)
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 "ClangSACheckers.h"
17 #include "clang/StaticAnalyzer/Core/Checker.h"
18 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.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/AST/DeclObjC.h"
26 #include "clang/AST/Expr.h"
27 #include "clang/AST/ExprObjC.h"
28 #include "clang/AST/ASTContext.h"
29 
30 using namespace clang;
31 using namespace ento;
32 
33 namespace {
34 class APIMisuse : public BugType {
35 public:
36   APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
37 };
38 } // end anonymous namespace
39 
40 //===----------------------------------------------------------------------===//
41 // Utility functions.
42 //===----------------------------------------------------------------------===//
43 
44 static const char* GetReceiverNameType(const ObjCMessage &msg) {
45   if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
46     return ID->getIdentifier()->getNameStart();
47   return 0;
48 }
49 
50 static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID,
51                                         llvm::StringRef ClassName) {
52   if (ID->getIdentifier()->getName() == ClassName)
53     return true;
54 
55   if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
56     return isReceiverClassOrSuperclass(Super, ClassName);
57 
58   return false;
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 Checker<check::PreObjCMessage> {
71     mutable llvm::OwningPtr<APIMisuse> BT;
72 
73     void WarnNilArg(CheckerContext &C,
74                     const ObjCMessage &msg, unsigned Arg) const;
75 
76   public:
77     void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
78   };
79 }
80 
81 void NilArgChecker::WarnNilArg(CheckerContext &C,
82                                const ObjCMessage &msg,
83                                unsigned int Arg) const
84 {
85   if (!BT)
86     BT.reset(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::checkPreObjCMessage(ObjCMessage msg,
101                                         CheckerContext &C) const {
102   const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
103   if (!ID)
104     return;
105 
106   if (isReceiverClassOrSuperclass(ID, "NSString")) {
107     Selector S = msg.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(msg.getArgSVal(0, C.getState())))
131         WarnNilArg(C, msg, 0);
132     }
133   }
134 }
135 
136 //===----------------------------------------------------------------------===//
137 // Error reporting.
138 //===----------------------------------------------------------------------===//
139 
140 namespace {
141 class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
142   mutable llvm::OwningPtr<APIMisuse> BT;
143   mutable IdentifierInfo* II;
144 public:
145   CFNumberCreateChecker() : II(0) {}
146 
147   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
148 
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::checkPreStmt(const CallExpr *CE,
249                                          CheckerContext &C) const {
250   const Expr* Callee = CE->getCallee();
251   const GRState *state = C.getState();
252   SVal CallV = state->getSVal(Callee);
253   const FunctionDecl* FD = CallV.getAsFunctionDecl();
254 
255   if (!FD)
256     return;
257 
258   ASTContext &Ctx = C.getASTContext();
259   if (!II)
260     II = &Ctx.Idents.get("CFNumberCreate");
261 
262   if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
263     return;
264 
265   // Get the value of the "theType" argument.
266   SVal TheTypeVal = state->getSVal(CE->getArg(1));
267 
268   // FIXME: We really should allow ranges of valid theType values, and
269   //   bifurcate the state appropriately.
270   nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
271   if (!V)
272     return;
273 
274   uint64_t NumberKind = V->getValue().getLimitedValue();
275   Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
276 
277   // FIXME: In some cases we can emit an error.
278   if (!TargetSize.isKnown())
279     return;
280 
281   // Look at the value of the integer being passed by reference.  Essentially
282   // we want to catch cases where the value passed in is not equal to the
283   // size of the type being created.
284   SVal TheValueExpr = state->getSVal(CE->getArg(2));
285 
286   // FIXME: Eventually we should handle arbitrary locations.  We can do this
287   //  by having an enhanced memory model that does low-level typing.
288   loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
289   if (!LV)
290     return;
291 
292   const TypedRegion* R = dyn_cast<TypedRegion>(LV->stripCasts());
293   if (!R)
294     return;
295 
296   QualType T = Ctx.getCanonicalType(R->getValueType());
297 
298   // FIXME: If the pointee isn't an integer type, should we flag a warning?
299   //  People can do weird stuff with pointers.
300 
301   if (!T->isIntegerType())
302     return;
303 
304   uint64_t SourceSize = Ctx.getTypeSize(T);
305 
306   // CHECK: is SourceSize == TargetSize
307   if (SourceSize == TargetSize)
308     return;
309 
310   // Generate an error.  Only generate a sink if 'SourceSize < TargetSize';
311   // otherwise generate a regular node.
312   //
313   // FIXME: We can actually create an abstract "CFNumber" object that has
314   //  the bits initialized to the provided values.
315   //
316   if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
317                                                 : C.generateNode()) {
318     llvm::SmallString<128> sbuf;
319     llvm::raw_svector_ostream os(sbuf);
320 
321     os << (SourceSize == 8 ? "An " : "A ")
322        << SourceSize << " bit integer is used to initialize a CFNumber "
323                         "object that represents "
324        << (TargetSize == 8 ? "an " : "a ")
325        << TargetSize << " bit integer. ";
326 
327     if (SourceSize < TargetSize)
328       os << (TargetSize - SourceSize)
329       << " bits of the CFNumber value will be garbage." ;
330     else
331       os << (SourceSize - TargetSize)
332       << " bits of the input integer will be lost.";
333 
334     if (!BT)
335       BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
336 
337     RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
338     report->addRange(CE->getArg(2)->getSourceRange());
339     C.EmitReport(report);
340   }
341 }
342 
343 //===----------------------------------------------------------------------===//
344 // CFRetain/CFRelease checking for null arguments.
345 //===----------------------------------------------------------------------===//
346 
347 namespace {
348 class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
349   mutable llvm::OwningPtr<APIMisuse> BT;
350   mutable IdentifierInfo *Retain, *Release;
351 public:
352   CFRetainReleaseChecker(): Retain(0), Release(0) {}
353   void checkPreStmt(const CallExpr* CE, CheckerContext& C) const;
354 };
355 } // end anonymous namespace
356 
357 
358 void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE,
359                                           CheckerContext& C) const {
360   // If the CallExpr doesn't have exactly 1 argument just give up checking.
361   if (CE->getNumArgs() != 1)
362     return;
363 
364   // Get the function declaration of the callee.
365   const GRState* state = C.getState();
366   SVal X = state->getSVal(CE->getCallee());
367   const FunctionDecl* FD = X.getAsFunctionDecl();
368 
369   if (!FD)
370     return;
371 
372   if (!BT) {
373     ASTContext &Ctx = C.getASTContext();
374     Retain = &Ctx.Idents.get("CFRetain");
375     Release = &Ctx.Idents.get("CFRelease");
376     BT.reset(new APIMisuse("null passed to CFRetain/CFRelease"));
377   }
378 
379   // Check if we called CFRetain/CFRelease.
380   const IdentifierInfo *FuncII = FD->getIdentifier();
381   if (!(FuncII == Retain || FuncII == Release))
382     return;
383 
384   // FIXME: The rest of this just checks that the argument is non-null.
385   // It should probably be refactored and combined with AttrNonNullChecker.
386 
387   // Get the argument's value.
388   const Expr *Arg = CE->getArg(0);
389   SVal ArgVal = state->getSVal(Arg);
390   DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
391   if (!DefArgVal)
392     return;
393 
394   // Get a NULL value.
395   SValBuilder &svalBuilder = C.getSValBuilder();
396   DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
397 
398   // Make an expression asserting that they're equal.
399   DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
400 
401   // Are they equal?
402   const GRState *stateTrue, *stateFalse;
403   llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
404 
405   if (stateTrue && !stateFalse) {
406     ExplodedNode *N = C.generateSink(stateTrue);
407     if (!N)
408       return;
409 
410     const char *description = (FuncII == Retain)
411                             ? "Null pointer argument in call to CFRetain"
412                             : "Null pointer argument in call to CFRelease";
413 
414     EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N);
415     report->addRange(Arg->getSourceRange());
416     report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg);
417     C.EmitReport(report);
418     return;
419   }
420 
421   // From here on, we know the argument is non-null.
422   C.addTransition(stateFalse);
423 }
424 
425 //===----------------------------------------------------------------------===//
426 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
427 //===----------------------------------------------------------------------===//
428 
429 namespace {
430 class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
431   mutable Selector releaseS;
432   mutable Selector retainS;
433   mutable Selector autoreleaseS;
434   mutable Selector drainS;
435   mutable llvm::OwningPtr<BugType> BT;
436 
437 public:
438   void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
439 };
440 }
441 
442 void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
443                                               CheckerContext &C) const {
444 
445   if (!BT) {
446     BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
447                            "instance"));
448 
449     ASTContext &Ctx = C.getASTContext();
450     releaseS = GetNullarySelector("release", Ctx);
451     retainS = GetNullarySelector("retain", Ctx);
452     autoreleaseS = GetNullarySelector("autorelease", Ctx);
453     drainS = GetNullarySelector("drain", Ctx);
454   }
455 
456   if (msg.isInstanceMessage())
457     return;
458   const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
459   assert(Class);
460 
461   Selector S = msg.getSelector();
462   if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
463     return;
464 
465   if (ExplodedNode *N = C.generateNode()) {
466     llvm::SmallString<200> buf;
467     llvm::raw_svector_ostream os(buf);
468 
469     os << "The '" << S.getAsString() << "' message should be sent to instances "
470           "of class '" << Class->getName()
471        << "' and not the class directly";
472 
473     RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
474     report->addRange(msg.getSourceRange());
475     C.EmitReport(report);
476   }
477 }
478 
479 //===----------------------------------------------------------------------===//
480 // Check registration.
481 //===----------------------------------------------------------------------===//
482 
483 void ento::registerNilArgChecker(CheckerManager &mgr) {
484   mgr.registerChecker<NilArgChecker>();
485 }
486 
487 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
488   mgr.registerChecker<CFNumberCreateChecker>();
489 }
490 
491 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
492   mgr.registerChecker<CFRetainReleaseChecker>();
493 }
494 
495 void ento::registerClassReleaseChecker(CheckerManager &mgr) {
496   mgr.registerChecker<ClassReleaseChecker>();
497 }
498