xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp (revision dd058d8a50b95b12c65b28d56b2f16f86c562b1d)
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/ExplodedGraph.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.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/Core/PathSensitive/CheckerVisitor.h"
28 #include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
29 #include "clang/AST/DeclObjC.h"
30 #include "clang/AST/Expr.h"
31 #include "clang/AST/ExprObjC.h"
32 #include "clang/AST/ASTContext.h"
33 
34 using namespace clang;
35 using namespace ento;
36 
37 namespace {
38 class APIMisuse : public BugType {
39 public:
40   APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
41 };
42 } // end anonymous namespace
43 
44 //===----------------------------------------------------------------------===//
45 // Utility functions.
46 //===----------------------------------------------------------------------===//
47 
48 static const ObjCInterfaceType* GetReceiverType(const ObjCMessage &msg) {
49   if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
50     return ID->getTypeForDecl()->getAs<ObjCInterfaceType>();
51   return NULL;
52 }
53 
54 static const char* GetReceiverNameType(const ObjCMessage &msg) {
55   if (const ObjCInterfaceType *ReceiverType = GetReceiverType(msg))
56     return ReceiverType->getDecl()->getIdentifier()->getNameStart();
57   return NULL;
58 }
59 
60 static bool isNSString(llvm::StringRef ClassName) {
61   return ClassName == "NSString" || ClassName == "NSMutableString";
62 }
63 
64 static inline bool isNil(SVal X) {
65   return isa<loc::ConcreteInt>(X);
66 }
67 
68 //===----------------------------------------------------------------------===//
69 // NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
70 //===----------------------------------------------------------------------===//
71 
72 namespace {
73   class NilArgChecker : public CheckerV2<check::PreObjCMessage> {
74     mutable llvm::OwningPtr<APIMisuse> BT;
75 
76     void WarnNilArg(CheckerContext &C,
77                     const ObjCMessage &msg, unsigned Arg) const;
78 
79   public:
80     void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
81   };
82 }
83 
84 void NilArgChecker::WarnNilArg(CheckerContext &C,
85                                const ObjCMessage &msg,
86                                unsigned int Arg) const
87 {
88   if (!BT)
89     BT.reset(new APIMisuse("nil argument"));
90 
91   if (ExplodedNode *N = C.generateSink()) {
92     llvm::SmallString<128> sbuf;
93     llvm::raw_svector_ostream os(sbuf);
94     os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
95        << msg.getSelector().getAsString() << "' cannot be nil";
96 
97     RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
98     R->addRange(msg.getArgSourceRange(Arg));
99     C.EmitReport(R);
100   }
101 }
102 
103 void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
104                                         CheckerContext &C) const {
105   const ObjCInterfaceType *ReceiverType = GetReceiverType(msg);
106   if (!ReceiverType)
107     return;
108 
109   if (isNSString(ReceiverType->getDecl()->getIdentifier()->getName())) {
110     Selector S = msg.getSelector();
111 
112     if (S.isUnarySelector())
113       return;
114 
115     // FIXME: This is going to be really slow doing these checks with
116     //  lexical comparisons.
117 
118     std::string NameStr = S.getAsString();
119     llvm::StringRef Name(NameStr);
120     assert(!Name.empty());
121 
122     // FIXME: Checking for initWithFormat: will not work in most cases
123     //  yet because [NSString alloc] returns id, not NSString*.  We will
124     //  need support for tracking expected-type information in the analyzer
125     //  to find these errors.
126     if (Name == "caseInsensitiveCompare:" ||
127         Name == "compare:" ||
128         Name == "compare:options:" ||
129         Name == "compare:options:range:" ||
130         Name == "compare:options:range:locale:" ||
131         Name == "componentsSeparatedByCharactersInSet:" ||
132         Name == "initWithFormat:") {
133       if (isNil(msg.getArgSVal(0, C.getState())))
134         WarnNilArg(C, msg, 0);
135     }
136   }
137 }
138 
139 //===----------------------------------------------------------------------===//
140 // Error reporting.
141 //===----------------------------------------------------------------------===//
142 
143 namespace {
144 class CFNumberCreateChecker : public CheckerV2< check::PreStmt<CallExpr> > {
145   mutable llvm::OwningPtr<APIMisuse> BT;
146   mutable IdentifierInfo* II;
147 public:
148   CFNumberCreateChecker() : II(0) {}
149 
150   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
151 
152 private:
153   void EmitError(const TypedRegion* R, const Expr* Ex,
154                 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
155 };
156 } // end anonymous namespace
157 
158 enum CFNumberType {
159   kCFNumberSInt8Type = 1,
160   kCFNumberSInt16Type = 2,
161   kCFNumberSInt32Type = 3,
162   kCFNumberSInt64Type = 4,
163   kCFNumberFloat32Type = 5,
164   kCFNumberFloat64Type = 6,
165   kCFNumberCharType = 7,
166   kCFNumberShortType = 8,
167   kCFNumberIntType = 9,
168   kCFNumberLongType = 10,
169   kCFNumberLongLongType = 11,
170   kCFNumberFloatType = 12,
171   kCFNumberDoubleType = 13,
172   kCFNumberCFIndexType = 14,
173   kCFNumberNSIntegerType = 15,
174   kCFNumberCGFloatType = 16
175 };
176 
177 namespace {
178   template<typename T>
179   class Optional {
180     bool IsKnown;
181     T Val;
182   public:
183     Optional() : IsKnown(false), Val(0) {}
184     Optional(const T& val) : IsKnown(true), Val(val) {}
185 
186     bool isKnown() const { return IsKnown; }
187 
188     const T& getValue() const {
189       assert (isKnown());
190       return Val;
191     }
192 
193     operator const T&() const {
194       return getValue();
195     }
196   };
197 }
198 
199 static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
200   static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
201 
202   if (i < kCFNumberCharType)
203     return FixedSize[i-1];
204 
205   QualType T;
206 
207   switch (i) {
208     case kCFNumberCharType:     T = Ctx.CharTy;     break;
209     case kCFNumberShortType:    T = Ctx.ShortTy;    break;
210     case kCFNumberIntType:      T = Ctx.IntTy;      break;
211     case kCFNumberLongType:     T = Ctx.LongTy;     break;
212     case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
213     case kCFNumberFloatType:    T = Ctx.FloatTy;    break;
214     case kCFNumberDoubleType:   T = Ctx.DoubleTy;   break;
215     case kCFNumberCFIndexType:
216     case kCFNumberNSIntegerType:
217     case kCFNumberCGFloatType:
218       // FIXME: We need a way to map from names to Type*.
219     default:
220       return Optional<uint64_t>();
221   }
222 
223   return Ctx.getTypeSize(T);
224 }
225 
226 #if 0
227 static const char* GetCFNumberTypeStr(uint64_t i) {
228   static const char* Names[] = {
229     "kCFNumberSInt8Type",
230     "kCFNumberSInt16Type",
231     "kCFNumberSInt32Type",
232     "kCFNumberSInt64Type",
233     "kCFNumberFloat32Type",
234     "kCFNumberFloat64Type",
235     "kCFNumberCharType",
236     "kCFNumberShortType",
237     "kCFNumberIntType",
238     "kCFNumberLongType",
239     "kCFNumberLongLongType",
240     "kCFNumberFloatType",
241     "kCFNumberDoubleType",
242     "kCFNumberCFIndexType",
243     "kCFNumberNSIntegerType",
244     "kCFNumberCGFloatType"
245   };
246 
247   return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
248 }
249 #endif
250 
251 void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
252                                          CheckerContext &C) const {
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.reset(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 CheckerV2< check::PreStmt<CallExpr> > {
352   mutable llvm::OwningPtr<APIMisuse> BT;
353   mutable IdentifierInfo *Retain, *Release;
354 public:
355   CFRetainReleaseChecker(): Retain(0), Release(0) {}
356   void checkPreStmt(const CallExpr* CE, CheckerContext& C) const;
357 };
358 } // end anonymous namespace
359 
360 
361 void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE,
362                                           CheckerContext& C) const {
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.reset(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 CheckerV2<check::PreObjCMessage> {
434   mutable Selector releaseS;
435   mutable Selector retainS;
436   mutable Selector autoreleaseS;
437   mutable Selector drainS;
438   mutable llvm::OwningPtr<BugType> BT;
439 
440 public:
441   void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
442 };
443 }
444 
445 void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
446                                               CheckerContext &C) const {
447 
448   if (!BT) {
449     BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
450                            "instance"));
451 
452     ASTContext &Ctx = C.getASTContext();
453     releaseS = GetNullarySelector("release", Ctx);
454     retainS = GetNullarySelector("retain", Ctx);
455     autoreleaseS = GetNullarySelector("autorelease", Ctx);
456     drainS = GetNullarySelector("drain", Ctx);
457   }
458 
459   if (msg.isInstanceMessage())
460     return;
461   const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
462   assert(Class);
463 
464   Selector S = msg.getSelector();
465   if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
466     return;
467 
468   if (ExplodedNode *N = C.generateNode()) {
469     llvm::SmallString<200> buf;
470     llvm::raw_svector_ostream os(buf);
471 
472     os << "The '" << S.getAsString() << "' message should be sent to instances "
473           "of class '" << Class->getName()
474        << "' and not the class directly";
475 
476     RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
477     report->addRange(msg.getSourceRange());
478     C.EmitReport(report);
479   }
480 }
481 
482 //===----------------------------------------------------------------------===//
483 // Check registration.
484 //===----------------------------------------------------------------------===//
485 
486 void ento::registerNilArgChecker(CheckerManager &mgr) {
487   mgr.registerChecker<NilArgChecker>();
488 }
489 
490 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
491   mgr.registerChecker<CFNumberCreateChecker>();
492 }
493 
494 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
495   mgr.registerChecker<CFRetainReleaseChecker>();
496 }
497 
498 void ento::registerClassReleaseChecker(CheckerManager &mgr) {
499   mgr.registerChecker<ClassReleaseChecker>();
500 }
501