blob: 8bbf94c072e9bdc04d2c8a874f3be39653dcc6f3 [file] [log] [blame]
Ted Kremenekc0414922008-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 Kremeneka4d60b62008-03-27 17:17:22 +000016#include "BasicObjCFoundationChecks.h"
17
Ted Kremenekc0414922008-03-27 07:25:52 +000018#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
19#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h"
Ted Kremenek1f352db2008-07-22 16:21:24 +000020#include "clang/Analysis/PathSensitive/GRExprEngine.h"
Ted Kremenek5ab5a1b2008-08-13 04:27:00 +000021#include "clang/Analysis/PathSensitive/GRState.h"
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000022#include "clang/Analysis/PathSensitive/BugReporter.h"
Ted Kremenek5ca90a22008-10-04 05:50:14 +000023#include "clang/Analysis/PathSensitive/MemRegion.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000024#include "clang/Analysis/PathDiagnostic.h"
Ted Kremenek1f352db2008-07-22 16:21:24 +000025#include "clang/Analysis/LocalCheckers.h"
Daniel Dunbar6e8aa532008-08-11 05:35:13 +000026#include "clang/AST/DeclObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000027#include "clang/AST/Expr.h"
Steve Naroff021ca182008-05-29 21:12:08 +000028#include "clang/AST/ExprObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000029#include "clang/AST/ASTContext.h"
30#include "llvm/Support/Compiler.h"
31
Ted Kremenekc0414922008-03-27 07:25:52 +000032using namespace clang;
Ted Kremeneka4d60b62008-03-27 17:17:22 +000033
Steve Naroff7cae42b2009-07-10 23:34:53 +000034static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) {
35 const Expr* Receiver = ME->getReceiver();
Ted Kremenek276278e2008-03-27 22:05:32 +000036
37 if (!Receiver)
38 return NULL;
39
Steve Naroff7cae42b2009-07-10 23:34:53 +000040 if (const ObjCObjectPointerType *PT =
41 Receiver->getType()->getAsObjCObjectPointerType())
42 return PT->getInterfaceType();
Ted Kremenekf20e2282008-04-30 22:48:21 +000043
Ted Kremenekf20e2282008-04-30 22:48:21 +000044 return NULL;
Ted Kremenek276278e2008-03-27 22:05:32 +000045}
46
Steve Naroff7cae42b2009-07-10 23:34:53 +000047static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
48 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
Ted Kremenek276278e2008-03-27 22:05:32 +000049 return ReceiverType ? ReceiverType->getDecl()->getIdentifier()->getName()
50 : NULL;
51}
Ted Kremeneka4d60b62008-03-27 17:17:22 +000052
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000053namespace {
Ted Kremenek638e2802008-09-21 19:01:39 +000054
Ted Kremenekfc5d0672009-02-04 23:49:09 +000055class VISIBILITY_HIDDEN APIMisuse : public BugType {
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000056public:
Ted Kremenekfc5d0672009-02-04 23:49:09 +000057 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000058};
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000059
60class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenekfc5d0672009-02-04 23:49:09 +000061 APIMisuse *BT;
62 BugReporter& BR;
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000063 ASTContext &Ctx;
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000064
Steve Naroff7cae42b2009-07-10 23:34:53 +000065 bool isNSString(const ObjCInterfaceType *T, const char* suffix);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000066 bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME);
67
68 void Warn(NodeTy* N, Expr* E, const std::string& s);
69 void WarnNilArg(NodeTy* N, Expr* E);
70
71 bool CheckNilArg(NodeTy* N, unsigned Arg);
72
73public:
Ted Kremenek095f1a92009-06-18 23:58:37 +000074 BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br)
75 : BT(0), BR(br), Ctx(ctx) {}
Ted Kremenekfc5d0672009-02-04 23:49:09 +000076
77 bool Audit(ExplodedNode<GRState>* N, GRStateManager&);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000078
Ted Kremenekfc5d0672009-02-04 23:49:09 +000079private:
80 void WarnNilArg(NodeTy* N, ObjCMessageExpr* ME, unsigned Arg) {
81 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 Kremenekcb2dc8e2008-04-03 17:57:38 +000093 }
94};
95
96} // end anonymous namespace
97
98
99GRSimpleAPICheck*
Ted Kremenek095f1a92009-06-18 23:58:37 +0000100clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) {
101 return new BasicObjCFoundationChecks(Ctx, BR);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000102}
103
104
105
Ted Kremenek5ab5a1b2008-08-13 04:27:00 +0000106bool BasicObjCFoundationChecks::Audit(ExplodedNode<GRState>* N,
107 GRStateManager&) {
Ted Kremenekc0414922008-03-27 07:25:52 +0000108
109 ObjCMessageExpr* ME =
110 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenekc0414922008-03-27 07:25:52 +0000111
Steve Naroff7cae42b2009-07-10 23:34:53 +0000112 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
Ted Kremenekc0414922008-03-27 07:25:52 +0000113
114 if (!ReceiverType)
Nuno Lopes652eaab2008-05-20 17:33:56 +0000115 return false;
Ted Kremenekc0414922008-03-27 07:25:52 +0000116
Ted Kremenek276278e2008-03-27 22:05:32 +0000117 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
118
119 if (!name)
120 return false;
Ted Kremenekc0414922008-03-27 07:25:52 +0000121
122 if (name[0] != 'N' || name[1] != 'S')
123 return false;
124
125 name += 2;
126
Steve Naroff7cae42b2009-07-10 23:34:53 +0000127 // FIXME: Make all of this faster.
Ted Kremenekc0414922008-03-27 07:25:52 +0000128 if (isNSString(ReceiverType, name))
129 return AuditNSString(N, ME);
130
Nuno Lopes652eaab2008-05-20 17:33:56 +0000131 return false;
Ted Kremenekc0414922008-03-27 07:25:52 +0000132}
133
Zhongxing Xu27f17422008-10-17 05:57:07 +0000134static inline bool isNil(SVal X) {
135 return isa<loc::ConcreteInt>(X);
Ted Kremenek27156c82008-03-27 21:15:17 +0000136}
137
Ted Kremenekc0414922008-03-27 07:25:52 +0000138//===----------------------------------------------------------------------===//
139// Error reporting.
140//===----------------------------------------------------------------------===//
141
Ted Kremenek276278e2008-03-27 22:05:32 +0000142bool BasicObjCFoundationChecks::CheckNilArg(NodeTy* N, unsigned Arg) {
143 ObjCMessageExpr* ME =
144 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
145
146 Expr * E = ME->getArg(Arg);
147
Ted Kremenek095f1a92009-06-18 23:58:37 +0000148 if (isNil(N->getState()->getSVal(E))) {
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000149 WarnNilArg(N, ME, Arg);
Ted Kremenek276278e2008-03-27 22:05:32 +0000150 return true;
151 }
152
153 return false;
154}
155
Ted Kremenekc0414922008-03-27 07:25:52 +0000156//===----------------------------------------------------------------------===//
157// NSString checking.
158//===----------------------------------------------------------------------===//
159
Steve Naroff7cae42b2009-07-10 23:34:53 +0000160bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T,
161 const char* suffix) {
Ted Kremenekc0414922008-03-27 07:25:52 +0000162 return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
163}
164
165bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N,
166 ObjCMessageExpr* ME) {
167
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 Lattnere4b95692008-11-24 03:33:13 +0000176 std::string name = S.getAsString();
Ted Kremenek2e4e7cc2008-03-27 21:23:57 +0000177 assert (!name.empty());
178 const char* cstr = &name[0];
179 unsigned len = name.size();
Ted Kremenek276278e2008-03-27 22:05:32 +0000180
Ted Kremenek2e4e7cc2008-03-27 21:23:57 +0000181 switch (len) {
182 default:
183 break;
Ted Kremenekc7194242008-03-28 16:09:38 +0000184 case 8:
Ted Kremenek276278e2008-03-27 22:05:32 +0000185 if (!strcmp(cstr, "compare:"))
186 return CheckNilArg(N, 0);
187
188 break;
Ted Kremenekc7194242008-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 Kremenekc0414922008-03-27 07:25:52 +0000199
Ted Kremenek276278e2008-03-27 22:05:32 +0000200 case 16:
201 if (!strcmp(cstr, "compare:options:"))
202 return CheckNilArg(N, 0);
Ted Kremenek2e4e7cc2008-03-27 21:23:57 +0000203
204 break;
Ted Kremenek276278e2008-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 Kremenekc7194242008-03-28 16:09:38 +0000218
Ted Kremenek276278e2008-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 Kremenekc0414922008-03-27 07:25:52 +0000230 }
231
232 return false;
233}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000234
235//===----------------------------------------------------------------------===//
236// Error reporting.
237//===----------------------------------------------------------------------===//
238
239namespace {
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000240
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000241class VISIBILITY_HIDDEN AuditCFNumberCreate : public GRSimpleAPICheck {
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000242 APIMisuse* BT;
Ted Kremenekcf1ab192008-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 Kremenekfc5d0672009-02-04 23:49:09 +0000249 BugReporter& BR;
Ted Kremenek095f1a92009-06-18 23:58:37 +0000250
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000251public:
Ted Kremenek095f1a92009-06-18 23:58:37 +0000252 AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
253 : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000254
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000255 ~AuditCFNumberCreate() {}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000256
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000257 bool Audit(ExplodedNode<GRState>* N, GRStateManager&);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000258
259private:
Ted Kremenek8b103c62008-10-17 20:28:54 +0000260 void AddError(const TypedRegion* R, Expr* Ex, ExplodedNode<GRState> *N,
Ted Kremenekcf1ab192008-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
Ted Kremenek5ab5a1b2008-08-13 04:27:00 +0000358bool AuditCFNumberCreate::Audit(ExplodedNode<GRState>* N,GRStateManager&){
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000359 CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
360 Expr* Callee = CE->getCallee();
Ted Kremenek095f1a92009-06-18 23:58:37 +0000361 SVal CallV = N->getState()->getSVal(Callee);
Zhongxing Xuac129432009-04-20 05:24:46 +0000362 const FunctionDecl* FD = CallV.getAsFunctionDecl();
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000363
Zhongxing Xuac129432009-04-20 05:24:46 +0000364 if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000365 return false;
366
367 // Get the value of the "theType" argument.
Ted Kremenek095f1a92009-06-18 23:58:37 +0000368 SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000369
370 // FIXME: We really should allow ranges of valid theType values, and
371 // bifurcate the state appropriately.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000372 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000373
374 if (!V)
375 return false;
376
377 uint64_t NumberKind = V->getValue().getLimitedValue();
378 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
379
380 // FIXME: In some cases we can emit an error.
381 if (!TargetSize.isKnown())
382 return false;
383
384 // Look at the value of the integer being passed by reference. Essentially
385 // we want to catch cases where the value passed in is not equal to the
386 // size of the type being created.
Ted Kremenek095f1a92009-06-18 23:58:37 +0000387 SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000388
389 // FIXME: Eventually we should handle arbitrary locations. We can do this
390 // by having an enhanced memory model that does low-level typing.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000391 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000392
393 if (!LV)
394 return false;
395
Ted Kremenek8b103c62008-10-17 20:28:54 +0000396 const TypedRegion* R = dyn_cast<TypedRegion>(LV->getRegion());
Ted Kremenekb5670fd2008-12-13 21:49:13 +0000397 if (!R) return false;
Ted Kremenek5ca90a22008-10-04 05:50:14 +0000398
Ted Kremenek07e95682009-03-01 05:44:08 +0000399 while (const TypedViewRegion* ATR = dyn_cast<TypedViewRegion>(R)) {
Ted Kremenekb5670fd2008-12-13 21:49:13 +0000400 R = dyn_cast<TypedRegion>(ATR->getSuperRegion());
401 if (!R) return false;
402 }
Ted Kremenek5ca90a22008-10-04 05:50:14 +0000403
Zhongxing Xu34d04b32009-05-09 03:57:34 +0000404 QualType T = Ctx.getCanonicalType(R->getValueType(Ctx));
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000405
406 // FIXME: If the pointee isn't an integer type, should we flag a warning?
407 // People can do weird stuff with pointers.
408
409 if (!T->isIntegerType())
410 return false;
411
412 uint64_t SourceSize = Ctx.getTypeSize(T);
413
414 // CHECK: is SourceSize == TargetSize
415
416 if (SourceSize == TargetSize)
417 return false;
418
Ted Kremenek5ca90a22008-10-04 05:50:14 +0000419 AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000420
421 // FIXME: We can actually create an abstract "CFNumber" object that has
422 // the bits initialized to the provided values.
423 return SourceSize < TargetSize;
424}
425
Ted Kremenek8b103c62008-10-17 20:28:54 +0000426void AuditCFNumberCreate::AddError(const TypedRegion* R, Expr* Ex,
Ted Kremenek5ab5a1b2008-08-13 04:27:00 +0000427 ExplodedNode<GRState> *N,
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000428 uint64_t SourceSize, uint64_t TargetSize,
429 uint64_t NumberKind) {
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000430
431 std::string sbuf;
432 llvm::raw_string_ostream os(sbuf);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000433
434 os << (SourceSize == 8 ? "An " : "A ")
435 << SourceSize << " bit integer is used to initialize a CFNumber "
436 "object that represents "
437 << (TargetSize == 8 ? "an " : "a ")
438 << TargetSize << " bit integer. ";
439
440 if (SourceSize < TargetSize)
441 os << (TargetSize - SourceSize)
442 << " bits of the CFNumber value will be garbage." ;
443 else
444 os << (SourceSize - TargetSize)
445 << " bits of the input integer will be lost.";
446
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000447 // Lazily create the BugType object. This will be owned
448 // by the BugReporter object 'BR' once we call BR.EmitWarning.
449 if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate");
450 RangedBugReport *report = new RangedBugReport(*BT, os.str().c_str(), N);
451 report->addRange(Ex->getSourceRange());
452 BR.EmitReport(report);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000453}
454
455GRSimpleAPICheck*
Ted Kremenek095f1a92009-06-18 23:58:37 +0000456clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
457 return new AuditCFNumberCreate(Ctx, BR);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000458}
459
Ted Kremenek1f352db2008-07-22 16:21:24 +0000460//===----------------------------------------------------------------------===//
Ted Kremenekc057f412009-07-14 00:43:42 +0000461// CFRetain/CFRelease auditing for null arguments.
462//===----------------------------------------------------------------------===//
463
464namespace {
465class VISIBILITY_HIDDEN AuditCFRetainRelease : public GRSimpleAPICheck {
466 APIMisuse *BT;
467
468 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
469 // it should always be passed with a call to Audit. The latter
470 // approach makes this class more stateless.
471 ASTContext& Ctx;
472 IdentifierInfo *Retain, *Release;
473 BugReporter& BR;
474
475public:
476 AuditCFRetainRelease(ASTContext& ctx, BugReporter& br)
477 : BT(0), Ctx(ctx),
478 Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")),
479 BR(br){}
480
481 ~AuditCFRetainRelease() {}
482
483 bool Audit(ExplodedNode<GRState>* N, GRStateManager&);
484};
485} // end anonymous namespace
486
487
488bool AuditCFRetainRelease::Audit(ExplodedNode<GRState>* N, GRStateManager&) {
489 CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
490
491 // If the CallExpr doesn't have exactly 1 argument just give up checking.
492 if (CE->getNumArgs() != 1)
493 return false;
494
495 // Check if we called CFRetain/CFRelease.
496 const GRState* state = N->getState();
497 SVal X = state->getSVal(CE->getCallee());
498 const FunctionDecl* FD = X.getAsFunctionDecl();
499
500 if (!FD)
501 return false;
502
503 const IdentifierInfo *FuncII = FD->getIdentifier();
504 if (!(FuncII == Retain || FuncII == Release))
505 return false;
506
507 // Finally, check if the argument is NULL.
508 // FIXME: We should be able to bifurcate the state here, as a successful
509 // check will result in the value not being NULL afterwards.
510 // FIXME: Need a way to register vistors for the BugReporter. Would like
511 // to benefit from the same diagnostics that regular null dereference
512 // reporting has.
513 if (state->getStateManager().isEqual(state, CE->getArg(0), 0)) {
514 if (!BT)
515 BT = new APIMisuse("null passed to CFRetain/CFRelease");
516
517 const char *description = (FuncII == Retain)
518 ? "Null pointer argument in call to CFRetain"
519 : "Null pointer argument in call to CFRelease";
520
521 RangedBugReport *report = new RangedBugReport(*BT, description, N);
522 report->addRange(CE->getArg(0)->getSourceRange());
523 BR.EmitReport(report);
524 return true;
525 }
526
527 return false;
528}
529
530
531GRSimpleAPICheck*
532clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) {
533 return new AuditCFRetainRelease(Ctx, BR);
534}
535
536//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000537// Check registration.
Ted Kremenekc057f412009-07-14 00:43:42 +0000538//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000539
540void clang::RegisterAppleChecks(GRExprEngine& Eng) {
541 ASTContext& Ctx = Eng.getContext();
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000542 BugReporter &BR = Eng.getBugReporter();
Ted Kremenek1f352db2008-07-22 16:21:24 +0000543
Ted Kremenek095f1a92009-06-18 23:58:37 +0000544 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
Ted Kremenek1f352db2008-07-22 16:21:24 +0000545 Stmt::ObjCMessageExprClass);
Ted Kremenekc057f412009-07-14 00:43:42 +0000546 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
547 Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass);
Ted Kremenekf0673e42008-09-18 21:25:13 +0000548
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000549 RegisterNSErrorChecks(BR, Eng);
Ted Kremenek1f352db2008-07-22 16:21:24 +0000550}