blob: 1f627297ec895b2a83bb896a2c495b0fbb5243e0 [file] [log] [blame]
Ted Kremenek99c6ad32008-03-27 07:25:52 +00001//== 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
Argyrios Kyrtzidis0b1ba622011-02-16 01:40:52 +000016#include "ClangSACheckers.h"
Ted Kremenek928c4152011-03-17 04:01:35 +000017#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +000018#include "clang/StaticAnalyzer/Core/Checker.h"
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +000019#include "clang/StaticAnalyzer/Core/CheckerManager.h"
Argyrios Kyrtzidis983326f2011-02-23 01:05:36 +000020#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
Ted Kremenek9b663712011-02-10 01:03:03 +000021#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
Ted Kremenek9b663712011-02-10 01:03:03 +000022#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
Jordy Rosed1e5a892011-09-02 08:02:59 +000023#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
Ted Kremenek18c66fd2011-08-15 22:09:50 +000024#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
Ted Kremenek9b663712011-02-10 01:03:03 +000025#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
26#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
Daniel Dunbarc4a1dea2008-08-11 05:35:13 +000027#include "clang/AST/DeclObjC.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000028#include "clang/AST/Expr.h"
Steve Narofff494b572008-05-29 21:12:08 +000029#include "clang/AST/ExprObjC.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000030#include "clang/AST/ASTContext.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000031
Ted Kremenek99c6ad32008-03-27 07:25:52 +000032using namespace clang;
Ted Kremenek9ef65372010-12-23 07:20:52 +000033using namespace ento;
Ted Kremenek52755612008-03-27 17:17:22 +000034
Ted Kremenek2ce2baa2010-10-20 23:38:56 +000035namespace {
36class APIMisuse : public BugType {
37public:
38 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
39};
40} // end anonymous namespace
41
42//===----------------------------------------------------------------------===//
43// Utility functions.
44//===----------------------------------------------------------------------===//
45
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +000046static const char* GetReceiverNameType(const ObjCMessage &msg) {
Anders Carlssonb62bdce2011-03-08 20:05:26 +000047 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
48 return ID->getIdentifier()->getNameStart();
49 return 0;
Ted Kremenek4ba62832008-03-27 22:05:32 +000050}
Ted Kremenek52755612008-03-27 17:17:22 +000051
Anders Carlssonb62bdce2011-03-08 20:05:26 +000052static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID,
Chris Lattner5f9e2722011-07-23 10:55:15 +000053 StringRef ClassName) {
Anders Carlssonb62bdce2011-03-08 20:05:26 +000054 if (ID->getIdentifier()->getName() == ClassName)
55 return true;
56
57 if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
58 return isReceiverClassOrSuperclass(Super, ClassName);
59
60 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +000061}
62
Zhongxing Xu1c96b242008-10-17 05:57:07 +000063static inline bool isNil(SVal X) {
Mike Stump1eb44332009-09-09 15:08:12 +000064 return isa<loc::ConcreteInt>(X);
Ted Kremeneke5d5c202008-03-27 21:15:17 +000065}
66
Ted Kremenek99c6ad32008-03-27 07:25:52 +000067//===----------------------------------------------------------------------===//
Ted Kremenek2ce2baa2010-10-20 23:38:56 +000068// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
Ted Kremenek99c6ad32008-03-27 07:25:52 +000069//===----------------------------------------------------------------------===//
70
Benjamin Kramercb9c0742010-10-22 16:33:16 +000071namespace {
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +000072 class NilArgChecker : public Checker<check::PreObjCMessage> {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +000073 mutable llvm::OwningPtr<APIMisuse> BT;
74
75 void WarnNilArg(CheckerContext &C,
76 const ObjCMessage &msg, unsigned Arg) const;
77
Benjamin Kramercb9c0742010-10-22 16:33:16 +000078 public:
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +000079 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
Benjamin Kramercb9c0742010-10-22 16:33:16 +000080 };
81}
Mike Stump1eb44332009-09-09 15:08:12 +000082
Ted Kremenek2ce2baa2010-10-20 23:38:56 +000083void NilArgChecker::WarnNilArg(CheckerContext &C,
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +000084 const ObjCMessage &msg,
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +000085 unsigned int Arg) const
Ted Kremenek2ce2baa2010-10-20 23:38:56 +000086{
87 if (!BT)
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +000088 BT.reset(new APIMisuse("nil argument"));
Ted Kremenek2ce2baa2010-10-20 23:38:56 +000089
Ted Kremenekd048c6e2010-12-20 21:19:09 +000090 if (ExplodedNode *N = C.generateSink()) {
Ted Kremenek2ce2baa2010-10-20 23:38:56 +000091 llvm::SmallString<128> sbuf;
92 llvm::raw_svector_ostream os(sbuf);
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +000093 os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
94 << msg.getSelector().getAsString() << "' cannot be nil";
Mike Stump1eb44332009-09-09 15:08:12 +000095
Anna Zakse172e8b2011-08-17 23:00:25 +000096 BugReport *R = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +000097 R->addRange(msg.getArgSourceRange(Arg));
Ted Kremenek2ce2baa2010-10-20 23:38:56 +000098 C.EmitReport(R);
Ted Kremenek4ba62832008-03-27 22:05:32 +000099 }
Ted Kremenek4ba62832008-03-27 22:05:32 +0000100}
101
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000102void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
103 CheckerContext &C) const {
Anders Carlssonb62bdce2011-03-08 20:05:26 +0000104 const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
105 if (!ID)
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000106 return;
107
Anders Carlssonb62bdce2011-03-08 20:05:26 +0000108 if (isReceiverClassOrSuperclass(ID, "NSString")) {
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000109 Selector S = msg.getSelector();
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000110
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();
Chris Lattner5f9e2722011-07-23 10:55:15 +0000118 StringRef Name(NameStr);
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000119 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:") {
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000132 if (isNil(msg.getArgSVal(0, C.getState())))
133 WarnNilArg(C, msg, 0);
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000134 }
135 }
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000136}
Ted Kremenek04bc8762008-06-26 23:59:48 +0000137
138//===----------------------------------------------------------------------===//
139// Error reporting.
140//===----------------------------------------------------------------------===//
141
142namespace {
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +0000143class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000144 mutable llvm::OwningPtr<APIMisuse> BT;
145 mutable IdentifierInfo* II;
Ted Kremenek04bc8762008-06-26 23:59:48 +0000146public:
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000147 CFNumberCreateChecker() : II(0) {}
148
149 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
150
Ted Kremenek04bc8762008-06-26 23:59:48 +0000151private:
Ted Kremenek9c378f72011-08-12 23:37:29 +0000152 void EmitError(const TypedRegion* R, const Expr *Ex,
Mike Stump1eb44332009-09-09 15:08:12 +0000153 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000154};
155} // end anonymous namespace
156
157enum 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
176namespace {
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) {}
Mike Stump1eb44332009-09-09 15:08:12 +0000184
Ted Kremenek04bc8762008-06-26 23:59:48 +0000185 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
Ted Kremenek9c378f72011-08-12 23:37:29 +0000198static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
Nuno Lopes2550d702009-12-23 17:49:57 +0000199 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump1eb44332009-09-09 15:08:12 +0000200
Ted Kremenek04bc8762008-06-26 23:59:48 +0000201 if (i < kCFNumberCharType)
202 return FixedSize[i-1];
Mike Stump1eb44332009-09-09 15:08:12 +0000203
Ted Kremenek04bc8762008-06-26 23:59:48 +0000204 QualType T;
Mike Stump1eb44332009-09-09 15:08:12 +0000205
Ted Kremenek04bc8762008-06-26 23:59:48 +0000206 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:
Mike Stump1eb44332009-09-09 15:08:12 +0000217 // FIXME: We need a way to map from names to Type*.
Ted Kremenek04bc8762008-06-26 23:59:48 +0000218 default:
219 return Optional<uint64_t>();
220 }
Mike Stump1eb44332009-09-09 15:08:12 +0000221
Ted Kremenek04bc8762008-06-26 23:59:48 +0000222 return Ctx.getTypeSize(T);
223}
224
225#if 0
226static 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 };
Mike Stump1eb44332009-09-09 15:08:12 +0000245
Ted Kremenek04bc8762008-06-26 23:59:48 +0000246 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
247}
248#endif
249
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000250void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
251 CheckerContext &C) const {
Ted Kremenek18c66fd2011-08-15 22:09:50 +0000252 const ProgramState *state = C.getState();
Anna Zaksb805c8f2011-12-01 05:57:37 +0000253 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000254 if (!FD)
255 return;
256
257 ASTContext &Ctx = C.getASTContext();
258 if (!II)
259 II = &Ctx.Idents.get("CFNumberCreate");
260
261 if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
262 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000263
Ted Kremenek04bc8762008-06-26 23:59:48 +0000264 // Get the value of the "theType" argument.
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000265 SVal TheTypeVal = state->getSVal(CE->getArg(1));
Mike Stump1eb44332009-09-09 15:08:12 +0000266
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000267 // FIXME: We really should allow ranges of valid theType values, and
268 // bifurcate the state appropriately.
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000269 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000270 if (!V)
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000271 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000272
Ted Kremenek04bc8762008-06-26 23:59:48 +0000273 uint64_t NumberKind = V->getValue().getLimitedValue();
274 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000275
Ted Kremenek04bc8762008-06-26 23:59:48 +0000276 // FIXME: In some cases we can emit an error.
277 if (!TargetSize.isKnown())
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000278 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000279
Ted Kremenek04bc8762008-06-26 23:59:48 +0000280 // Look at the value of the integer being passed by reference. Essentially
281 // we want to catch cases where the value passed in is not equal to the
282 // size of the type being created.
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000283 SVal TheValueExpr = state->getSVal(CE->getArg(2));
Mike Stump1eb44332009-09-09 15:08:12 +0000284
Ted Kremenek04bc8762008-06-26 23:59:48 +0000285 // FIXME: Eventually we should handle arbitrary locations. We can do this
286 // by having an enhanced memory model that does low-level typing.
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000287 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000288 if (!LV)
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000289 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000290
Ted Kremenek96979342011-08-12 20:02:48 +0000291 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
Ted Kremenek5e77eba2009-07-29 18:17:40 +0000292 if (!R)
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000293 return;
Ted Kremenek5e77eba2009-07-29 18:17:40 +0000294
Zhongxing Xu018220c2010-08-11 06:10:55 +0000295 QualType T = Ctx.getCanonicalType(R->getValueType());
Mike Stump1eb44332009-09-09 15:08:12 +0000296
Ted Kremenek04bc8762008-06-26 23:59:48 +0000297 // FIXME: If the pointee isn't an integer type, should we flag a warning?
298 // People can do weird stuff with pointers.
Mike Stump1eb44332009-09-09 15:08:12 +0000299
300 if (!T->isIntegerType())
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000301 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000302
Ted Kremenek04bc8762008-06-26 23:59:48 +0000303 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump1eb44332009-09-09 15:08:12 +0000304
Ted Kremenek04bc8762008-06-26 23:59:48 +0000305 // CHECK: is SourceSize == TargetSize
Ted Kremenek04bc8762008-06-26 23:59:48 +0000306 if (SourceSize == TargetSize)
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000307 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000308
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000309 // Generate an error. Only generate a sink if 'SourceSize < TargetSize';
310 // otherwise generate a regular node.
311 //
Ted Kremenek04bc8762008-06-26 23:59:48 +0000312 // FIXME: We can actually create an abstract "CFNumber" object that has
313 // the bits initialized to the provided values.
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000314 //
Ted Kremenekd048c6e2010-12-20 21:19:09 +0000315 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
Anna Zaks0bd6b112011-10-26 21:06:34 +0000316 : C.addTransition()) {
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000317 llvm::SmallString<128> sbuf;
318 llvm::raw_svector_ostream os(sbuf);
319
320 os << (SourceSize == 8 ? "An " : "A ")
321 << SourceSize << " bit integer is used to initialize a CFNumber "
322 "object that represents "
323 << (TargetSize == 8 ? "an " : "a ")
324 << TargetSize << " bit integer. ";
325
326 if (SourceSize < TargetSize)
327 os << (TargetSize - SourceSize)
328 << " bits of the CFNumber value will be garbage." ;
329 else
330 os << (SourceSize - TargetSize)
331 << " bits of the input integer will be lost.";
Ted Kremenek04bc8762008-06-26 23:59:48 +0000332
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000333 if (!BT)
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000334 BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000335
Anna Zakse172e8b2011-08-17 23:00:25 +0000336 BugReport *report = new BugReport(*BT, os.str(), N);
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000337 report->addRange(CE->getArg(2)->getSourceRange());
338 C.EmitReport(report);
339 }
Ted Kremenek04bc8762008-06-26 23:59:48 +0000340}
341
Ted Kremenek78d46242008-07-22 16:21:24 +0000342//===----------------------------------------------------------------------===//
Jordy Rose61fb55c2010-07-06 02:34:42 +0000343// CFRetain/CFRelease checking for null arguments.
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000344//===----------------------------------------------------------------------===//
345
346namespace {
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +0000347class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000348 mutable llvm::OwningPtr<APIMisuse> BT;
349 mutable IdentifierInfo *Retain, *Release;
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000350public:
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000351 CFRetainReleaseChecker(): Retain(0), Release(0) {}
Ted Kremenek9c378f72011-08-12 23:37:29 +0000352 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000353};
354} // end anonymous namespace
355
356
Ted Kremenek9c378f72011-08-12 23:37:29 +0000357void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
358 CheckerContext &C) const {
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000359 // If the CallExpr doesn't have exactly 1 argument just give up checking.
360 if (CE->getNumArgs() != 1)
Jordy Rose61fb55c2010-07-06 02:34:42 +0000361 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000362
Ted Kremenek18c66fd2011-08-15 22:09:50 +0000363 const ProgramState *state = C.getState();
Anna Zaksb805c8f2011-12-01 05:57:37 +0000364 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000365 if (!FD)
Jordy Rose61fb55c2010-07-06 02:34:42 +0000366 return;
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000367
368 if (!BT) {
369 ASTContext &Ctx = C.getASTContext();
370 Retain = &Ctx.Idents.get("CFRetain");
371 Release = &Ctx.Idents.get("CFRelease");
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000372 BT.reset(new APIMisuse("null passed to CFRetain/CFRelease"));
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000373 }
Mike Stump1eb44332009-09-09 15:08:12 +0000374
Jordy Rose61fb55c2010-07-06 02:34:42 +0000375 // Check if we called CFRetain/CFRelease.
Mike Stump1eb44332009-09-09 15:08:12 +0000376 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000377 if (!(FuncII == Retain || FuncII == Release))
Jordy Rose61fb55c2010-07-06 02:34:42 +0000378 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000379
Jordy Rose61fb55c2010-07-06 02:34:42 +0000380 // FIXME: The rest of this just checks that the argument is non-null.
381 // It should probably be refactored and combined with AttrNonNullChecker.
382
383 // Get the argument's value.
384 const Expr *Arg = CE->getArg(0);
385 SVal ArgVal = state->getSVal(Arg);
386 DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
387 if (!DefArgVal)
388 return;
389
390 // Get a NULL value.
Ted Kremenekc8413fd2010-12-02 07:49:45 +0000391 SValBuilder &svalBuilder = C.getSValBuilder();
392 DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
Jordy Rose61fb55c2010-07-06 02:34:42 +0000393
394 // Make an expression asserting that they're equal.
Ted Kremenekc8413fd2010-12-02 07:49:45 +0000395 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
Jordy Rose61fb55c2010-07-06 02:34:42 +0000396
397 // Are they equal?
Ted Kremenek18c66fd2011-08-15 22:09:50 +0000398 const ProgramState *stateTrue, *stateFalse;
Ted Kremenek28f47b92010-12-01 22:16:56 +0000399 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
Jordy Rose61fb55c2010-07-06 02:34:42 +0000400
401 if (stateTrue && !stateFalse) {
Ted Kremenekd048c6e2010-12-20 21:19:09 +0000402 ExplodedNode *N = C.generateSink(stateTrue);
Jordy Rose61fb55c2010-07-06 02:34:42 +0000403 if (!N)
404 return;
405
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000406 const char *description = (FuncII == Retain)
407 ? "Null pointer argument in call to CFRetain"
408 : "Null pointer argument in call to CFRelease";
409
Anna Zakse172e8b2011-08-17 23:00:25 +0000410 BugReport *report = new BugReport(*BT, description, N);
Jordy Rose61fb55c2010-07-06 02:34:42 +0000411 report->addRange(Arg->getSourceRange());
Anna Zaks50bbc162011-08-19 22:33:38 +0000412 report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg));
Jordy Rose61fb55c2010-07-06 02:34:42 +0000413 C.EmitReport(report);
414 return;
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000415 }
416
Jordy Rose61fb55c2010-07-06 02:34:42 +0000417 // From here on, we know the argument is non-null.
Anna Zaks0bd6b112011-10-26 21:06:34 +0000418 C.addTransition(stateFalse);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000419}
420
421//===----------------------------------------------------------------------===//
Ted Kremenek50e837b2009-11-20 05:27:05 +0000422// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
423//===----------------------------------------------------------------------===//
424
425namespace {
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +0000426class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000427 mutable Selector releaseS;
428 mutable Selector retainS;
429 mutable Selector autoreleaseS;
430 mutable Selector drainS;
431 mutable llvm::OwningPtr<BugType> BT;
Ted Kremenek50e837b2009-11-20 05:27:05 +0000432
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000433public:
434 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
Ted Kremenek50e837b2009-11-20 05:27:05 +0000435};
436}
437
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000438void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
439 CheckerContext &C) const {
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000440
441 if (!BT) {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000442 BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
443 "instance"));
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000444
445 ASTContext &Ctx = C.getASTContext();
446 releaseS = GetNullarySelector("release", Ctx);
447 retainS = GetNullarySelector("retain", Ctx);
448 autoreleaseS = GetNullarySelector("autorelease", Ctx);
449 drainS = GetNullarySelector("drain", Ctx);
450 }
451
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000452 if (msg.isInstanceMessage())
Ted Kremenek50e837b2009-11-20 05:27:05 +0000453 return;
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000454 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
455 assert(Class);
Douglas Gregor04badcf2010-04-21 00:45:42 +0000456
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000457 Selector S = msg.getSelector();
Benjamin Kramer921ddc42009-11-20 10:03:00 +0000458 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremenek50e837b2009-11-20 05:27:05 +0000459 return;
460
Anna Zaks0bd6b112011-10-26 21:06:34 +0000461 if (ExplodedNode *N = C.addTransition()) {
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000462 llvm::SmallString<200> buf;
463 llvm::raw_svector_ostream os(buf);
Ted Kremenek19d67b52009-11-23 22:22:01 +0000464
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000465 os << "The '" << S.getAsString() << "' message should be sent to instances "
466 "of class '" << Class->getName()
467 << "' and not the class directly";
Ted Kremenek50e837b2009-11-20 05:27:05 +0000468
Anna Zakse172e8b2011-08-17 23:00:25 +0000469 BugReport *report = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000470 report->addRange(msg.getSourceRange());
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000471 C.EmitReport(report);
472 }
Ted Kremenek50e837b2009-11-20 05:27:05 +0000473}
474
475//===----------------------------------------------------------------------===//
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000476// Check for passing non-Objective-C types to variadic methods that expect
477// only Objective-C types.
478//===----------------------------------------------------------------------===//
479
480namespace {
481class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
482 mutable Selector arrayWithObjectsS;
483 mutable Selector dictionaryWithObjectsAndKeysS;
484 mutable Selector setWithObjectsS;
485 mutable Selector initWithObjectsS;
486 mutable Selector initWithObjectsAndKeysS;
487 mutable llvm::OwningPtr<BugType> BT;
488
489 bool isVariadicMessage(const ObjCMessage &msg) const;
490
491public:
492 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
493};
494}
495
496/// isVariadicMessage - Returns whether the given message is a variadic message,
497/// where all arguments must be Objective-C types.
498bool
499VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const {
500 const ObjCMethodDecl *MD = msg.getMethodDecl();
Ted Kremenek9281efe2011-04-12 21:47:05 +0000501
502 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000503 return false;
504
505 Selector S = msg.getSelector();
506
507 if (msg.isInstanceMessage()) {
508 // FIXME: Ideally we'd look at the receiver interface here, but that's not
509 // useful for init, because alloc returns 'id'. In theory, this could lead
510 // to false positives, for example if there existed a class that had an
511 // initWithObjects: implementation that does accept non-Objective-C pointer
512 // types, but the chance of that happening is pretty small compared to the
513 // gains that this analysis gives.
514 const ObjCInterfaceDecl *Class = MD->getClassInterface();
515
516 // -[NSArray initWithObjects:]
517 if (isReceiverClassOrSuperclass(Class, "NSArray") &&
518 S == initWithObjectsS)
519 return true;
520
521 // -[NSDictionary initWithObjectsAndKeys:]
522 if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
523 S == initWithObjectsAndKeysS)
524 return true;
525
526 // -[NSSet initWithObjects:]
527 if (isReceiverClassOrSuperclass(Class, "NSSet") &&
528 S == initWithObjectsS)
529 return true;
530 } else {
531 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
532
533 // -[NSArray arrayWithObjects:]
534 if (isReceiverClassOrSuperclass(Class, "NSArray") &&
535 S == arrayWithObjectsS)
536 return true;
537
538 // -[NSDictionary dictionaryWithObjectsAndKeys:]
539 if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
540 S == dictionaryWithObjectsAndKeysS)
541 return true;
542
543 // -[NSSet setWithObjects:]
544 if (isReceiverClassOrSuperclass(Class, "NSSet") &&
545 S == setWithObjectsS)
546 return true;
547 }
548
549 return false;
550}
551
552void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
553 CheckerContext &C) const {
554 if (!BT) {
555 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
556 "Objective-C pointer types"));
557
558 ASTContext &Ctx = C.getASTContext();
559 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
560 dictionaryWithObjectsAndKeysS =
561 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
562 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
563
564 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
565 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
566 }
567
568 if (!isVariadicMessage(msg))
569 return;
570
571 // We are not interested in the selector arguments since they have
572 // well-defined types, so the compiler will issue a warning for them.
573 unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
574
575 // We're not interested in the last argument since it has to be nil or the
576 // compiler would have issued a warning for it elsewhere.
577 unsigned variadicArgsEnd = msg.getNumArgs() - 1;
578
579 if (variadicArgsEnd <= variadicArgsBegin)
580 return;
581
582 // Verify that all arguments have Objective-C types.
Ted Kremenek6fb5c1f2011-03-14 19:50:37 +0000583 llvm::Optional<ExplodedNode*> errorNode;
Ted Kremenek18c66fd2011-08-15 22:09:50 +0000584 const ProgramState *state = C.getState();
Ted Kremenek6fb5c1f2011-03-14 19:50:37 +0000585
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000586 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
587 QualType ArgTy = msg.getArgType(I);
588 if (ArgTy->isObjCObjectPointerType())
589 continue;
590
Anders Carlssonf05982b2011-04-19 01:16:46 +0000591 // Block pointers are treaded as Objective-C pointers.
592 if (ArgTy->isBlockPointerType())
593 continue;
594
Ted Kremenekd5fde212011-03-16 00:22:51 +0000595 // Ignore pointer constants.
596 if (isa<loc::ConcreteInt>(msg.getArgSVal(I, state)))
597 continue;
Ted Kremenek928c4152011-03-17 04:01:35 +0000598
Ted Kremenekf3f92932011-03-17 04:10:25 +0000599 // Ignore pointer types annotated with 'NSObject' attribute.
600 if (C.getASTContext().isObjCNSObjectType(ArgTy))
601 continue;
602
Ted Kremenek928c4152011-03-17 04:01:35 +0000603 // Ignore CF references, which can be toll-free bridged.
Ted Kremenek05560482011-07-16 19:50:32 +0000604 if (coreFoundation::isCFObjectRef(ArgTy))
Ted Kremenek928c4152011-03-17 04:01:35 +0000605 continue;
Ted Kremenekd5fde212011-03-16 00:22:51 +0000606
Ted Kremenek6fb5c1f2011-03-14 19:50:37 +0000607 // Generate only one error node to use for all bug reports.
608 if (!errorNode.hasValue()) {
Anna Zaks0bd6b112011-10-26 21:06:34 +0000609 errorNode = C.addTransition();
Ted Kremenek6fb5c1f2011-03-14 19:50:37 +0000610 }
611
612 if (!errorNode.getValue())
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000613 continue;
614
615 llvm::SmallString<128> sbuf;
616 llvm::raw_svector_ostream os(sbuf);
617
618 if (const char *TypeName = GetReceiverNameType(msg))
619 os << "Argument to '" << TypeName << "' method '";
620 else
621 os << "Argument to method '";
622
623 os << msg.getSelector().getAsString()
624 << "' should be an Objective-C pointer type, not '"
625 << ArgTy.getAsString() << "'";
626
Anna Zakse172e8b2011-08-17 23:00:25 +0000627 BugReport *R = new BugReport(*BT, os.str(),
Ted Kremenek6fb5c1f2011-03-14 19:50:37 +0000628 errorNode.getValue());
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000629 R->addRange(msg.getArgSourceRange(I));
630 C.EmitReport(R);
631 }
632}
633
634//===----------------------------------------------------------------------===//
Ted Kremenek78d46242008-07-22 16:21:24 +0000635// Check registration.
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000636//===----------------------------------------------------------------------===//
Argyrios Kyrtzidis0b1ba622011-02-16 01:40:52 +0000637
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +0000638void ento::registerNilArgChecker(CheckerManager &mgr) {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000639 mgr.registerChecker<NilArgChecker>();
Argyrios Kyrtzidis0b1ba622011-02-16 01:40:52 +0000640}
641
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +0000642void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000643 mgr.registerChecker<CFNumberCreateChecker>();
Argyrios Kyrtzidis0b1ba622011-02-16 01:40:52 +0000644}
645
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +0000646void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000647 mgr.registerChecker<CFRetainReleaseChecker>();
Ted Kremenek78d46242008-07-22 16:21:24 +0000648}
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +0000649
650void ento::registerClassReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000651 mgr.registerChecker<ClassReleaseChecker>();
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +0000652}
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000653
654void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
655 mgr.registerChecker<VariadicMethodTypeChecker>();
656}