blob: c913779864961e9a97e0727143e3ccb4e8b9acf8 [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 Kremenek50e837b2009-11-20 05:27:05 +000025#include "clang/Analysis/PathSensitive/CheckerVisitor.h"
Ted Kremenek78d46242008-07-22 16:21:24 +000026#include "clang/Analysis/LocalCheckers.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 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) {
Daniel Dunbare013d682009-10-18 20:26:12 +000048 if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME))
49 return ReceiverType->getDecl()->getIdentifier()->getNameStart();
50 return NULL;
Ted Kremenek4ba62832008-03-27 22:05:32 +000051}
Ted Kremenek52755612008-03-27 17:17:22 +000052
Ted Kremenekf1ae7052008-04-03 17:57:38 +000053namespace {
Ted Kremenekb344f912008-09-21 19:01:39 +000054
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +000055class 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
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +000060class 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
Daniel Dunbard777d582009-10-17 18:12:21 +000065 bool isNSString(const ObjCInterfaceType *T, llvm::StringRef 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
Benjamin Kramer4988a9a2009-11-29 18:03:28 +000090 RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
Ted Kremenekcf118d42009-02-04 23:49:09 +000091 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
Daniel Dunbard777d582009-10-17 18:12:21 +0000117 if (isNSString(ReceiverType,
Daniel Dunbar01eb9b92009-10-18 21:17:35 +0000118 ReceiverType->getDecl()->getIdentifier()->getName()))
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000119 return AuditNSString(N, ME);
120
Nuno Lopesf7427942008-05-20 17:33:56 +0000121 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000122}
123
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000124static inline bool isNil(SVal X) {
Mike Stump1eb44332009-09-09 15:08:12 +0000125 return isa<loc::ConcreteInt>(X);
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000126}
127
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000128//===----------------------------------------------------------------------===//
129// Error reporting.
130//===----------------------------------------------------------------------===//
131
Zhongxing Xu031ccc02009-08-06 12:48:26 +0000132bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) {
Ted Kremenek5f85e172009-07-22 22:35:28 +0000133 const ObjCMessageExpr* ME =
Ted Kremenek4ba62832008-03-27 22:05:32 +0000134 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000135
Ted Kremenek5f85e172009-07-22 22:35:28 +0000136 const Expr * E = ME->getArg(Arg);
Mike Stump1eb44332009-09-09 15:08:12 +0000137
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000138 if (isNil(N->getState()->getSVal(E))) {
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000139 WarnNilArg(N, ME, Arg);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000140 return true;
141 }
Mike Stump1eb44332009-09-09 15:08:12 +0000142
Ted Kremenek4ba62832008-03-27 22:05:32 +0000143 return false;
144}
145
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000146//===----------------------------------------------------------------------===//
147// NSString checking.
148//===----------------------------------------------------------------------===//
149
Steve Naroff14108da2009-07-10 23:34:53 +0000150bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T,
Daniel Dunbard777d582009-10-17 18:12:21 +0000151 llvm::StringRef ClassName) {
152 return ClassName == "NSString" || ClassName == "NSMutableString";
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000153}
154
Mike Stump1eb44332009-09-09 15:08:12 +0000155bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N,
Ted Kremenek5f85e172009-07-22 22:35:28 +0000156 const ObjCMessageExpr* ME) {
Mike Stump1eb44332009-09-09 15:08:12 +0000157
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000158 Selector S = ME->getSelector();
Mike Stump1eb44332009-09-09 15:08:12 +0000159
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000160 if (S.isUnarySelector())
161 return false;
162
163 // FIXME: This is going to be really slow doing these checks with
164 // lexical comparisons.
Mike Stump1eb44332009-09-09 15:08:12 +0000165
Chris Lattner077bf5e2008-11-24 03:33:13 +0000166 std::string name = S.getAsString();
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000167 assert (!name.empty());
168 const char* cstr = &name[0];
169 unsigned len = name.size();
Mike Stump1eb44332009-09-09 15:08:12 +0000170
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000171 switch (len) {
172 default:
173 break;
Mike Stump1eb44332009-09-09 15:08:12 +0000174 case 8:
Ted Kremenek4ba62832008-03-27 22:05:32 +0000175 if (!strcmp(cstr, "compare:"))
176 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000177
Ted Kremenek4ba62832008-03-27 22:05:32 +0000178 break;
Mike Stump1eb44332009-09-09 15:08:12 +0000179
Ted Kremenek8730e132008-03-28 16:09:38 +0000180 case 15:
181 // FIXME: Checking for initWithFormat: will not work in most cases
182 // yet because [NSString alloc] returns id, not NSString*. We will
183 // need support for tracking expected-type information in the analyzer
184 // to find these errors.
185 if (!strcmp(cstr, "initWithFormat:"))
186 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000187
Ted Kremenek8730e132008-03-28 16:09:38 +0000188 break;
Mike Stump1eb44332009-09-09 15:08:12 +0000189
Ted Kremenek4ba62832008-03-27 22:05:32 +0000190 case 16:
191 if (!strcmp(cstr, "compare:options:"))
192 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000193
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000194 break;
Mike Stump1eb44332009-09-09 15:08:12 +0000195
Ted Kremenek4ba62832008-03-27 22:05:32 +0000196 case 22:
197 if (!strcmp(cstr, "compare:options:range:"))
198 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000199
Ted Kremenek4ba62832008-03-27 22:05:32 +0000200 break;
Mike Stump1eb44332009-09-09 15:08:12 +0000201
Ted Kremenek4ba62832008-03-27 22:05:32 +0000202 case 23:
Mike Stump1eb44332009-09-09 15:08:12 +0000203
Ted Kremenek4ba62832008-03-27 22:05:32 +0000204 if (!strcmp(cstr, "caseInsensitiveCompare:"))
205 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000206
Ted Kremenek4ba62832008-03-27 22:05:32 +0000207 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000208
Ted Kremenek4ba62832008-03-27 22:05:32 +0000209 case 29:
210 if (!strcmp(cstr, "compare:options:range:locale:"))
211 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000212
213 break;
214
Ted Kremenek4ba62832008-03-27 22:05:32 +0000215 case 37:
216 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
217 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000218
219 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000220 }
Mike Stump1eb44332009-09-09 15:08:12 +0000221
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000222 return false;
223}
Ted Kremenek04bc8762008-06-26 23:59:48 +0000224
225//===----------------------------------------------------------------------===//
226// Error reporting.
227//===----------------------------------------------------------------------===//
228
229namespace {
Ted Kremenek04bc8762008-06-26 23:59:48 +0000230
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +0000231class AuditCFNumberCreate : public GRSimpleAPICheck {
Ted Kremenekcf118d42009-02-04 23:49:09 +0000232 APIMisuse* BT;
Mike Stump1eb44332009-09-09 15:08:12 +0000233
Ted Kremenek04bc8762008-06-26 23:59:48 +0000234 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
235 // it should always be passed with a call to Audit. The latter
236 // approach makes this class more stateless.
237 ASTContext& Ctx;
238 IdentifierInfo* II;
Ted Kremenekcf118d42009-02-04 23:49:09 +0000239 BugReporter& BR;
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000240
Ted Kremenek04bc8762008-06-26 23:59:48 +0000241public:
Mike Stump1eb44332009-09-09 15:08:12 +0000242 AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000243 : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
Mike Stump1eb44332009-09-09 15:08:12 +0000244
Ted Kremenekcf118d42009-02-04 23:49:09 +0000245 ~AuditCFNumberCreate() {}
Mike Stump1eb44332009-09-09 15:08:12 +0000246
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000247 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump1eb44332009-09-09 15:08:12 +0000248
Ted Kremenek04bc8762008-06-26 23:59:48 +0000249private:
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000250 void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N,
Mike Stump1eb44332009-09-09 15:08:12 +0000251 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000252};
253} // end anonymous namespace
254
255enum CFNumberType {
256 kCFNumberSInt8Type = 1,
257 kCFNumberSInt16Type = 2,
258 kCFNumberSInt32Type = 3,
259 kCFNumberSInt64Type = 4,
260 kCFNumberFloat32Type = 5,
261 kCFNumberFloat64Type = 6,
262 kCFNumberCharType = 7,
263 kCFNumberShortType = 8,
264 kCFNumberIntType = 9,
265 kCFNumberLongType = 10,
266 kCFNumberLongLongType = 11,
267 kCFNumberFloatType = 12,
268 kCFNumberDoubleType = 13,
269 kCFNumberCFIndexType = 14,
270 kCFNumberNSIntegerType = 15,
271 kCFNumberCGFloatType = 16
272};
273
274namespace {
275 template<typename T>
276 class Optional {
277 bool IsKnown;
278 T Val;
279 public:
280 Optional() : IsKnown(false), Val(0) {}
281 Optional(const T& val) : IsKnown(true), Val(val) {}
Mike Stump1eb44332009-09-09 15:08:12 +0000282
Ted Kremenek04bc8762008-06-26 23:59:48 +0000283 bool isKnown() const { return IsKnown; }
284
285 const T& getValue() const {
286 assert (isKnown());
287 return Val;
288 }
289
290 operator const T&() const {
291 return getValue();
292 }
293 };
294}
295
296static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
297 static unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump1eb44332009-09-09 15:08:12 +0000298
Ted Kremenek04bc8762008-06-26 23:59:48 +0000299 if (i < kCFNumberCharType)
300 return FixedSize[i-1];
Mike Stump1eb44332009-09-09 15:08:12 +0000301
Ted Kremenek04bc8762008-06-26 23:59:48 +0000302 QualType T;
Mike Stump1eb44332009-09-09 15:08:12 +0000303
Ted Kremenek04bc8762008-06-26 23:59:48 +0000304 switch (i) {
305 case kCFNumberCharType: T = Ctx.CharTy; break;
306 case kCFNumberShortType: T = Ctx.ShortTy; break;
307 case kCFNumberIntType: T = Ctx.IntTy; break;
308 case kCFNumberLongType: T = Ctx.LongTy; break;
309 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
310 case kCFNumberFloatType: T = Ctx.FloatTy; break;
311 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
312 case kCFNumberCFIndexType:
313 case kCFNumberNSIntegerType:
314 case kCFNumberCGFloatType:
Mike Stump1eb44332009-09-09 15:08:12 +0000315 // FIXME: We need a way to map from names to Type*.
Ted Kremenek04bc8762008-06-26 23:59:48 +0000316 default:
317 return Optional<uint64_t>();
318 }
Mike Stump1eb44332009-09-09 15:08:12 +0000319
Ted Kremenek04bc8762008-06-26 23:59:48 +0000320 return Ctx.getTypeSize(T);
321}
322
323#if 0
324static const char* GetCFNumberTypeStr(uint64_t i) {
325 static const char* Names[] = {
326 "kCFNumberSInt8Type",
327 "kCFNumberSInt16Type",
328 "kCFNumberSInt32Type",
329 "kCFNumberSInt64Type",
330 "kCFNumberFloat32Type",
331 "kCFNumberFloat64Type",
332 "kCFNumberCharType",
333 "kCFNumberShortType",
334 "kCFNumberIntType",
335 "kCFNumberLongType",
336 "kCFNumberLongLongType",
337 "kCFNumberFloatType",
338 "kCFNumberDoubleType",
339 "kCFNumberCFIndexType",
340 "kCFNumberNSIntegerType",
341 "kCFNumberCGFloatType"
342 };
Mike Stump1eb44332009-09-09 15:08:12 +0000343
Ted Kremenek04bc8762008-06-26 23:59:48 +0000344 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
345}
346#endif
347
Mike Stump1eb44332009-09-09 15:08:12 +0000348bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
Ted Kremenek5f85e172009-07-22 22:35:28 +0000349 const CallExpr* CE =
350 cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000351 const Expr* Callee = CE->getCallee();
352 SVal CallV = N->getState()->getSVal(Callee);
Zhongxing Xu369f4472009-04-20 05:24:46 +0000353 const FunctionDecl* FD = CallV.getAsFunctionDecl();
Ted Kremenek04bc8762008-06-26 23:59:48 +0000354
Zhongxing Xu369f4472009-04-20 05:24:46 +0000355 if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
Ted Kremenek04bc8762008-06-26 23:59:48 +0000356 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000357
Ted Kremenek04bc8762008-06-26 23:59:48 +0000358 // Get the value of the "theType" argument.
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000359 SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
Mike Stump1eb44332009-09-09 15:08:12 +0000360
Ted Kremenek04bc8762008-06-26 23:59:48 +0000361 // FIXME: We really should allow ranges of valid theType values, and
362 // bifurcate the state appropriately.
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000363 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Mike Stump1eb44332009-09-09 15:08:12 +0000364
Ted Kremenek04bc8762008-06-26 23:59:48 +0000365 if (!V)
366 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000367
Ted Kremenek04bc8762008-06-26 23:59:48 +0000368 uint64_t NumberKind = V->getValue().getLimitedValue();
369 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000370
Ted Kremenek04bc8762008-06-26 23:59:48 +0000371 // FIXME: In some cases we can emit an error.
372 if (!TargetSize.isKnown())
373 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000374
Ted Kremenek04bc8762008-06-26 23:59:48 +0000375 // Look at the value of the integer being passed by reference. Essentially
376 // we want to catch cases where the value passed in is not equal to the
377 // size of the type being created.
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000378 SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
Mike Stump1eb44332009-09-09 15:08:12 +0000379
Ted Kremenek04bc8762008-06-26 23:59:48 +0000380 // FIXME: Eventually we should handle arbitrary locations. We can do this
381 // by having an enhanced memory model that does low-level typing.
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000382 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000383
384 if (!LV)
385 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000386
Zhongxing Xu479529e2009-11-10 02:17:20 +0000387 const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
Ted Kremenek5e77eba2009-07-29 18:17:40 +0000388
389 if (!R)
390 return false;
391
Zhongxing Xua82d8aa2009-05-09 03:57:34 +0000392 QualType T = Ctx.getCanonicalType(R->getValueType(Ctx));
Mike Stump1eb44332009-09-09 15:08:12 +0000393
Ted Kremenek04bc8762008-06-26 23:59:48 +0000394 // FIXME: If the pointee isn't an integer type, should we flag a warning?
395 // People can do weird stuff with pointers.
Mike Stump1eb44332009-09-09 15:08:12 +0000396
397 if (!T->isIntegerType())
Ted Kremenek04bc8762008-06-26 23:59:48 +0000398 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000399
Ted Kremenek04bc8762008-06-26 23:59:48 +0000400 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump1eb44332009-09-09 15:08:12 +0000401
Ted Kremenek04bc8762008-06-26 23:59:48 +0000402 // CHECK: is SourceSize == TargetSize
Mike Stump1eb44332009-09-09 15:08:12 +0000403
Ted Kremenek04bc8762008-06-26 23:59:48 +0000404 if (SourceSize == TargetSize)
405 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000406
Ted Kremenek9e240492008-10-04 05:50:14 +0000407 AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000408
Ted Kremenek04bc8762008-06-26 23:59:48 +0000409 // FIXME: We can actually create an abstract "CFNumber" object that has
410 // the bits initialized to the provided values.
411 return SourceSize < TargetSize;
412}
413
Ted Kremenek5f85e172009-07-22 22:35:28 +0000414void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex,
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000415 ExplodedNode *N,
Ted Kremenek04bc8762008-06-26 23:59:48 +0000416 uint64_t SourceSize, uint64_t TargetSize,
417 uint64_t NumberKind) {
Mike Stump1eb44332009-09-09 15:08:12 +0000418
Ted Kremenekcf118d42009-02-04 23:49:09 +0000419 std::string sbuf;
420 llvm::raw_string_ostream os(sbuf);
Mike Stump1eb44332009-09-09 15:08:12 +0000421
Ted Kremenek04bc8762008-06-26 23:59:48 +0000422 os << (SourceSize == 8 ? "An " : "A ")
423 << SourceSize << " bit integer is used to initialize a CFNumber "
424 "object that represents "
425 << (TargetSize == 8 ? "an " : "a ")
Mike Stump1eb44332009-09-09 15:08:12 +0000426 << TargetSize << " bit integer. ";
Ted Kremenek04bc8762008-06-26 23:59:48 +0000427
428 if (SourceSize < TargetSize)
429 os << (TargetSize - SourceSize)
Mike Stump1eb44332009-09-09 15:08:12 +0000430 << " bits of the CFNumber value will be garbage." ;
Ted Kremenek04bc8762008-06-26 23:59:48 +0000431 else
432 os << (SourceSize - TargetSize)
433 << " bits of the input integer will be lost.";
Mike Stump1eb44332009-09-09 15:08:12 +0000434
Ted Kremenekcf118d42009-02-04 23:49:09 +0000435 // Lazily create the BugType object. This will be owned
436 // by the BugReporter object 'BR' once we call BR.EmitWarning.
437 if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate");
Benjamin Kramer4988a9a2009-11-29 18:03:28 +0000438 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
Ted Kremenekcf118d42009-02-04 23:49:09 +0000439 report->addRange(Ex->getSourceRange());
440 BR.EmitReport(report);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000441}
442
443GRSimpleAPICheck*
Mike Stump1eb44332009-09-09 15:08:12 +0000444clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000445 return new AuditCFNumberCreate(Ctx, BR);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000446}
447
Ted Kremenek78d46242008-07-22 16:21:24 +0000448//===----------------------------------------------------------------------===//
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000449// CFRetain/CFRelease auditing for null arguments.
450//===----------------------------------------------------------------------===//
451
452namespace {
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +0000453class AuditCFRetainRelease : public GRSimpleAPICheck {
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000454 APIMisuse *BT;
Mike Stump1eb44332009-09-09 15:08:12 +0000455
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000456 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
457 // it should always be passed with a call to Audit. The latter
458 // approach makes this class more stateless.
459 ASTContext& Ctx;
460 IdentifierInfo *Retain, *Release;
461 BugReporter& BR;
Mike Stump1eb44332009-09-09 15:08:12 +0000462
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000463public:
Mike Stump1eb44332009-09-09 15:08:12 +0000464 AuditCFRetainRelease(ASTContext& ctx, BugReporter& br)
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000465 : BT(0), Ctx(ctx),
466 Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")),
467 BR(br){}
Mike Stump1eb44332009-09-09 15:08:12 +0000468
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000469 ~AuditCFRetainRelease() {}
Mike Stump1eb44332009-09-09 15:08:12 +0000470
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000471 bool Audit(ExplodedNode* N, GRStateManager&);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000472};
473} // end anonymous namespace
474
475
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000476bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) {
Ted Kremenek5f85e172009-07-22 22:35:28 +0000477 const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000478
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000479 // If the CallExpr doesn't have exactly 1 argument just give up checking.
480 if (CE->getNumArgs() != 1)
481 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000482
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000483 // Check if we called CFRetain/CFRelease.
484 const GRState* state = N->getState();
485 SVal X = state->getSVal(CE->getCallee());
486 const FunctionDecl* FD = X.getAsFunctionDecl();
Mike Stump1eb44332009-09-09 15:08:12 +0000487
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000488 if (!FD)
489 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000490
491 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000492 if (!(FuncII == Retain || FuncII == Release))
493 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000494
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000495 // Finally, check if the argument is NULL.
496 // FIXME: We should be able to bifurcate the state here, as a successful
497 // check will result in the value not being NULL afterwards.
498 // FIXME: Need a way to register vistors for the BugReporter. Would like
499 // to benefit from the same diagnostics that regular null dereference
500 // reporting has.
501 if (state->getStateManager().isEqual(state, CE->getArg(0), 0)) {
502 if (!BT)
503 BT = new APIMisuse("null passed to CFRetain/CFRelease");
Mike Stump1eb44332009-09-09 15:08:12 +0000504
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000505 const char *description = (FuncII == Retain)
506 ? "Null pointer argument in call to CFRetain"
507 : "Null pointer argument in call to CFRelease";
508
509 RangedBugReport *report = new RangedBugReport(*BT, description, N);
510 report->addRange(CE->getArg(0)->getSourceRange());
511 BR.EmitReport(report);
512 return true;
513 }
514
515 return false;
516}
Mike Stump1eb44332009-09-09 15:08:12 +0000517
518
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000519GRSimpleAPICheck*
Mike Stump1eb44332009-09-09 15:08:12 +0000520clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000521 return new AuditCFRetainRelease(Ctx, BR);
522}
523
524//===----------------------------------------------------------------------===//
Ted Kremenek50e837b2009-11-20 05:27:05 +0000525// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
526//===----------------------------------------------------------------------===//
527
528namespace {
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +0000529class ClassReleaseChecker :
Ted Kremenek50e837b2009-11-20 05:27:05 +0000530 public CheckerVisitor<ClassReleaseChecker> {
531 Selector releaseS;
532 Selector retainS;
533 Selector autoreleaseS;
534 Selector drainS;
535 BugType *BT;
536public:
537 ClassReleaseChecker(ASTContext &Ctx)
538 : releaseS(GetNullarySelector("release", Ctx)),
539 retainS(GetNullarySelector("retain", Ctx)),
540 autoreleaseS(GetNullarySelector("autorelease", Ctx)),
541 drainS(GetNullarySelector("drain", Ctx)),
542 BT(0) {}
543
544 static void *getTag() { static int x = 0; return &x; }
545
546 void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);
547};
548}
549
550void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C,
551 const ObjCMessageExpr *ME) {
552
553 const IdentifierInfo *ClsName = ME->getClassName();
554 if (!ClsName)
555 return;
556
557 Selector S = ME->getSelector();
Benjamin Kramer921ddc42009-11-20 10:03:00 +0000558 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremenek50e837b2009-11-20 05:27:05 +0000559 return;
560
561 if (!BT)
562 BT = new APIMisuse("message incorrectly sent to class instead of class "
563 "instance");
564
Ted Kremenek19d67b52009-11-23 22:22:01 +0000565 ExplodedNode *N = C.GenerateNode();
566
Ted Kremenek50e837b2009-11-20 05:27:05 +0000567 if (!N)
568 return;
569
Ted Kremenek50e837b2009-11-20 05:27:05 +0000570 llvm::SmallString<200> buf;
571 llvm::raw_svector_ostream os(buf);
572
573 os << "The '" << S.getAsString() << "' message should be sent to instances "
574 "of class '" << ClsName->getName()
575 << "' and not the class directly";
576
577 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
578 report->addRange(ME->getSourceRange());
579 C.EmitReport(report);
580}
581
582//===----------------------------------------------------------------------===//
Ted Kremenek78d46242008-07-22 16:21:24 +0000583// Check registration.
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000584//===----------------------------------------------------------------------===//
Ted Kremenek78d46242008-07-22 16:21:24 +0000585
Zhongxing Xu5ab128b2009-08-21 02:18:44 +0000586void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) {
Ted Kremenek78d46242008-07-22 16:21:24 +0000587 ASTContext& Ctx = Eng.getContext();
Ted Kremenekcf118d42009-02-04 23:49:09 +0000588 BugReporter &BR = Eng.getBugReporter();
Ted Kremenek78d46242008-07-22 16:21:24 +0000589
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000590 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
Ted Kremenek78d46242008-07-22 16:21:24 +0000591 Stmt::ObjCMessageExprClass);
Mike Stump1eb44332009-09-09 15:08:12 +0000592 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000593 Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass);
Mike Stump1eb44332009-09-09 15:08:12 +0000594
Zhongxing Xu5ab128b2009-08-21 02:18:44 +0000595 RegisterNSErrorChecks(BR, Eng, D);
Ted Kremenek54cb7cc2009-11-03 08:03:59 +0000596 RegisterNSAutoreleasePoolChecks(Eng);
Ted Kremenek50e837b2009-11-20 05:27:05 +0000597 Eng.registerCheck(new ClassReleaseChecker(Ctx));
Ted Kremenek78d46242008-07-22 16:21:24 +0000598}