blob: 5a88a7af9354cac89cf874907be8a95de78e4654 [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"
31#include "llvm/Support/Compiler.h"
32
Ted Kremenek99c6ad32008-03-27 07:25:52 +000033using namespace clang;
Ted Kremenek52755612008-03-27 17:17:22 +000034
Steve Naroff14108da2009-07-10 23:34:53 +000035static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) {
36 const Expr* Receiver = ME->getReceiver();
Mike Stump1eb44332009-09-09 15:08:12 +000037
Ted Kremenek4ba62832008-03-27 22:05:32 +000038 if (!Receiver)
39 return NULL;
Mike Stump1eb44332009-09-09 15:08:12 +000040
Steve Naroff14108da2009-07-10 23:34:53 +000041 if (const ObjCObjectPointerType *PT =
John McCall183700f2009-09-21 23:43:11 +000042 Receiver->getType()->getAs<ObjCObjectPointerType>())
Steve Naroff14108da2009-07-10 23:34:53 +000043 return PT->getInterfaceType();
Ted Kremenekc1ff3cd2008-04-30 22:48:21 +000044
Ted Kremenekc1ff3cd2008-04-30 22:48:21 +000045 return NULL;
Ted Kremenek4ba62832008-03-27 22:05:32 +000046}
47
Steve Naroff14108da2009-07-10 23:34:53 +000048static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
Daniel Dunbare013d682009-10-18 20:26:12 +000049 if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME))
50 return ReceiverType->getDecl()->getIdentifier()->getNameStart();
51 return NULL;
Ted Kremenek4ba62832008-03-27 22:05:32 +000052}
Ted Kremenek52755612008-03-27 17:17:22 +000053
Ted Kremenekf1ae7052008-04-03 17:57:38 +000054namespace {
Ted Kremenekb344f912008-09-21 19:01:39 +000055
Ted Kremenekcf118d42009-02-04 23:49:09 +000056class VISIBILITY_HIDDEN APIMisuse : public BugType {
Ted Kremenekf1ae7052008-04-03 17:57:38 +000057public:
Ted Kremenekcf118d42009-02-04 23:49:09 +000058 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
Ted Kremenekf1ae7052008-04-03 17:57:38 +000059};
Mike Stump1eb44332009-09-09 15:08:12 +000060
Ted Kremenekf1ae7052008-04-03 17:57:38 +000061class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenekcf118d42009-02-04 23:49:09 +000062 APIMisuse *BT;
63 BugReporter& BR;
Ted Kremenekf1ae7052008-04-03 17:57:38 +000064 ASTContext &Ctx;
Mike Stump1eb44332009-09-09 15:08:12 +000065
Daniel Dunbard777d582009-10-17 18:12:21 +000066 bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix);
Zhongxing Xu031ccc02009-08-06 12:48:26 +000067 bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME);
Mike Stump1eb44332009-09-09 15:08:12 +000068
69 void Warn(ExplodedNode* N, const Expr* E, const std::string& s);
Zhongxing Xu031ccc02009-08-06 12:48:26 +000070 void WarnNilArg(ExplodedNode* N, const Expr* E);
Mike Stump1eb44332009-09-09 15:08:12 +000071
Zhongxing Xu031ccc02009-08-06 12:48:26 +000072 bool CheckNilArg(ExplodedNode* N, unsigned Arg);
Ted Kremenekf1ae7052008-04-03 17:57:38 +000073
74public:
Mike Stump1eb44332009-09-09 15:08:12 +000075 BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br)
Ted Kremenek23ec48c2009-06-18 23:58:37 +000076 : BT(0), BR(br), Ctx(ctx) {}
Mike Stump1eb44332009-09-09 15:08:12 +000077
Zhongxing Xuc5619d92009-08-06 01:32:16 +000078 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump1eb44332009-09-09 15:08:12 +000079
80private:
81 void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekcf118d42009-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 Stump1eb44332009-09-09 15:08:12 +000086
Ted Kremenekcf118d42009-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 Stump1eb44332009-09-09 15:08:12 +000090
Ted Kremenekcf118d42009-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 Kremenekf1ae7052008-04-03 17:57:38 +000094 }
95};
Mike Stump1eb44332009-09-09 15:08:12 +000096
Ted Kremenekf1ae7052008-04-03 17:57:38 +000097} // end anonymous namespace
98
99
100GRSimpleAPICheck*
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000101clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) {
Mike Stump1eb44332009-09-09 15:08:12 +0000102 return new BasicObjCFoundationChecks(Ctx, BR);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000103}
104
105
106
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000107bool BasicObjCFoundationChecks::Audit(ExplodedNode* N,
Ted Kremenek4adc81e2008-08-13 04:27:00 +0000108 GRStateManager&) {
Mike Stump1eb44332009-09-09 15:08:12 +0000109
Ted Kremenek5f85e172009-07-22 22:35:28 +0000110 const ObjCMessageExpr* ME =
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000111 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000112
Steve Naroff14108da2009-07-10 23:34:53 +0000113 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
Mike Stump1eb44332009-09-09 15:08:12 +0000114
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000115 if (!ReceiverType)
Nuno Lopesf7427942008-05-20 17:33:56 +0000116 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000117
Daniel Dunbard777d582009-10-17 18:12:21 +0000118 if (isNSString(ReceiverType,
Daniel Dunbar01eb9b92009-10-18 21:17:35 +0000119 ReceiverType->getDecl()->getIdentifier()->getName()))
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000120 return AuditNSString(N, ME);
121
Nuno Lopesf7427942008-05-20 17:33:56 +0000122 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000123}
124
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000125static inline bool isNil(SVal X) {
Mike Stump1eb44332009-09-09 15:08:12 +0000126 return isa<loc::ConcreteInt>(X);
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000127}
128
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000129//===----------------------------------------------------------------------===//
130// Error reporting.
131//===----------------------------------------------------------------------===//
132
Zhongxing Xu031ccc02009-08-06 12:48:26 +0000133bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) {
Ted Kremenek5f85e172009-07-22 22:35:28 +0000134 const ObjCMessageExpr* ME =
Ted Kremenek4ba62832008-03-27 22:05:32 +0000135 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000136
Ted Kremenek5f85e172009-07-22 22:35:28 +0000137 const Expr * E = ME->getArg(Arg);
Mike Stump1eb44332009-09-09 15:08:12 +0000138
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000139 if (isNil(N->getState()->getSVal(E))) {
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000140 WarnNilArg(N, ME, Arg);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000141 return true;
142 }
Mike Stump1eb44332009-09-09 15:08:12 +0000143
Ted Kremenek4ba62832008-03-27 22:05:32 +0000144 return false;
145}
146
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000147//===----------------------------------------------------------------------===//
148// NSString checking.
149//===----------------------------------------------------------------------===//
150
Steve Naroff14108da2009-07-10 23:34:53 +0000151bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T,
Daniel Dunbard777d582009-10-17 18:12:21 +0000152 llvm::StringRef ClassName) {
153 return ClassName == "NSString" || ClassName == "NSMutableString";
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000154}
155
Mike Stump1eb44332009-09-09 15:08:12 +0000156bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N,
Ted Kremenek5f85e172009-07-22 22:35:28 +0000157 const ObjCMessageExpr* ME) {
Mike Stump1eb44332009-09-09 15:08:12 +0000158
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000159 Selector S = ME->getSelector();
Mike Stump1eb44332009-09-09 15:08:12 +0000160
Ted Kremenek99c6ad32008-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 Stump1eb44332009-09-09 15:08:12 +0000166
Chris Lattner077bf5e2008-11-24 03:33:13 +0000167 std::string name = S.getAsString();
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000168 assert (!name.empty());
169 const char* cstr = &name[0];
170 unsigned len = name.size();
Mike Stump1eb44332009-09-09 15:08:12 +0000171
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000172 switch (len) {
173 default:
174 break;
Mike Stump1eb44332009-09-09 15:08:12 +0000175 case 8:
Ted Kremenek4ba62832008-03-27 22:05:32 +0000176 if (!strcmp(cstr, "compare:"))
177 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000178
Ted Kremenek4ba62832008-03-27 22:05:32 +0000179 break;
Mike Stump1eb44332009-09-09 15:08:12 +0000180
Ted Kremenek8730e132008-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 Stump1eb44332009-09-09 15:08:12 +0000188
Ted Kremenek8730e132008-03-28 16:09:38 +0000189 break;
Mike Stump1eb44332009-09-09 15:08:12 +0000190
Ted Kremenek4ba62832008-03-27 22:05:32 +0000191 case 16:
192 if (!strcmp(cstr, "compare:options:"))
193 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000194
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000195 break;
Mike Stump1eb44332009-09-09 15:08:12 +0000196
Ted Kremenek4ba62832008-03-27 22:05:32 +0000197 case 22:
198 if (!strcmp(cstr, "compare:options:range:"))
199 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000200
Ted Kremenek4ba62832008-03-27 22:05:32 +0000201 break;
Mike Stump1eb44332009-09-09 15:08:12 +0000202
Ted Kremenek4ba62832008-03-27 22:05:32 +0000203 case 23:
Mike Stump1eb44332009-09-09 15:08:12 +0000204
Ted Kremenek4ba62832008-03-27 22:05:32 +0000205 if (!strcmp(cstr, "caseInsensitiveCompare:"))
206 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000207
Ted Kremenek4ba62832008-03-27 22:05:32 +0000208 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000209
Ted Kremenek4ba62832008-03-27 22:05:32 +0000210 case 29:
211 if (!strcmp(cstr, "compare:options:range:locale:"))
212 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000213
214 break;
215
Ted Kremenek4ba62832008-03-27 22:05:32 +0000216 case 37:
217 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
218 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000219
220 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000221 }
Mike Stump1eb44332009-09-09 15:08:12 +0000222
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000223 return false;
224}
Ted Kremenek04bc8762008-06-26 23:59:48 +0000225
226//===----------------------------------------------------------------------===//
227// Error reporting.
228//===----------------------------------------------------------------------===//
229
230namespace {
Ted Kremenek04bc8762008-06-26 23:59:48 +0000231
Ted Kremenek04bc8762008-06-26 23:59:48 +0000232class VISIBILITY_HIDDEN AuditCFNumberCreate : public GRSimpleAPICheck {
Ted Kremenekcf118d42009-02-04 23:49:09 +0000233 APIMisuse* BT;
Mike Stump1eb44332009-09-09 15:08:12 +0000234
Ted Kremenek04bc8762008-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 Kremenekcf118d42009-02-04 23:49:09 +0000240 BugReporter& BR;
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000241
Ted Kremenek04bc8762008-06-26 23:59:48 +0000242public:
Mike Stump1eb44332009-09-09 15:08:12 +0000243 AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000244 : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
Mike Stump1eb44332009-09-09 15:08:12 +0000245
Ted Kremenekcf118d42009-02-04 23:49:09 +0000246 ~AuditCFNumberCreate() {}
Mike Stump1eb44332009-09-09 15:08:12 +0000247
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000248 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump1eb44332009-09-09 15:08:12 +0000249
Ted Kremenek04bc8762008-06-26 23:59:48 +0000250private:
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000251 void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N,
Mike Stump1eb44332009-09-09 15:08:12 +0000252 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000283
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000299
Ted Kremenek04bc8762008-06-26 23:59:48 +0000300 if (i < kCFNumberCharType)
301 return FixedSize[i-1];
Mike Stump1eb44332009-09-09 15:08:12 +0000302
Ted Kremenek04bc8762008-06-26 23:59:48 +0000303 QualType T;
Mike Stump1eb44332009-09-09 15:08:12 +0000304
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000316 // FIXME: We need a way to map from names to Type*.
Ted Kremenek04bc8762008-06-26 23:59:48 +0000317 default:
318 return Optional<uint64_t>();
319 }
Mike Stump1eb44332009-09-09 15:08:12 +0000320
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000344
Ted Kremenek04bc8762008-06-26 23:59:48 +0000345 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
346}
347#endif
348
Mike Stump1eb44332009-09-09 15:08:12 +0000349bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
Ted Kremenek5f85e172009-07-22 22:35:28 +0000350 const CallExpr* CE =
351 cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000352 const Expr* Callee = CE->getCallee();
353 SVal CallV = N->getState()->getSVal(Callee);
Zhongxing Xu369f4472009-04-20 05:24:46 +0000354 const FunctionDecl* FD = CallV.getAsFunctionDecl();
Ted Kremenek04bc8762008-06-26 23:59:48 +0000355
Zhongxing Xu369f4472009-04-20 05:24:46 +0000356 if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
Ted Kremenek04bc8762008-06-26 23:59:48 +0000357 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000358
Ted Kremenek04bc8762008-06-26 23:59:48 +0000359 // Get the value of the "theType" argument.
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000360 SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
Mike Stump1eb44332009-09-09 15:08:12 +0000361
Ted Kremenek04bc8762008-06-26 23:59:48 +0000362 // FIXME: We really should allow ranges of valid theType values, and
363 // bifurcate the state appropriately.
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000364 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Mike Stump1eb44332009-09-09 15:08:12 +0000365
Ted Kremenek04bc8762008-06-26 23:59:48 +0000366 if (!V)
367 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000368
Ted Kremenek04bc8762008-06-26 23:59:48 +0000369 uint64_t NumberKind = V->getValue().getLimitedValue();
370 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000371
Ted Kremenek04bc8762008-06-26 23:59:48 +0000372 // FIXME: In some cases we can emit an error.
373 if (!TargetSize.isKnown())
374 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000375
Ted Kremenek04bc8762008-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 Kremenek23ec48c2009-06-18 23:58:37 +0000379 SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
Mike Stump1eb44332009-09-09 15:08:12 +0000380
Ted Kremenek04bc8762008-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 Xu1c96b242008-10-17 05:57:07 +0000383 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000384
385 if (!LV)
386 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000387
Zhongxing Xu479529e2009-11-10 02:17:20 +0000388 const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
Ted Kremenek5e77eba2009-07-29 18:17:40 +0000389
390 if (!R)
391 return false;
392
Zhongxing Xua82d8aa2009-05-09 03:57:34 +0000393 QualType T = Ctx.getCanonicalType(R->getValueType(Ctx));
Mike Stump1eb44332009-09-09 15:08:12 +0000394
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000397
398 if (!T->isIntegerType())
Ted Kremenek04bc8762008-06-26 23:59:48 +0000399 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000400
Ted Kremenek04bc8762008-06-26 23:59:48 +0000401 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump1eb44332009-09-09 15:08:12 +0000402
Ted Kremenek04bc8762008-06-26 23:59:48 +0000403 // CHECK: is SourceSize == TargetSize
Mike Stump1eb44332009-09-09 15:08:12 +0000404
Ted Kremenek04bc8762008-06-26 23:59:48 +0000405 if (SourceSize == TargetSize)
406 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000407
Ted Kremenek9e240492008-10-04 05:50:14 +0000408 AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000409
Ted Kremenek04bc8762008-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 Kremenek5f85e172009-07-22 22:35:28 +0000415void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex,
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000416 ExplodedNode *N,
Ted Kremenek04bc8762008-06-26 23:59:48 +0000417 uint64_t SourceSize, uint64_t TargetSize,
418 uint64_t NumberKind) {
Mike Stump1eb44332009-09-09 15:08:12 +0000419
Ted Kremenekcf118d42009-02-04 23:49:09 +0000420 std::string sbuf;
421 llvm::raw_string_ostream os(sbuf);
Mike Stump1eb44332009-09-09 15:08:12 +0000422
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000427 << TargetSize << " bit integer. ";
Ted Kremenek04bc8762008-06-26 23:59:48 +0000428
429 if (SourceSize < TargetSize)
430 os << (TargetSize - SourceSize)
Mike Stump1eb44332009-09-09 15:08:12 +0000431 << " bits of the CFNumber value will be garbage." ;
Ted Kremenek04bc8762008-06-26 23:59:48 +0000432 else
433 os << (SourceSize - TargetSize)
434 << " bits of the input integer will be lost.";
Mike Stump1eb44332009-09-09 15:08:12 +0000435
Ted Kremenekcf118d42009-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 Kremenek04bc8762008-06-26 23:59:48 +0000442}
443
444GRSimpleAPICheck*
Mike Stump1eb44332009-09-09 15:08:12 +0000445clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000446 return new AuditCFNumberCreate(Ctx, BR);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000447}
448
Ted Kremenek78d46242008-07-22 16:21:24 +0000449//===----------------------------------------------------------------------===//
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000456
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000463
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000464public:
Mike Stump1eb44332009-09-09 15:08:12 +0000465 AuditCFRetainRelease(ASTContext& ctx, BugReporter& br)
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000469
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000470 ~AuditCFRetainRelease() {}
Mike Stump1eb44332009-09-09 15:08:12 +0000471
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000472 bool Audit(ExplodedNode* N, GRStateManager&);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000473};
474} // end anonymous namespace
475
476
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000477bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) {
Ted Kremenek5f85e172009-07-22 22:35:28 +0000478 const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000479
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000483
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000488
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000489 if (!FD)
490 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000491
492 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000493 if (!(FuncII == Retain || FuncII == Release))
494 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000495
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000505
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000518
519
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000520GRSimpleAPICheck*
Mike Stump1eb44332009-09-09 15:08:12 +0000521clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000522 return new AuditCFRetainRelease(Ctx, BR);
523}
524
525//===----------------------------------------------------------------------===//
Ted Kremenek50e837b2009-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 Kramer921ddc42009-11-20 10:03:00 +0000559 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremenek50e837b2009-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 Kremenek78d46242008-07-22 16:21:24 +0000585// Check registration.
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000586//===----------------------------------------------------------------------===//
Ted Kremenek78d46242008-07-22 16:21:24 +0000587
Zhongxing Xu5ab128b2009-08-21 02:18:44 +0000588void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) {
Ted Kremenek78d46242008-07-22 16:21:24 +0000589 ASTContext& Ctx = Eng.getContext();
Ted Kremenekcf118d42009-02-04 23:49:09 +0000590 BugReporter &BR = Eng.getBugReporter();
Ted Kremenek78d46242008-07-22 16:21:24 +0000591
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000592 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
Ted Kremenek78d46242008-07-22 16:21:24 +0000593 Stmt::ObjCMessageExprClass);
Mike Stump1eb44332009-09-09 15:08:12 +0000594 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000595 Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass);
Mike Stump1eb44332009-09-09 15:08:12 +0000596
Zhongxing Xu5ab128b2009-08-21 02:18:44 +0000597 RegisterNSErrorChecks(BR, Eng, D);
Ted Kremenek54cb7cc2009-11-03 08:03:59 +0000598 RegisterNSAutoreleasePoolChecks(Eng);
Ted Kremenek50e837b2009-11-20 05:27:05 +0000599 Eng.registerCheck(new ClassReleaseChecker(Ctx));
Ted Kremenek78d46242008-07-22 16:21:24 +0000600}