blob: af300f36fa7219ed7532530315e348bcba548651 [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
Ted Kremenek52755612008-03-27 17:17:22 +000016#include "BasicObjCFoundationChecks.h"
17
Ted Kremenek99c6ad32008-03-27 07:25:52 +000018#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
19#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h"
Ted Kremenek78d46242008-07-22 16:21:24 +000020#include "clang/Analysis/PathSensitive/GRExprEngine.h"
Ted Kremenek4adc81e2008-08-13 04:27:00 +000021#include "clang/Analysis/PathSensitive/GRState.h"
Ted Kremenekf1ae7052008-04-03 17:57:38 +000022#include "clang/Analysis/PathSensitive/BugReporter.h"
Ted Kremenek9e240492008-10-04 05:50:14 +000023#include "clang/Analysis/PathSensitive/MemRegion.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000024#include "clang/Analysis/PathDiagnostic.h"
Ted Kremenek78d46242008-07-22 16:21:24 +000025#include "clang/Analysis/LocalCheckers.h"
Daniel Dunbarc4a1dea2008-08-11 05:35:13 +000026#include "clang/AST/DeclObjC.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000027#include "clang/AST/Expr.h"
Steve Narofff494b572008-05-29 21:12:08 +000028#include "clang/AST/ExprObjC.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000029#include "clang/AST/ASTContext.h"
30#include "llvm/Support/Compiler.h"
31
Ted Kremenek99c6ad32008-03-27 07:25:52 +000032using namespace clang;
Ted Kremenek52755612008-03-27 17:17:22 +000033
Steve Naroff14108da2009-07-10 23:34:53 +000034static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) {
35 const Expr* Receiver = ME->getReceiver();
Mike Stump1eb44332009-09-09 15:08:12 +000036
Ted Kremenek4ba62832008-03-27 22:05:32 +000037 if (!Receiver)
38 return NULL;
Mike Stump1eb44332009-09-09 15:08:12 +000039
Steve Naroff14108da2009-07-10 23:34:53 +000040 if (const ObjCObjectPointerType *PT =
John McCall183700f2009-09-21 23:43:11 +000041 Receiver->getType()->getAs<ObjCObjectPointerType>())
Steve Naroff14108da2009-07-10 23:34:53 +000042 return PT->getInterfaceType();
Ted Kremenekc1ff3cd2008-04-30 22:48:21 +000043
Ted Kremenekc1ff3cd2008-04-30 22:48:21 +000044 return NULL;
Ted Kremenek4ba62832008-03-27 22:05:32 +000045}
46
Steve Naroff14108da2009-07-10 23:34:53 +000047static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
48 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
Ted Kremenek4ba62832008-03-27 22:05:32 +000049 return ReceiverType ? ReceiverType->getDecl()->getIdentifier()->getName()
50 : NULL;
51}
Ted Kremenek52755612008-03-27 17:17:22 +000052
Ted Kremenekf1ae7052008-04-03 17:57:38 +000053namespace {
Ted Kremenekb344f912008-09-21 19:01:39 +000054
Ted Kremenekcf118d42009-02-04 23:49:09 +000055class VISIBILITY_HIDDEN APIMisuse : public BugType {
Ted Kremenekf1ae7052008-04-03 17:57:38 +000056public:
Ted Kremenekcf118d42009-02-04 23:49:09 +000057 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
Ted Kremenekf1ae7052008-04-03 17:57:38 +000058};
Mike Stump1eb44332009-09-09 15:08:12 +000059
Ted Kremenekf1ae7052008-04-03 17:57:38 +000060class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenekcf118d42009-02-04 23:49:09 +000061 APIMisuse *BT;
62 BugReporter& BR;
Ted Kremenekf1ae7052008-04-03 17:57:38 +000063 ASTContext &Ctx;
Mike Stump1eb44332009-09-09 15:08:12 +000064
Steve Naroff14108da2009-07-10 23:34:53 +000065 bool isNSString(const ObjCInterfaceType *T, const char* suffix);
Zhongxing Xu031ccc02009-08-06 12:48:26 +000066 bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME);
Mike Stump1eb44332009-09-09 15:08:12 +000067
68 void Warn(ExplodedNode* N, const Expr* E, const std::string& s);
Zhongxing Xu031ccc02009-08-06 12:48:26 +000069 void WarnNilArg(ExplodedNode* N, const Expr* E);
Mike Stump1eb44332009-09-09 15:08:12 +000070
Zhongxing Xu031ccc02009-08-06 12:48:26 +000071 bool CheckNilArg(ExplodedNode* N, unsigned Arg);
Ted Kremenekf1ae7052008-04-03 17:57:38 +000072
73public:
Mike Stump1eb44332009-09-09 15:08:12 +000074 BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br)
Ted Kremenek23ec48c2009-06-18 23:58:37 +000075 : BT(0), BR(br), Ctx(ctx) {}
Mike Stump1eb44332009-09-09 15:08:12 +000076
Zhongxing Xuc5619d92009-08-06 01:32:16 +000077 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump1eb44332009-09-09 15:08:12 +000078
79private:
80 void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekcf118d42009-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.";
Mike Stump1eb44332009-09-09 15:08:12 +000085
Ted Kremenekcf118d42009-02-04 23:49:09 +000086 // 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");
Mike Stump1eb44332009-09-09 15:08:12 +000089
Ted Kremenekcf118d42009-02-04 23:49:09 +000090 RangedBugReport *R = new RangedBugReport(*BT, os.str().c_str(), N);
91 R->addRange(ME->getArg(Arg)->getSourceRange());
92 BR.EmitReport(R);
Ted Kremenekf1ae7052008-04-03 17:57:38 +000093 }
94};
Mike Stump1eb44332009-09-09 15:08:12 +000095
Ted Kremenekf1ae7052008-04-03 17:57:38 +000096} // end anonymous namespace
97
98
99GRSimpleAPICheck*
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000100clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) {
Mike Stump1eb44332009-09-09 15:08:12 +0000101 return new BasicObjCFoundationChecks(Ctx, BR);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000102}
103
104
105
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000106bool BasicObjCFoundationChecks::Audit(ExplodedNode* N,
Ted Kremenek4adc81e2008-08-13 04:27:00 +0000107 GRStateManager&) {
Mike Stump1eb44332009-09-09 15:08:12 +0000108
Ted Kremenek5f85e172009-07-22 22:35:28 +0000109 const ObjCMessageExpr* ME =
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000110 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000111
Steve Naroff14108da2009-07-10 23:34:53 +0000112 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
Mike Stump1eb44332009-09-09 15:08:12 +0000113
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000114 if (!ReceiverType)
Nuno Lopesf7427942008-05-20 17:33:56 +0000115 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000116
Ted Kremenek4ba62832008-03-27 22:05:32 +0000117 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
Mike Stump1eb44332009-09-09 15:08:12 +0000118
Ted Kremenek4ba62832008-03-27 22:05:32 +0000119 if (!name)
120 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000121
122 if (name[0] != 'N' || name[1] != 'S')
123 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000124
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000125 name += 2;
Mike Stump1eb44332009-09-09 15:08:12 +0000126
127 // FIXME: Make all of this faster.
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000128 if (isNSString(ReceiverType, name))
129 return AuditNSString(N, ME);
130
Nuno Lopesf7427942008-05-20 17:33:56 +0000131 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000132}
133
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000134static inline bool isNil(SVal X) {
Mike Stump1eb44332009-09-09 15:08:12 +0000135 return isa<loc::ConcreteInt>(X);
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000136}
137
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000138//===----------------------------------------------------------------------===//
139// Error reporting.
140//===----------------------------------------------------------------------===//
141
Zhongxing Xu031ccc02009-08-06 12:48:26 +0000142bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) {
Ted Kremenek5f85e172009-07-22 22:35:28 +0000143 const ObjCMessageExpr* ME =
Ted Kremenek4ba62832008-03-27 22:05:32 +0000144 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000145
Ted Kremenek5f85e172009-07-22 22:35:28 +0000146 const Expr * E = ME->getArg(Arg);
Mike Stump1eb44332009-09-09 15:08:12 +0000147
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000148 if (isNil(N->getState()->getSVal(E))) {
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000149 WarnNilArg(N, ME, Arg);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000150 return true;
151 }
Mike Stump1eb44332009-09-09 15:08:12 +0000152
Ted Kremenek4ba62832008-03-27 22:05:32 +0000153 return false;
154}
155
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000156//===----------------------------------------------------------------------===//
157// NSString checking.
158//===----------------------------------------------------------------------===//
159
Steve Naroff14108da2009-07-10 23:34:53 +0000160bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T,
Mike Stump1eb44332009-09-09 15:08:12 +0000161 const char* suffix) {
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000162 return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
163}
164
Mike Stump1eb44332009-09-09 15:08:12 +0000165bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N,
Ted Kremenek5f85e172009-07-22 22:35:28 +0000166 const ObjCMessageExpr* ME) {
Mike Stump1eb44332009-09-09 15:08:12 +0000167
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000168 Selector S = ME->getSelector();
Mike Stump1eb44332009-09-09 15:08:12 +0000169
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000170 if (S.isUnarySelector())
171 return false;
172
173 // FIXME: This is going to be really slow doing these checks with
174 // lexical comparisons.
Mike Stump1eb44332009-09-09 15:08:12 +0000175
Chris Lattner077bf5e2008-11-24 03:33:13 +0000176 std::string name = S.getAsString();
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000177 assert (!name.empty());
178 const char* cstr = &name[0];
179 unsigned len = name.size();
Mike Stump1eb44332009-09-09 15:08:12 +0000180
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000181 switch (len) {
182 default:
183 break;
Mike Stump1eb44332009-09-09 15:08:12 +0000184 case 8:
Ted Kremenek4ba62832008-03-27 22:05:32 +0000185 if (!strcmp(cstr, "compare:"))
186 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000187
Ted Kremenek4ba62832008-03-27 22:05:32 +0000188 break;
Mike Stump1eb44332009-09-09 15:08:12 +0000189
Ted Kremenek8730e132008-03-28 16:09:38 +0000190 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);
Mike Stump1eb44332009-09-09 15:08:12 +0000197
Ted Kremenek8730e132008-03-28 16:09:38 +0000198 break;
Mike Stump1eb44332009-09-09 15:08:12 +0000199
Ted Kremenek4ba62832008-03-27 22:05:32 +0000200 case 16:
201 if (!strcmp(cstr, "compare:options:"))
202 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000203
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000204 break;
Mike Stump1eb44332009-09-09 15:08:12 +0000205
Ted Kremenek4ba62832008-03-27 22:05:32 +0000206 case 22:
207 if (!strcmp(cstr, "compare:options:range:"))
208 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000209
Ted Kremenek4ba62832008-03-27 22:05:32 +0000210 break;
Mike Stump1eb44332009-09-09 15:08:12 +0000211
Ted Kremenek4ba62832008-03-27 22:05:32 +0000212 case 23:
Mike Stump1eb44332009-09-09 15:08:12 +0000213
Ted Kremenek4ba62832008-03-27 22:05:32 +0000214 if (!strcmp(cstr, "caseInsensitiveCompare:"))
215 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000216
Ted Kremenek4ba62832008-03-27 22:05:32 +0000217 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000218
Ted Kremenek4ba62832008-03-27 22:05:32 +0000219 case 29:
220 if (!strcmp(cstr, "compare:options:range:locale:"))
221 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000222
223 break;
224
Ted Kremenek4ba62832008-03-27 22:05:32 +0000225 case 37:
226 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
227 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000228
229 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000230 }
Mike Stump1eb44332009-09-09 15:08:12 +0000231
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000232 return false;
233}
Ted Kremenek04bc8762008-06-26 23:59:48 +0000234
235//===----------------------------------------------------------------------===//
236// Error reporting.
237//===----------------------------------------------------------------------===//
238
239namespace {
Ted Kremenek04bc8762008-06-26 23:59:48 +0000240
Ted Kremenek04bc8762008-06-26 23:59:48 +0000241class VISIBILITY_HIDDEN AuditCFNumberCreate : public GRSimpleAPICheck {
Ted Kremenekcf118d42009-02-04 23:49:09 +0000242 APIMisuse* BT;
Mike Stump1eb44332009-09-09 15:08:12 +0000243
Ted Kremenek04bc8762008-06-26 23:59:48 +0000244 // 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 Kremenekcf118d42009-02-04 23:49:09 +0000249 BugReporter& BR;
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000250
Ted Kremenek04bc8762008-06-26 23:59:48 +0000251public:
Mike Stump1eb44332009-09-09 15:08:12 +0000252 AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000253 : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
Mike Stump1eb44332009-09-09 15:08:12 +0000254
Ted Kremenekcf118d42009-02-04 23:49:09 +0000255 ~AuditCFNumberCreate() {}
Mike Stump1eb44332009-09-09 15:08:12 +0000256
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000257 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump1eb44332009-09-09 15:08:12 +0000258
Ted Kremenek04bc8762008-06-26 23:59:48 +0000259private:
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000260 void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N,
Mike Stump1eb44332009-09-09 15:08:12 +0000261 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000262};
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) {}
Mike Stump1eb44332009-09-09 15:08:12 +0000292
Ted Kremenek04bc8762008-06-26 23:59:48 +0000293 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 };
Mike Stump1eb44332009-09-09 15:08:12 +0000308
Ted Kremenek04bc8762008-06-26 23:59:48 +0000309 if (i < kCFNumberCharType)
310 return FixedSize[i-1];
Mike Stump1eb44332009-09-09 15:08:12 +0000311
Ted Kremenek04bc8762008-06-26 23:59:48 +0000312 QualType T;
Mike Stump1eb44332009-09-09 15:08:12 +0000313
Ted Kremenek04bc8762008-06-26 23:59:48 +0000314 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:
Mike Stump1eb44332009-09-09 15:08:12 +0000325 // FIXME: We need a way to map from names to Type*.
Ted Kremenek04bc8762008-06-26 23:59:48 +0000326 default:
327 return Optional<uint64_t>();
328 }
Mike Stump1eb44332009-09-09 15:08:12 +0000329
Ted Kremenek04bc8762008-06-26 23:59:48 +0000330 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 };
Mike Stump1eb44332009-09-09 15:08:12 +0000353
Ted Kremenek04bc8762008-06-26 23:59:48 +0000354 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
355}
356#endif
357
Mike Stump1eb44332009-09-09 15:08:12 +0000358bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
Ted Kremenek5f85e172009-07-22 22:35:28 +0000359 const CallExpr* CE =
360 cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000361 const Expr* Callee = CE->getCallee();
362 SVal CallV = N->getState()->getSVal(Callee);
Zhongxing Xu369f4472009-04-20 05:24:46 +0000363 const FunctionDecl* FD = CallV.getAsFunctionDecl();
Ted Kremenek04bc8762008-06-26 23:59:48 +0000364
Zhongxing Xu369f4472009-04-20 05:24:46 +0000365 if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
Ted Kremenek04bc8762008-06-26 23:59:48 +0000366 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000367
Ted Kremenek04bc8762008-06-26 23:59:48 +0000368 // Get the value of the "theType" argument.
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000369 SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
Mike Stump1eb44332009-09-09 15:08:12 +0000370
Ted Kremenek04bc8762008-06-26 23:59:48 +0000371 // FIXME: We really should allow ranges of valid theType values, and
372 // bifurcate the state appropriately.
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000373 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Mike Stump1eb44332009-09-09 15:08:12 +0000374
Ted Kremenek04bc8762008-06-26 23:59:48 +0000375 if (!V)
376 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000377
Ted Kremenek04bc8762008-06-26 23:59:48 +0000378 uint64_t NumberKind = V->getValue().getLimitedValue();
379 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000380
Ted Kremenek04bc8762008-06-26 23:59:48 +0000381 // FIXME: In some cases we can emit an error.
382 if (!TargetSize.isKnown())
383 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000384
Ted Kremenek04bc8762008-06-26 23:59:48 +0000385 // 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 Kremenek23ec48c2009-06-18 23:58:37 +0000388 SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
Mike Stump1eb44332009-09-09 15:08:12 +0000389
Ted Kremenek04bc8762008-06-26 23:59:48 +0000390 // FIXME: Eventually we should handle arbitrary locations. We can do this
391 // by having an enhanced memory model that does low-level typing.
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000392 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000393
394 if (!LV)
395 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000396
Ted Kremenek5e77eba2009-07-29 18:17:40 +0000397 const TypedRegion* R = dyn_cast<TypedRegion>(LV->getBaseRegion());
398
399 if (!R)
400 return false;
401
Zhongxing Xua82d8aa2009-05-09 03:57:34 +0000402 QualType T = Ctx.getCanonicalType(R->getValueType(Ctx));
Mike Stump1eb44332009-09-09 15:08:12 +0000403
Ted Kremenek04bc8762008-06-26 23:59:48 +0000404 // FIXME: If the pointee isn't an integer type, should we flag a warning?
405 // People can do weird stuff with pointers.
Mike Stump1eb44332009-09-09 15:08:12 +0000406
407 if (!T->isIntegerType())
Ted Kremenek04bc8762008-06-26 23:59:48 +0000408 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000409
Ted Kremenek04bc8762008-06-26 23:59:48 +0000410 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump1eb44332009-09-09 15:08:12 +0000411
Ted Kremenek04bc8762008-06-26 23:59:48 +0000412 // CHECK: is SourceSize == TargetSize
Mike Stump1eb44332009-09-09 15:08:12 +0000413
Ted Kremenek04bc8762008-06-26 23:59:48 +0000414 if (SourceSize == TargetSize)
415 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000416
Ted Kremenek9e240492008-10-04 05:50:14 +0000417 AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000418
Ted Kremenek04bc8762008-06-26 23:59:48 +0000419 // 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 Kremenek5f85e172009-07-22 22:35:28 +0000424void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex,
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000425 ExplodedNode *N,
Ted Kremenek04bc8762008-06-26 23:59:48 +0000426 uint64_t SourceSize, uint64_t TargetSize,
427 uint64_t NumberKind) {
Mike Stump1eb44332009-09-09 15:08:12 +0000428
Ted Kremenekcf118d42009-02-04 23:49:09 +0000429 std::string sbuf;
430 llvm::raw_string_ostream os(sbuf);
Mike Stump1eb44332009-09-09 15:08:12 +0000431
Ted Kremenek04bc8762008-06-26 23:59:48 +0000432 os << (SourceSize == 8 ? "An " : "A ")
433 << SourceSize << " bit integer is used to initialize a CFNumber "
434 "object that represents "
435 << (TargetSize == 8 ? "an " : "a ")
Mike Stump1eb44332009-09-09 15:08:12 +0000436 << TargetSize << " bit integer. ";
Ted Kremenek04bc8762008-06-26 23:59:48 +0000437
438 if (SourceSize < TargetSize)
439 os << (TargetSize - SourceSize)
Mike Stump1eb44332009-09-09 15:08:12 +0000440 << " bits of the CFNumber value will be garbage." ;
Ted Kremenek04bc8762008-06-26 23:59:48 +0000441 else
442 os << (SourceSize - TargetSize)
443 << " bits of the input integer will be lost.";
Mike Stump1eb44332009-09-09 15:08:12 +0000444
Ted Kremenekcf118d42009-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 Kremenek04bc8762008-06-26 23:59:48 +0000451}
452
453GRSimpleAPICheck*
Mike Stump1eb44332009-09-09 15:08:12 +0000454clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000455 return new AuditCFNumberCreate(Ctx, BR);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000456}
457
Ted Kremenek78d46242008-07-22 16:21:24 +0000458//===----------------------------------------------------------------------===//
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000459// CFRetain/CFRelease auditing for null arguments.
460//===----------------------------------------------------------------------===//
461
462namespace {
463class VISIBILITY_HIDDEN AuditCFRetainRelease : public GRSimpleAPICheck {
464 APIMisuse *BT;
Mike Stump1eb44332009-09-09 15:08:12 +0000465
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000466 // 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;
Mike Stump1eb44332009-09-09 15:08:12 +0000472
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000473public:
Mike Stump1eb44332009-09-09 15:08:12 +0000474 AuditCFRetainRelease(ASTContext& ctx, BugReporter& br)
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000475 : BT(0), Ctx(ctx),
476 Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")),
477 BR(br){}
Mike Stump1eb44332009-09-09 15:08:12 +0000478
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000479 ~AuditCFRetainRelease() {}
Mike Stump1eb44332009-09-09 15:08:12 +0000480
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000481 bool Audit(ExplodedNode* N, GRStateManager&);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000482};
483} // end anonymous namespace
484
485
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000486bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) {
Ted Kremenek5f85e172009-07-22 22:35:28 +0000487 const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000488
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000489 // If the CallExpr doesn't have exactly 1 argument just give up checking.
490 if (CE->getNumArgs() != 1)
491 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000492
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000493 // 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();
Mike Stump1eb44332009-09-09 15:08:12 +0000497
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000498 if (!FD)
499 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000500
501 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000502 if (!(FuncII == Retain || FuncII == Release))
503 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000504
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000505 // 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");
Mike Stump1eb44332009-09-09 15:08:12 +0000514
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000515 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}
Mike Stump1eb44332009-09-09 15:08:12 +0000527
528
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000529GRSimpleAPICheck*
Mike Stump1eb44332009-09-09 15:08:12 +0000530clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000531 return new AuditCFRetainRelease(Ctx, BR);
532}
533
534//===----------------------------------------------------------------------===//
Ted Kremenek78d46242008-07-22 16:21:24 +0000535// Check registration.
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000536//===----------------------------------------------------------------------===//
Ted Kremenek78d46242008-07-22 16:21:24 +0000537
Zhongxing Xu5ab128b2009-08-21 02:18:44 +0000538void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) {
Ted Kremenek78d46242008-07-22 16:21:24 +0000539 ASTContext& Ctx = Eng.getContext();
Ted Kremenekcf118d42009-02-04 23:49:09 +0000540 BugReporter &BR = Eng.getBugReporter();
Ted Kremenek78d46242008-07-22 16:21:24 +0000541
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000542 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
Ted Kremenek78d46242008-07-22 16:21:24 +0000543 Stmt::ObjCMessageExprClass);
Mike Stump1eb44332009-09-09 15:08:12 +0000544 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000545 Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass);
Mike Stump1eb44332009-09-09 15:08:12 +0000546
Zhongxing Xu5ab128b2009-08-21 02:18:44 +0000547 RegisterNSErrorChecks(BR, Eng, D);
Ted Kremenek78d46242008-07-22 16:21:24 +0000548}