blob: 5a88a7af9354cac89cf874907be8a95de78e4654 [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 Kremeneka4f7c182009-11-20 05:27:05 +000025#include "clang/Analysis/PathSensitive/CheckerVisitor.h"
Ted Kremenek1f352db2008-07-22 16:21:24 +000026#include "clang/Analysis/LocalCheckers.h"
Daniel Dunbar6e8aa532008-08-11 05:35:13 +000027#include "clang/AST/DeclObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000028#include "clang/AST/Expr.h"
Steve Naroff021ca182008-05-29 21:12:08 +000029#include "clang/AST/ExprObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000030#include "clang/AST/ASTContext.h"
31#include "llvm/Support/Compiler.h"
32
Ted Kremenekc0414922008-03-27 07:25:52 +000033using namespace clang;
Ted Kremeneka4d60b62008-03-27 17:17:22 +000034
Steve Naroff7cae42b2009-07-10 23:34:53 +000035static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) {
36 const Expr* Receiver = ME->getReceiver();
Mike Stump11289f42009-09-09 15:08:12 +000037
Ted Kremenek276278e2008-03-27 22:05:32 +000038 if (!Receiver)
39 return NULL;
Mike Stump11289f42009-09-09 15:08:12 +000040
Steve Naroff7cae42b2009-07-10 23:34:53 +000041 if (const ObjCObjectPointerType *PT =
John McCall9dd450b2009-09-21 23:43:11 +000042 Receiver->getType()->getAs<ObjCObjectPointerType>())
Steve Naroff7cae42b2009-07-10 23:34:53 +000043 return PT->getInterfaceType();
Ted Kremenekf20e2282008-04-30 22:48:21 +000044
Ted Kremenekf20e2282008-04-30 22:48:21 +000045 return NULL;
Ted Kremenek276278e2008-03-27 22:05:32 +000046}
47
Steve Naroff7cae42b2009-07-10 23:34:53 +000048static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
Daniel Dunbar2c422dc92009-10-18 20:26:12 +000049 if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME))
50 return ReceiverType->getDecl()->getIdentifier()->getNameStart();
51 return NULL;
Ted Kremenek276278e2008-03-27 22:05:32 +000052}
Ted Kremeneka4d60b62008-03-27 17:17:22 +000053
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000054namespace {
Ted Kremenek638e2802008-09-21 19:01:39 +000055
Ted Kremenekfc5d0672009-02-04 23:49:09 +000056class VISIBILITY_HIDDEN APIMisuse : public BugType {
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000057public:
Ted Kremenekfc5d0672009-02-04 23:49:09 +000058 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000059};
Mike Stump11289f42009-09-09 15:08:12 +000060
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000061class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenekfc5d0672009-02-04 23:49:09 +000062 APIMisuse *BT;
63 BugReporter& BR;
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000064 ASTContext &Ctx;
Mike Stump11289f42009-09-09 15:08:12 +000065
Daniel Dunbar403073e2009-10-17 18:12:21 +000066 bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix);
Zhongxing Xu107f7592009-08-06 12:48:26 +000067 bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME);
Mike Stump11289f42009-09-09 15:08:12 +000068
69 void Warn(ExplodedNode* N, const Expr* E, const std::string& s);
Zhongxing Xu107f7592009-08-06 12:48:26 +000070 void WarnNilArg(ExplodedNode* N, const Expr* E);
Mike Stump11289f42009-09-09 15:08:12 +000071
Zhongxing Xu107f7592009-08-06 12:48:26 +000072 bool CheckNilArg(ExplodedNode* N, unsigned Arg);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000073
74public:
Mike Stump11289f42009-09-09 15:08:12 +000075 BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br)
Ted Kremenek095f1a92009-06-18 23:58:37 +000076 : BT(0), BR(br), Ctx(ctx) {}
Mike Stump11289f42009-09-09 15:08:12 +000077
Zhongxing Xu20227f72009-08-06 01:32:16 +000078 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump11289f42009-09-09 15:08:12 +000079
80private:
81 void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekfc5d0672009-02-04 23:49:09 +000082 std::string sbuf;
83 llvm::raw_string_ostream os(sbuf);
84 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
85 << ME->getSelector().getAsString() << "' cannot be nil.";
Mike Stump11289f42009-09-09 15:08:12 +000086
Ted Kremenekfc5d0672009-02-04 23:49:09 +000087 // Lazily create the BugType object for NilArg. This will be owned
88 // by the BugReporter object 'BR' once we call BR.EmitWarning.
89 if (!BT) BT = new APIMisuse("nil argument");
Mike Stump11289f42009-09-09 15:08:12 +000090
Ted Kremenekfc5d0672009-02-04 23:49:09 +000091 RangedBugReport *R = new RangedBugReport(*BT, os.str().c_str(), N);
92 R->addRange(ME->getArg(Arg)->getSourceRange());
93 BR.EmitReport(R);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000094 }
95};
Mike Stump11289f42009-09-09 15:08:12 +000096
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000097} // end anonymous namespace
98
99
100GRSimpleAPICheck*
Ted Kremenek095f1a92009-06-18 23:58:37 +0000101clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) {
Mike Stump11289f42009-09-09 15:08:12 +0000102 return new BasicObjCFoundationChecks(Ctx, BR);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000103}
104
105
106
Zhongxing Xu20227f72009-08-06 01:32:16 +0000107bool BasicObjCFoundationChecks::Audit(ExplodedNode* N,
Ted Kremenek5ab5a1b2008-08-13 04:27:00 +0000108 GRStateManager&) {
Mike Stump11289f42009-09-09 15:08:12 +0000109
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000110 const ObjCMessageExpr* ME =
Ted Kremenekc0414922008-03-27 07:25:52 +0000111 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenekc0414922008-03-27 07:25:52 +0000112
Steve Naroff7cae42b2009-07-10 23:34:53 +0000113 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
Mike Stump11289f42009-09-09 15:08:12 +0000114
Ted Kremenekc0414922008-03-27 07:25:52 +0000115 if (!ReceiverType)
Nuno Lopes652eaab2008-05-20 17:33:56 +0000116 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000117
Daniel Dunbar403073e2009-10-17 18:12:21 +0000118 if (isNSString(ReceiverType,
Daniel Dunbar07d07852009-10-18 21:17:35 +0000119 ReceiverType->getDecl()->getIdentifier()->getName()))
Ted Kremenekc0414922008-03-27 07:25:52 +0000120 return AuditNSString(N, ME);
121
Nuno Lopes652eaab2008-05-20 17:33:56 +0000122 return false;
Ted Kremenekc0414922008-03-27 07:25:52 +0000123}
124
Zhongxing Xu27f17422008-10-17 05:57:07 +0000125static inline bool isNil(SVal X) {
Mike Stump11289f42009-09-09 15:08:12 +0000126 return isa<loc::ConcreteInt>(X);
Ted Kremenek27156c82008-03-27 21:15:17 +0000127}
128
Ted Kremenekc0414922008-03-27 07:25:52 +0000129//===----------------------------------------------------------------------===//
130// Error reporting.
131//===----------------------------------------------------------------------===//
132
Zhongxing Xu107f7592009-08-06 12:48:26 +0000133bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) {
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000134 const ObjCMessageExpr* ME =
Ted Kremenek276278e2008-03-27 22:05:32 +0000135 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump11289f42009-09-09 15:08:12 +0000136
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000137 const Expr * E = ME->getArg(Arg);
Mike Stump11289f42009-09-09 15:08:12 +0000138
Ted Kremenek095f1a92009-06-18 23:58:37 +0000139 if (isNil(N->getState()->getSVal(E))) {
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000140 WarnNilArg(N, ME, Arg);
Ted Kremenek276278e2008-03-27 22:05:32 +0000141 return true;
142 }
Mike Stump11289f42009-09-09 15:08:12 +0000143
Ted Kremenek276278e2008-03-27 22:05:32 +0000144 return false;
145}
146
Ted Kremenekc0414922008-03-27 07:25:52 +0000147//===----------------------------------------------------------------------===//
148// NSString checking.
149//===----------------------------------------------------------------------===//
150
Steve Naroff7cae42b2009-07-10 23:34:53 +0000151bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T,
Daniel Dunbar403073e2009-10-17 18:12:21 +0000152 llvm::StringRef ClassName) {
153 return ClassName == "NSString" || ClassName == "NSMutableString";
Ted Kremenekc0414922008-03-27 07:25:52 +0000154}
155
Mike Stump11289f42009-09-09 15:08:12 +0000156bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N,
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000157 const ObjCMessageExpr* ME) {
Mike Stump11289f42009-09-09 15:08:12 +0000158
Ted Kremenekc0414922008-03-27 07:25:52 +0000159 Selector S = ME->getSelector();
Mike Stump11289f42009-09-09 15:08:12 +0000160
Ted Kremenekc0414922008-03-27 07:25:52 +0000161 if (S.isUnarySelector())
162 return false;
163
164 // FIXME: This is going to be really slow doing these checks with
165 // lexical comparisons.
Mike Stump11289f42009-09-09 15:08:12 +0000166
Chris Lattnere4b95692008-11-24 03:33:13 +0000167 std::string name = S.getAsString();
Ted Kremenek2e4e7cc2008-03-27 21:23:57 +0000168 assert (!name.empty());
169 const char* cstr = &name[0];
170 unsigned len = name.size();
Mike Stump11289f42009-09-09 15:08:12 +0000171
Ted Kremenek2e4e7cc2008-03-27 21:23:57 +0000172 switch (len) {
173 default:
174 break;
Mike Stump11289f42009-09-09 15:08:12 +0000175 case 8:
Ted Kremenek276278e2008-03-27 22:05:32 +0000176 if (!strcmp(cstr, "compare:"))
177 return CheckNilArg(N, 0);
Mike Stump11289f42009-09-09 15:08:12 +0000178
Ted Kremenek276278e2008-03-27 22:05:32 +0000179 break;
Mike Stump11289f42009-09-09 15:08:12 +0000180
Ted Kremenekc7194242008-03-28 16:09:38 +0000181 case 15:
182 // FIXME: Checking for initWithFormat: will not work in most cases
183 // yet because [NSString alloc] returns id, not NSString*. We will
184 // need support for tracking expected-type information in the analyzer
185 // to find these errors.
186 if (!strcmp(cstr, "initWithFormat:"))
187 return CheckNilArg(N, 0);
Mike Stump11289f42009-09-09 15:08:12 +0000188
Ted Kremenekc7194242008-03-28 16:09:38 +0000189 break;
Mike Stump11289f42009-09-09 15:08:12 +0000190
Ted Kremenek276278e2008-03-27 22:05:32 +0000191 case 16:
192 if (!strcmp(cstr, "compare:options:"))
193 return CheckNilArg(N, 0);
Mike Stump11289f42009-09-09 15:08:12 +0000194
Ted Kremenek2e4e7cc2008-03-27 21:23:57 +0000195 break;
Mike Stump11289f42009-09-09 15:08:12 +0000196
Ted Kremenek276278e2008-03-27 22:05:32 +0000197 case 22:
198 if (!strcmp(cstr, "compare:options:range:"))
199 return CheckNilArg(N, 0);
Mike Stump11289f42009-09-09 15:08:12 +0000200
Ted Kremenek276278e2008-03-27 22:05:32 +0000201 break;
Mike Stump11289f42009-09-09 15:08:12 +0000202
Ted Kremenek276278e2008-03-27 22:05:32 +0000203 case 23:
Mike Stump11289f42009-09-09 15:08:12 +0000204
Ted Kremenek276278e2008-03-27 22:05:32 +0000205 if (!strcmp(cstr, "caseInsensitiveCompare:"))
206 return CheckNilArg(N, 0);
Mike Stump11289f42009-09-09 15:08:12 +0000207
Ted Kremenek276278e2008-03-27 22:05:32 +0000208 break;
Ted Kremenekc7194242008-03-28 16:09:38 +0000209
Ted Kremenek276278e2008-03-27 22:05:32 +0000210 case 29:
211 if (!strcmp(cstr, "compare:options:range:locale:"))
212 return CheckNilArg(N, 0);
Mike Stump11289f42009-09-09 15:08:12 +0000213
214 break;
215
Ted Kremenek276278e2008-03-27 22:05:32 +0000216 case 37:
217 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
218 return CheckNilArg(N, 0);
Mike Stump11289f42009-09-09 15:08:12 +0000219
220 break;
Ted Kremenekc0414922008-03-27 07:25:52 +0000221 }
Mike Stump11289f42009-09-09 15:08:12 +0000222
Ted Kremenekc0414922008-03-27 07:25:52 +0000223 return false;
224}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000225
226//===----------------------------------------------------------------------===//
227// Error reporting.
228//===----------------------------------------------------------------------===//
229
230namespace {
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000231
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000232class VISIBILITY_HIDDEN AuditCFNumberCreate : public GRSimpleAPICheck {
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000233 APIMisuse* BT;
Mike Stump11289f42009-09-09 15:08:12 +0000234
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000235 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
236 // it should always be passed with a call to Audit. The latter
237 // approach makes this class more stateless.
238 ASTContext& Ctx;
239 IdentifierInfo* II;
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000240 BugReporter& BR;
Ted Kremenek095f1a92009-06-18 23:58:37 +0000241
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000242public:
Mike Stump11289f42009-09-09 15:08:12 +0000243 AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
Ted Kremenek095f1a92009-06-18 23:58:37 +0000244 : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
Mike Stump11289f42009-09-09 15:08:12 +0000245
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000246 ~AuditCFNumberCreate() {}
Mike Stump11289f42009-09-09 15:08:12 +0000247
Zhongxing Xu20227f72009-08-06 01:32:16 +0000248 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump11289f42009-09-09 15:08:12 +0000249
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000250private:
Zhongxing Xu20227f72009-08-06 01:32:16 +0000251 void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N,
Mike Stump11289f42009-09-09 15:08:12 +0000252 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000253};
254} // end anonymous namespace
255
256enum CFNumberType {
257 kCFNumberSInt8Type = 1,
258 kCFNumberSInt16Type = 2,
259 kCFNumberSInt32Type = 3,
260 kCFNumberSInt64Type = 4,
261 kCFNumberFloat32Type = 5,
262 kCFNumberFloat64Type = 6,
263 kCFNumberCharType = 7,
264 kCFNumberShortType = 8,
265 kCFNumberIntType = 9,
266 kCFNumberLongType = 10,
267 kCFNumberLongLongType = 11,
268 kCFNumberFloatType = 12,
269 kCFNumberDoubleType = 13,
270 kCFNumberCFIndexType = 14,
271 kCFNumberNSIntegerType = 15,
272 kCFNumberCGFloatType = 16
273};
274
275namespace {
276 template<typename T>
277 class Optional {
278 bool IsKnown;
279 T Val;
280 public:
281 Optional() : IsKnown(false), Val(0) {}
282 Optional(const T& val) : IsKnown(true), Val(val) {}
Mike Stump11289f42009-09-09 15:08:12 +0000283
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000284 bool isKnown() const { return IsKnown; }
285
286 const T& getValue() const {
287 assert (isKnown());
288 return Val;
289 }
290
291 operator const T&() const {
292 return getValue();
293 }
294 };
295}
296
297static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
298 static unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump11289f42009-09-09 15:08:12 +0000299
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000300 if (i < kCFNumberCharType)
301 return FixedSize[i-1];
Mike Stump11289f42009-09-09 15:08:12 +0000302
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000303 QualType T;
Mike Stump11289f42009-09-09 15:08:12 +0000304
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000305 switch (i) {
306 case kCFNumberCharType: T = Ctx.CharTy; break;
307 case kCFNumberShortType: T = Ctx.ShortTy; break;
308 case kCFNumberIntType: T = Ctx.IntTy; break;
309 case kCFNumberLongType: T = Ctx.LongTy; break;
310 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
311 case kCFNumberFloatType: T = Ctx.FloatTy; break;
312 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
313 case kCFNumberCFIndexType:
314 case kCFNumberNSIntegerType:
315 case kCFNumberCGFloatType:
Mike Stump11289f42009-09-09 15:08:12 +0000316 // FIXME: We need a way to map from names to Type*.
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000317 default:
318 return Optional<uint64_t>();
319 }
Mike Stump11289f42009-09-09 15:08:12 +0000320
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000321 return Ctx.getTypeSize(T);
322}
323
324#if 0
325static const char* GetCFNumberTypeStr(uint64_t i) {
326 static const char* Names[] = {
327 "kCFNumberSInt8Type",
328 "kCFNumberSInt16Type",
329 "kCFNumberSInt32Type",
330 "kCFNumberSInt64Type",
331 "kCFNumberFloat32Type",
332 "kCFNumberFloat64Type",
333 "kCFNumberCharType",
334 "kCFNumberShortType",
335 "kCFNumberIntType",
336 "kCFNumberLongType",
337 "kCFNumberLongLongType",
338 "kCFNumberFloatType",
339 "kCFNumberDoubleType",
340 "kCFNumberCFIndexType",
341 "kCFNumberNSIntegerType",
342 "kCFNumberCGFloatType"
343 };
Mike Stump11289f42009-09-09 15:08:12 +0000344
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000345 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
346}
347#endif
348
Mike Stump11289f42009-09-09 15:08:12 +0000349bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000350 const CallExpr* CE =
351 cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump11289f42009-09-09 15:08:12 +0000352 const Expr* Callee = CE->getCallee();
353 SVal CallV = N->getState()->getSVal(Callee);
Zhongxing Xuac129432009-04-20 05:24:46 +0000354 const FunctionDecl* FD = CallV.getAsFunctionDecl();
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000355
Zhongxing Xuac129432009-04-20 05:24:46 +0000356 if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000357 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000358
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000359 // Get the value of the "theType" argument.
Ted Kremenek095f1a92009-06-18 23:58:37 +0000360 SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
Mike Stump11289f42009-09-09 15:08:12 +0000361
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000362 // FIXME: We really should allow ranges of valid theType values, and
363 // bifurcate the state appropriately.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000364 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Mike Stump11289f42009-09-09 15:08:12 +0000365
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000366 if (!V)
367 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000368
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000369 uint64_t NumberKind = V->getValue().getLimitedValue();
370 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000371
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000372 // FIXME: In some cases we can emit an error.
373 if (!TargetSize.isKnown())
374 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000375
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000376 // Look at the value of the integer being passed by reference. Essentially
377 // we want to catch cases where the value passed in is not equal to the
378 // size of the type being created.
Ted Kremenek095f1a92009-06-18 23:58:37 +0000379 SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
Mike Stump11289f42009-09-09 15:08:12 +0000380
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000381 // FIXME: Eventually we should handle arbitrary locations. We can do this
382 // by having an enhanced memory model that does low-level typing.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000383 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000384
385 if (!LV)
386 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000387
Zhongxing Xuf8f3f9d2009-11-10 02:17:20 +0000388 const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
Ted Kremenek87a7a452009-07-29 18:17:40 +0000389
390 if (!R)
391 return false;
392
Zhongxing Xu34d04b32009-05-09 03:57:34 +0000393 QualType T = Ctx.getCanonicalType(R->getValueType(Ctx));
Mike Stump11289f42009-09-09 15:08:12 +0000394
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000395 // FIXME: If the pointee isn't an integer type, should we flag a warning?
396 // People can do weird stuff with pointers.
Mike Stump11289f42009-09-09 15:08:12 +0000397
398 if (!T->isIntegerType())
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000399 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000400
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000401 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump11289f42009-09-09 15:08:12 +0000402
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000403 // CHECK: is SourceSize == TargetSize
Mike Stump11289f42009-09-09 15:08:12 +0000404
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000405 if (SourceSize == TargetSize)
406 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000407
Ted Kremenek5ca90a22008-10-04 05:50:14 +0000408 AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000409
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000410 // FIXME: We can actually create an abstract "CFNumber" object that has
411 // the bits initialized to the provided values.
412 return SourceSize < TargetSize;
413}
414
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000415void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex,
Zhongxing Xu20227f72009-08-06 01:32:16 +0000416 ExplodedNode *N,
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000417 uint64_t SourceSize, uint64_t TargetSize,
418 uint64_t NumberKind) {
Mike Stump11289f42009-09-09 15:08:12 +0000419
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000420 std::string sbuf;
421 llvm::raw_string_ostream os(sbuf);
Mike Stump11289f42009-09-09 15:08:12 +0000422
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000423 os << (SourceSize == 8 ? "An " : "A ")
424 << SourceSize << " bit integer is used to initialize a CFNumber "
425 "object that represents "
426 << (TargetSize == 8 ? "an " : "a ")
Mike Stump11289f42009-09-09 15:08:12 +0000427 << TargetSize << " bit integer. ";
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000428
429 if (SourceSize < TargetSize)
430 os << (TargetSize - SourceSize)
Mike Stump11289f42009-09-09 15:08:12 +0000431 << " bits of the CFNumber value will be garbage." ;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000432 else
433 os << (SourceSize - TargetSize)
434 << " bits of the input integer will be lost.";
Mike Stump11289f42009-09-09 15:08:12 +0000435
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000436 // Lazily create the BugType object. This will be owned
437 // by the BugReporter object 'BR' once we call BR.EmitWarning.
438 if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate");
439 RangedBugReport *report = new RangedBugReport(*BT, os.str().c_str(), N);
440 report->addRange(Ex->getSourceRange());
441 BR.EmitReport(report);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000442}
443
444GRSimpleAPICheck*
Mike Stump11289f42009-09-09 15:08:12 +0000445clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek095f1a92009-06-18 23:58:37 +0000446 return new AuditCFNumberCreate(Ctx, BR);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000447}
448
Ted Kremenek1f352db2008-07-22 16:21:24 +0000449//===----------------------------------------------------------------------===//
Ted Kremenekc057f412009-07-14 00:43:42 +0000450// CFRetain/CFRelease auditing for null arguments.
451//===----------------------------------------------------------------------===//
452
453namespace {
454class VISIBILITY_HIDDEN AuditCFRetainRelease : public GRSimpleAPICheck {
455 APIMisuse *BT;
Mike Stump11289f42009-09-09 15:08:12 +0000456
Ted Kremenekc057f412009-07-14 00:43:42 +0000457 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
458 // it should always be passed with a call to Audit. The latter
459 // approach makes this class more stateless.
460 ASTContext& Ctx;
461 IdentifierInfo *Retain, *Release;
462 BugReporter& BR;
Mike Stump11289f42009-09-09 15:08:12 +0000463
Ted Kremenekc057f412009-07-14 00:43:42 +0000464public:
Mike Stump11289f42009-09-09 15:08:12 +0000465 AuditCFRetainRelease(ASTContext& ctx, BugReporter& br)
Ted Kremenekc057f412009-07-14 00:43:42 +0000466 : BT(0), Ctx(ctx),
467 Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")),
468 BR(br){}
Mike Stump11289f42009-09-09 15:08:12 +0000469
Ted Kremenekc057f412009-07-14 00:43:42 +0000470 ~AuditCFRetainRelease() {}
Mike Stump11289f42009-09-09 15:08:12 +0000471
Zhongxing Xu20227f72009-08-06 01:32:16 +0000472 bool Audit(ExplodedNode* N, GRStateManager&);
Ted Kremenekc057f412009-07-14 00:43:42 +0000473};
474} // end anonymous namespace
475
476
Zhongxing Xu20227f72009-08-06 01:32:16 +0000477bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) {
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000478 const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump11289f42009-09-09 15:08:12 +0000479
Ted Kremenekc057f412009-07-14 00:43:42 +0000480 // If the CallExpr doesn't have exactly 1 argument just give up checking.
481 if (CE->getNumArgs() != 1)
482 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000483
Ted Kremenekc057f412009-07-14 00:43:42 +0000484 // Check if we called CFRetain/CFRelease.
485 const GRState* state = N->getState();
486 SVal X = state->getSVal(CE->getCallee());
487 const FunctionDecl* FD = X.getAsFunctionDecl();
Mike Stump11289f42009-09-09 15:08:12 +0000488
Ted Kremenekc057f412009-07-14 00:43:42 +0000489 if (!FD)
490 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000491
492 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenekc057f412009-07-14 00:43:42 +0000493 if (!(FuncII == Retain || FuncII == Release))
494 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000495
Ted Kremenekc057f412009-07-14 00:43:42 +0000496 // Finally, check if the argument is NULL.
497 // FIXME: We should be able to bifurcate the state here, as a successful
498 // check will result in the value not being NULL afterwards.
499 // FIXME: Need a way to register vistors for the BugReporter. Would like
500 // to benefit from the same diagnostics that regular null dereference
501 // reporting has.
502 if (state->getStateManager().isEqual(state, CE->getArg(0), 0)) {
503 if (!BT)
504 BT = new APIMisuse("null passed to CFRetain/CFRelease");
Mike Stump11289f42009-09-09 15:08:12 +0000505
Ted Kremenekc057f412009-07-14 00:43:42 +0000506 const char *description = (FuncII == Retain)
507 ? "Null pointer argument in call to CFRetain"
508 : "Null pointer argument in call to CFRelease";
509
510 RangedBugReport *report = new RangedBugReport(*BT, description, N);
511 report->addRange(CE->getArg(0)->getSourceRange());
512 BR.EmitReport(report);
513 return true;
514 }
515
516 return false;
517}
Mike Stump11289f42009-09-09 15:08:12 +0000518
519
Ted Kremenekc057f412009-07-14 00:43:42 +0000520GRSimpleAPICheck*
Mike Stump11289f42009-09-09 15:08:12 +0000521clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenekc057f412009-07-14 00:43:42 +0000522 return new AuditCFRetainRelease(Ctx, BR);
523}
524
525//===----------------------------------------------------------------------===//
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000526// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
527//===----------------------------------------------------------------------===//
528
529namespace {
530class VISIBILITY_HIDDEN ClassReleaseChecker :
531 public CheckerVisitor<ClassReleaseChecker> {
532 Selector releaseS;
533 Selector retainS;
534 Selector autoreleaseS;
535 Selector drainS;
536 BugType *BT;
537public:
538 ClassReleaseChecker(ASTContext &Ctx)
539 : releaseS(GetNullarySelector("release", Ctx)),
540 retainS(GetNullarySelector("retain", Ctx)),
541 autoreleaseS(GetNullarySelector("autorelease", Ctx)),
542 drainS(GetNullarySelector("drain", Ctx)),
543 BT(0) {}
544
545 static void *getTag() { static int x = 0; return &x; }
546
547 void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);
548};
549}
550
551void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C,
552 const ObjCMessageExpr *ME) {
553
554 const IdentifierInfo *ClsName = ME->getClassName();
555 if (!ClsName)
556 return;
557
558 Selector S = ME->getSelector();
Benjamin Kramer7d875c72009-11-20 10:03:00 +0000559 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000560 return;
561
562 if (!BT)
563 BT = new APIMisuse("message incorrectly sent to class instead of class "
564 "instance");
565
566 ExplodedNode *N = C.GenerateNode(ME, C.getState(), false);
567 if (!N)
568 return;
569
570 C.addTransition(N);
571
572 llvm::SmallString<200> buf;
573 llvm::raw_svector_ostream os(buf);
574
575 os << "The '" << S.getAsString() << "' message should be sent to instances "
576 "of class '" << ClsName->getName()
577 << "' and not the class directly";
578
579 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
580 report->addRange(ME->getSourceRange());
581 C.EmitReport(report);
582}
583
584//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000585// Check registration.
Ted Kremenekc057f412009-07-14 00:43:42 +0000586//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000587
Zhongxing Xu6be1a4e2009-08-21 02:18:44 +0000588void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) {
Ted Kremenek1f352db2008-07-22 16:21:24 +0000589 ASTContext& Ctx = Eng.getContext();
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000590 BugReporter &BR = Eng.getBugReporter();
Ted Kremenek1f352db2008-07-22 16:21:24 +0000591
Ted Kremenek095f1a92009-06-18 23:58:37 +0000592 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
Ted Kremenek1f352db2008-07-22 16:21:24 +0000593 Stmt::ObjCMessageExprClass);
Mike Stump11289f42009-09-09 15:08:12 +0000594 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
Ted Kremenekc057f412009-07-14 00:43:42 +0000595 Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass);
Mike Stump11289f42009-09-09 15:08:12 +0000596
Zhongxing Xu6be1a4e2009-08-21 02:18:44 +0000597 RegisterNSErrorChecks(BR, Eng, D);
Ted Kremenek18c7cee2009-11-03 08:03:59 +0000598 RegisterNSAutoreleasePoolChecks(Eng);
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000599 Eng.registerCheck(new ClassReleaseChecker(Ctx));
Ted Kremenek1f352db2008-07-22 16:21:24 +0000600}