blob: 88910990ce29d255697416a3c2baaec0476c0f79 [file] [log] [blame]
Ted Kremenekb0a2e472008-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
Ted Kremenek7a681942008-03-27 17:17:22 +000016#include "BasicObjCFoundationChecks.h"
17
Ted Kremenekb0a2e472008-03-27 07:25:52 +000018#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
19#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h"
Ted Kremenek9f20c7c2008-07-22 16:21:24 +000020#include "clang/Analysis/PathSensitive/GRExprEngine.h"
Ted Kremenekabd89ac2008-08-13 04:27:00 +000021#include "clang/Analysis/PathSensitive/GRState.h"
Ted Kremenekf00daf02008-04-03 17:57:38 +000022#include "clang/Analysis/PathSensitive/BugReporter.h"
Ted Kremenekb15eba42008-10-04 05:50:14 +000023#include "clang/Analysis/PathSensitive/MemRegion.h"
Ted Kremenekb0a2e472008-03-27 07:25:52 +000024#include "clang/Analysis/PathDiagnostic.h"
Ted Kremenek9f20c7c2008-07-22 16:21:24 +000025#include "clang/Analysis/LocalCheckers.h"
Daniel Dunbar64789f82008-08-11 05:35:13 +000026#include "clang/AST/DeclObjC.h"
Ted Kremenekb0a2e472008-03-27 07:25:52 +000027#include "clang/AST/Expr.h"
Steve Naroff9ed3e772008-05-29 21:12:08 +000028#include "clang/AST/ExprObjC.h"
Ted Kremenekb0a2e472008-03-27 07:25:52 +000029#include "clang/AST/ASTContext.h"
30#include "llvm/Support/Compiler.h"
31
Ted Kremenekb0a2e472008-03-27 07:25:52 +000032using namespace clang;
Ted Kremenek7a681942008-03-27 17:17:22 +000033
Steve Naroff329ec222009-07-10 23:34:53 +000034static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) {
35 const Expr* Receiver = ME->getReceiver();
Ted Kremenek639247a2008-03-27 22:05:32 +000036
37 if (!Receiver)
38 return NULL;
39
Steve Naroff329ec222009-07-10 23:34:53 +000040 if (const ObjCObjectPointerType *PT =
41 Receiver->getType()->getAsObjCObjectPointerType())
42 return PT->getInterfaceType();
Ted Kremenekd810bf82008-04-30 22:48:21 +000043
Ted Kremenekd810bf82008-04-30 22:48:21 +000044 return NULL;
Ted Kremenek639247a2008-03-27 22:05:32 +000045}
46
Steve Naroff329ec222009-07-10 23:34:53 +000047static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
48 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
Ted Kremenek639247a2008-03-27 22:05:32 +000049 return ReceiverType ? ReceiverType->getDecl()->getIdentifier()->getName()
50 : NULL;
51}
Ted Kremenek7a681942008-03-27 17:17:22 +000052
Ted Kremenekf00daf02008-04-03 17:57:38 +000053namespace {
Ted Kremenek5fd0e672008-09-21 19:01:39 +000054
Ted Kremenekbf6babf2009-02-04 23:49:09 +000055class VISIBILITY_HIDDEN APIMisuse : public BugType {
Ted Kremenekf00daf02008-04-03 17:57:38 +000056public:
Ted Kremenekbf6babf2009-02-04 23:49:09 +000057 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
Ted Kremenekf00daf02008-04-03 17:57:38 +000058};
Ted Kremenekf00daf02008-04-03 17:57:38 +000059
60class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenekbf6babf2009-02-04 23:49:09 +000061 APIMisuse *BT;
62 BugReporter& BR;
Ted Kremenekf00daf02008-04-03 17:57:38 +000063 ASTContext &Ctx;
Ted Kremenekf00daf02008-04-03 17:57:38 +000064
Steve Naroff329ec222009-07-10 23:34:53 +000065 bool isNSString(const ObjCInterfaceType *T, const char* suffix);
Zhongxing Xu0ace2712009-08-06 12:48:26 +000066 bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME);
Ted Kremenekf00daf02008-04-03 17:57:38 +000067
Zhongxing Xu0ace2712009-08-06 12:48:26 +000068 void Warn(ExplodedNode* N, const Expr* E, const std::string& s);
69 void WarnNilArg(ExplodedNode* N, const Expr* E);
Ted Kremenekf00daf02008-04-03 17:57:38 +000070
Zhongxing Xu0ace2712009-08-06 12:48:26 +000071 bool CheckNilArg(ExplodedNode* N, unsigned Arg);
Ted Kremenekf00daf02008-04-03 17:57:38 +000072
73public:
Ted Kremenekc0cccca2009-06-18 23:58:37 +000074 BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br)
75 : BT(0), BR(br), Ctx(ctx) {}
Ted Kremenekbf6babf2009-02-04 23:49:09 +000076
Zhongxing Xuff71ed02009-08-06 01:32:16 +000077 bool Audit(ExplodedNode* N, GRStateManager&);
Ted Kremenekf00daf02008-04-03 17:57:38 +000078
Ted Kremenekbf6babf2009-02-04 23:49:09 +000079private:
Zhongxing Xu0ace2712009-08-06 12:48:26 +000080 void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekbf6babf2009-02-04 23:49:09 +000081 std::string sbuf;
82 llvm::raw_string_ostream os(sbuf);
83 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
84 << ME->getSelector().getAsString() << "' cannot be nil.";
85
86 // Lazily create the BugType object for NilArg. This will be owned
87 // by the BugReporter object 'BR' once we call BR.EmitWarning.
88 if (!BT) BT = new APIMisuse("nil argument");
89
90 RangedBugReport *R = new RangedBugReport(*BT, os.str().c_str(), N);
91 R->addRange(ME->getArg(Arg)->getSourceRange());
92 BR.EmitReport(R);
Ted Kremenekf00daf02008-04-03 17:57:38 +000093 }
94};
95
96} // end anonymous namespace
97
98
99GRSimpleAPICheck*
Ted Kremenekc0cccca2009-06-18 23:58:37 +0000100clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) {
101 return new BasicObjCFoundationChecks(Ctx, BR);
Ted Kremenekf00daf02008-04-03 17:57:38 +0000102}
103
104
105
Zhongxing Xuff71ed02009-08-06 01:32:16 +0000106bool BasicObjCFoundationChecks::Audit(ExplodedNode* N,
Ted Kremenekabd89ac2008-08-13 04:27:00 +0000107 GRStateManager&) {
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000108
Ted Kremenekc08c21e2009-07-22 22:35:28 +0000109 const ObjCMessageExpr* ME =
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000110 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000111
Steve Naroff329ec222009-07-10 23:34:53 +0000112 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000113
114 if (!ReceiverType)
Nuno Lopesf44f0932008-05-20 17:33:56 +0000115 return false;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000116
Ted Kremenek639247a2008-03-27 22:05:32 +0000117 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
118
119 if (!name)
120 return false;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000121
122 if (name[0] != 'N' || name[1] != 'S')
123 return false;
124
125 name += 2;
126
Steve Naroff329ec222009-07-10 23:34:53 +0000127 // FIXME: Make all of this faster.
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000128 if (isNSString(ReceiverType, name))
129 return AuditNSString(N, ME);
130
Nuno Lopesf44f0932008-05-20 17:33:56 +0000131 return false;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000132}
133
Zhongxing Xu097fc982008-10-17 05:57:07 +0000134static inline bool isNil(SVal X) {
135 return isa<loc::ConcreteInt>(X);
Ted Kremenek583c4382008-03-27 21:15:17 +0000136}
137
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000138//===----------------------------------------------------------------------===//
139// Error reporting.
140//===----------------------------------------------------------------------===//
141
Zhongxing Xu0ace2712009-08-06 12:48:26 +0000142bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) {
Ted Kremenekc08c21e2009-07-22 22:35:28 +0000143 const ObjCMessageExpr* ME =
Ted Kremenek639247a2008-03-27 22:05:32 +0000144 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
145
Ted Kremenekc08c21e2009-07-22 22:35:28 +0000146 const Expr * E = ME->getArg(Arg);
Ted Kremenek639247a2008-03-27 22:05:32 +0000147
Ted Kremenekc0cccca2009-06-18 23:58:37 +0000148 if (isNil(N->getState()->getSVal(E))) {
Ted Kremenekf00daf02008-04-03 17:57:38 +0000149 WarnNilArg(N, ME, Arg);
Ted Kremenek639247a2008-03-27 22:05:32 +0000150 return true;
151 }
152
153 return false;
154}
155
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000156//===----------------------------------------------------------------------===//
157// NSString checking.
158//===----------------------------------------------------------------------===//
159
Steve Naroff329ec222009-07-10 23:34:53 +0000160bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T,
161 const char* suffix) {
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000162 return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
163}
164
Zhongxing Xu0ace2712009-08-06 12:48:26 +0000165bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N,
Ted Kremenekc08c21e2009-07-22 22:35:28 +0000166 const ObjCMessageExpr* ME) {
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000167
168 Selector S = ME->getSelector();
169
170 if (S.isUnarySelector())
171 return false;
172
173 // FIXME: This is going to be really slow doing these checks with
174 // lexical comparisons.
175
Chris Lattner3a8f2942008-11-24 03:33:13 +0000176 std::string name = S.getAsString();
Ted Kremenekb20dccc2008-03-27 21:23:57 +0000177 assert (!name.empty());
178 const char* cstr = &name[0];
179 unsigned len = name.size();
Ted Kremenek639247a2008-03-27 22:05:32 +0000180
Ted Kremenekb20dccc2008-03-27 21:23:57 +0000181 switch (len) {
182 default:
183 break;
Ted Kremenek44e74042008-03-28 16:09:38 +0000184 case 8:
Ted Kremenek639247a2008-03-27 22:05:32 +0000185 if (!strcmp(cstr, "compare:"))
186 return CheckNilArg(N, 0);
187
188 break;
Ted Kremenek44e74042008-03-28 16:09:38 +0000189
190 case 15:
191 // FIXME: Checking for initWithFormat: will not work in most cases
192 // yet because [NSString alloc] returns id, not NSString*. We will
193 // need support for tracking expected-type information in the analyzer
194 // to find these errors.
195 if (!strcmp(cstr, "initWithFormat:"))
196 return CheckNilArg(N, 0);
197
198 break;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000199
Ted Kremenek639247a2008-03-27 22:05:32 +0000200 case 16:
201 if (!strcmp(cstr, "compare:options:"))
202 return CheckNilArg(N, 0);
Ted Kremenekb20dccc2008-03-27 21:23:57 +0000203
204 break;
Ted Kremenek639247a2008-03-27 22:05:32 +0000205
206 case 22:
207 if (!strcmp(cstr, "compare:options:range:"))
208 return CheckNilArg(N, 0);
209
210 break;
211
212 case 23:
213
214 if (!strcmp(cstr, "caseInsensitiveCompare:"))
215 return CheckNilArg(N, 0);
216
217 break;
Ted Kremenek44e74042008-03-28 16:09:38 +0000218
Ted Kremenek639247a2008-03-27 22:05:32 +0000219 case 29:
220 if (!strcmp(cstr, "compare:options:range:locale:"))
221 return CheckNilArg(N, 0);
222
223 break;
224
225 case 37:
226 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
227 return CheckNilArg(N, 0);
228
229 break;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000230 }
231
232 return false;
233}
Ted Kremenekc1290552008-06-26 23:59:48 +0000234
235//===----------------------------------------------------------------------===//
236// Error reporting.
237//===----------------------------------------------------------------------===//
238
239namespace {
Ted Kremenekc1290552008-06-26 23:59:48 +0000240
Ted Kremenekc1290552008-06-26 23:59:48 +0000241class VISIBILITY_HIDDEN AuditCFNumberCreate : public GRSimpleAPICheck {
Ted Kremenekbf6babf2009-02-04 23:49:09 +0000242 APIMisuse* BT;
Ted Kremenekc1290552008-06-26 23:59:48 +0000243
244 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
245 // it should always be passed with a call to Audit. The latter
246 // approach makes this class more stateless.
247 ASTContext& Ctx;
248 IdentifierInfo* II;
Ted Kremenekbf6babf2009-02-04 23:49:09 +0000249 BugReporter& BR;
Ted Kremenekc0cccca2009-06-18 23:58:37 +0000250
Ted Kremenekc1290552008-06-26 23:59:48 +0000251public:
Ted Kremenekc0cccca2009-06-18 23:58:37 +0000252 AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
253 : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
Ted Kremenekc1290552008-06-26 23:59:48 +0000254
Ted Kremenekbf6babf2009-02-04 23:49:09 +0000255 ~AuditCFNumberCreate() {}
Ted Kremenekc1290552008-06-26 23:59:48 +0000256
Zhongxing Xuff71ed02009-08-06 01:32:16 +0000257 bool Audit(ExplodedNode* N, GRStateManager&);
Ted Kremenekc1290552008-06-26 23:59:48 +0000258
259private:
Zhongxing Xuff71ed02009-08-06 01:32:16 +0000260 void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N,
Ted Kremenekc1290552008-06-26 23:59:48 +0000261 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
262};
263} // end anonymous namespace
264
265enum CFNumberType {
266 kCFNumberSInt8Type = 1,
267 kCFNumberSInt16Type = 2,
268 kCFNumberSInt32Type = 3,
269 kCFNumberSInt64Type = 4,
270 kCFNumberFloat32Type = 5,
271 kCFNumberFloat64Type = 6,
272 kCFNumberCharType = 7,
273 kCFNumberShortType = 8,
274 kCFNumberIntType = 9,
275 kCFNumberLongType = 10,
276 kCFNumberLongLongType = 11,
277 kCFNumberFloatType = 12,
278 kCFNumberDoubleType = 13,
279 kCFNumberCFIndexType = 14,
280 kCFNumberNSIntegerType = 15,
281 kCFNumberCGFloatType = 16
282};
283
284namespace {
285 template<typename T>
286 class Optional {
287 bool IsKnown;
288 T Val;
289 public:
290 Optional() : IsKnown(false), Val(0) {}
291 Optional(const T& val) : IsKnown(true), Val(val) {}
292
293 bool isKnown() const { return IsKnown; }
294
295 const T& getValue() const {
296 assert (isKnown());
297 return Val;
298 }
299
300 operator const T&() const {
301 return getValue();
302 }
303 };
304}
305
306static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
307 static unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
308
309 if (i < kCFNumberCharType)
310 return FixedSize[i-1];
311
312 QualType T;
313
314 switch (i) {
315 case kCFNumberCharType: T = Ctx.CharTy; break;
316 case kCFNumberShortType: T = Ctx.ShortTy; break;
317 case kCFNumberIntType: T = Ctx.IntTy; break;
318 case kCFNumberLongType: T = Ctx.LongTy; break;
319 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
320 case kCFNumberFloatType: T = Ctx.FloatTy; break;
321 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
322 case kCFNumberCFIndexType:
323 case kCFNumberNSIntegerType:
324 case kCFNumberCGFloatType:
325 // FIXME: We need a way to map from names to Type*.
326 default:
327 return Optional<uint64_t>();
328 }
329
330 return Ctx.getTypeSize(T);
331}
332
333#if 0
334static const char* GetCFNumberTypeStr(uint64_t i) {
335 static const char* Names[] = {
336 "kCFNumberSInt8Type",
337 "kCFNumberSInt16Type",
338 "kCFNumberSInt32Type",
339 "kCFNumberSInt64Type",
340 "kCFNumberFloat32Type",
341 "kCFNumberFloat64Type",
342 "kCFNumberCharType",
343 "kCFNumberShortType",
344 "kCFNumberIntType",
345 "kCFNumberLongType",
346 "kCFNumberLongLongType",
347 "kCFNumberFloatType",
348 "kCFNumberDoubleType",
349 "kCFNumberCFIndexType",
350 "kCFNumberNSIntegerType",
351 "kCFNumberCGFloatType"
352 };
353
354 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
355}
356#endif
357
Zhongxing Xuff71ed02009-08-06 01:32:16 +0000358bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
Ted Kremenekc08c21e2009-07-22 22:35:28 +0000359 const CallExpr* CE =
360 cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
361 const Expr* Callee = CE->getCallee();
Ted Kremenekc0cccca2009-06-18 23:58:37 +0000362 SVal CallV = N->getState()->getSVal(Callee);
Zhongxing Xucac107a2009-04-20 05:24:46 +0000363 const FunctionDecl* FD = CallV.getAsFunctionDecl();
Ted Kremenekc1290552008-06-26 23:59:48 +0000364
Zhongxing Xucac107a2009-04-20 05:24:46 +0000365 if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
Ted Kremenekc1290552008-06-26 23:59:48 +0000366 return false;
367
368 // Get the value of the "theType" argument.
Ted Kremenekc0cccca2009-06-18 23:58:37 +0000369 SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
Ted Kremenekc1290552008-06-26 23:59:48 +0000370
371 // FIXME: We really should allow ranges of valid theType values, and
372 // bifurcate the state appropriately.
Zhongxing Xu097fc982008-10-17 05:57:07 +0000373 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Ted Kremenekc1290552008-06-26 23:59:48 +0000374
375 if (!V)
376 return false;
377
378 uint64_t NumberKind = V->getValue().getLimitedValue();
379 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
380
381 // FIXME: In some cases we can emit an error.
382 if (!TargetSize.isKnown())
383 return false;
384
385 // Look at the value of the integer being passed by reference. Essentially
386 // we want to catch cases where the value passed in is not equal to the
387 // size of the type being created.
Ted Kremenekc0cccca2009-06-18 23:58:37 +0000388 SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
Ted Kremenekc1290552008-06-26 23:59:48 +0000389
390 // FIXME: Eventually we should handle arbitrary locations. We can do this
391 // by having an enhanced memory model that does low-level typing.
Zhongxing Xu097fc982008-10-17 05:57:07 +0000392 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenekc1290552008-06-26 23:59:48 +0000393
394 if (!LV)
395 return false;
396
Ted Kremenekb86c11c2009-07-29 18:17:40 +0000397 const TypedRegion* R = dyn_cast<TypedRegion>(LV->getBaseRegion());
398
399 if (!R)
400 return false;
401
Zhongxing Xu20362702009-05-09 03:57:34 +0000402 QualType T = Ctx.getCanonicalType(R->getValueType(Ctx));
Ted Kremenekc1290552008-06-26 23:59:48 +0000403
404 // FIXME: If the pointee isn't an integer type, should we flag a warning?
405 // People can do weird stuff with pointers.
406
407 if (!T->isIntegerType())
408 return false;
409
410 uint64_t SourceSize = Ctx.getTypeSize(T);
411
412 // CHECK: is SourceSize == TargetSize
413
414 if (SourceSize == TargetSize)
415 return false;
416
Ted Kremenekb15eba42008-10-04 05:50:14 +0000417 AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
Ted Kremenekc1290552008-06-26 23:59:48 +0000418
419 // FIXME: We can actually create an abstract "CFNumber" object that has
420 // the bits initialized to the provided values.
421 return SourceSize < TargetSize;
422}
423
Ted Kremenekc08c21e2009-07-22 22:35:28 +0000424void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex,
Zhongxing Xuff71ed02009-08-06 01:32:16 +0000425 ExplodedNode *N,
Ted Kremenekc1290552008-06-26 23:59:48 +0000426 uint64_t SourceSize, uint64_t TargetSize,
427 uint64_t NumberKind) {
Ted Kremenekbf6babf2009-02-04 23:49:09 +0000428
429 std::string sbuf;
430 llvm::raw_string_ostream os(sbuf);
Ted Kremenekc1290552008-06-26 23:59:48 +0000431
432 os << (SourceSize == 8 ? "An " : "A ")
433 << SourceSize << " bit integer is used to initialize a CFNumber "
434 "object that represents "
435 << (TargetSize == 8 ? "an " : "a ")
436 << TargetSize << " bit integer. ";
437
438 if (SourceSize < TargetSize)
439 os << (TargetSize - SourceSize)
440 << " bits of the CFNumber value will be garbage." ;
441 else
442 os << (SourceSize - TargetSize)
443 << " bits of the input integer will be lost.";
444
Ted Kremenekbf6babf2009-02-04 23:49:09 +0000445 // Lazily create the BugType object. This will be owned
446 // by the BugReporter object 'BR' once we call BR.EmitWarning.
447 if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate");
448 RangedBugReport *report = new RangedBugReport(*BT, os.str().c_str(), N);
449 report->addRange(Ex->getSourceRange());
450 BR.EmitReport(report);
Ted Kremenekc1290552008-06-26 23:59:48 +0000451}
452
453GRSimpleAPICheck*
Ted Kremenekc0cccca2009-06-18 23:58:37 +0000454clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
455 return new AuditCFNumberCreate(Ctx, BR);
Ted Kremenekc1290552008-06-26 23:59:48 +0000456}
457
Ted Kremenek9f20c7c2008-07-22 16:21:24 +0000458//===----------------------------------------------------------------------===//
Ted Kremenekaeb9c3c2009-07-14 00:43:42 +0000459// CFRetain/CFRelease auditing for null arguments.
460//===----------------------------------------------------------------------===//
461
462namespace {
463class VISIBILITY_HIDDEN AuditCFRetainRelease : public GRSimpleAPICheck {
464 APIMisuse *BT;
465
466 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
467 // it should always be passed with a call to Audit. The latter
468 // approach makes this class more stateless.
469 ASTContext& Ctx;
470 IdentifierInfo *Retain, *Release;
471 BugReporter& BR;
472
473public:
474 AuditCFRetainRelease(ASTContext& ctx, BugReporter& br)
475 : BT(0), Ctx(ctx),
476 Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")),
477 BR(br){}
478
479 ~AuditCFRetainRelease() {}
480
Zhongxing Xuff71ed02009-08-06 01:32:16 +0000481 bool Audit(ExplodedNode* N, GRStateManager&);
Ted Kremenekaeb9c3c2009-07-14 00:43:42 +0000482};
483} // end anonymous namespace
484
485
Zhongxing Xuff71ed02009-08-06 01:32:16 +0000486bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) {
Ted Kremenekc08c21e2009-07-22 22:35:28 +0000487 const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenekaeb9c3c2009-07-14 00:43:42 +0000488
489 // If the CallExpr doesn't have exactly 1 argument just give up checking.
490 if (CE->getNumArgs() != 1)
491 return false;
492
493 // Check if we called CFRetain/CFRelease.
494 const GRState* state = N->getState();
495 SVal X = state->getSVal(CE->getCallee());
496 const FunctionDecl* FD = X.getAsFunctionDecl();
497
498 if (!FD)
499 return false;
500
501 const IdentifierInfo *FuncII = FD->getIdentifier();
502 if (!(FuncII == Retain || FuncII == Release))
503 return false;
504
505 // Finally, check if the argument is NULL.
506 // FIXME: We should be able to bifurcate the state here, as a successful
507 // check will result in the value not being NULL afterwards.
508 // FIXME: Need a way to register vistors for the BugReporter. Would like
509 // to benefit from the same diagnostics that regular null dereference
510 // reporting has.
511 if (state->getStateManager().isEqual(state, CE->getArg(0), 0)) {
512 if (!BT)
513 BT = new APIMisuse("null passed to CFRetain/CFRelease");
514
515 const char *description = (FuncII == Retain)
516 ? "Null pointer argument in call to CFRetain"
517 : "Null pointer argument in call to CFRelease";
518
519 RangedBugReport *report = new RangedBugReport(*BT, description, N);
520 report->addRange(CE->getArg(0)->getSourceRange());
521 BR.EmitReport(report);
522 return true;
523 }
524
525 return false;
526}
527
528
529GRSimpleAPICheck*
530clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) {
531 return new AuditCFRetainRelease(Ctx, BR);
532}
533
534//===----------------------------------------------------------------------===//
Ted Kremenek9f20c7c2008-07-22 16:21:24 +0000535// Check registration.
Ted Kremenekaeb9c3c2009-07-14 00:43:42 +0000536//===----------------------------------------------------------------------===//
Ted Kremenek9f20c7c2008-07-22 16:21:24 +0000537
Zhongxing Xuab7c0722009-08-21 02:18:44 +0000538void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) {
Ted Kremenek9f20c7c2008-07-22 16:21:24 +0000539 ASTContext& Ctx = Eng.getContext();
Ted Kremenekbf6babf2009-02-04 23:49:09 +0000540 BugReporter &BR = Eng.getBugReporter();
Ted Kremenek9f20c7c2008-07-22 16:21:24 +0000541
Ted Kremenekc0cccca2009-06-18 23:58:37 +0000542 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
Ted Kremenek9f20c7c2008-07-22 16:21:24 +0000543 Stmt::ObjCMessageExprClass);
Ted Kremenekaeb9c3c2009-07-14 00:43:42 +0000544 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
545 Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass);
Ted Kremenekb3538ab2008-09-18 21:25:13 +0000546
Zhongxing Xuab7c0722009-08-21 02:18:44 +0000547 RegisterNSErrorChecks(BR, Eng, D);
Ted Kremenek9f20c7c2008-07-22 16:21:24 +0000548}