blob: b852e2ad9a488de06636fed5333ea65eb7e5030d [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 Kremenek1309f9a2010-01-25 04:41:41 +000018#include "clang/Checker/PathSensitive/ExplodedGraph.h"
19#include "clang/Checker/PathSensitive/GRSimpleAPICheck.h"
20#include "clang/Checker/PathSensitive/GRExprEngine.h"
21#include "clang/Checker/PathSensitive/GRState.h"
Benjamin Kramer5e2d2c22010-03-27 21:19:47 +000022#include "clang/Checker/BugReporter/BugType.h"
Ted Kremenek1309f9a2010-01-25 04:41:41 +000023#include "clang/Checker/PathSensitive/MemRegion.h"
Ted Kremenek1309f9a2010-01-25 04:41:41 +000024#include "clang/Checker/PathSensitive/CheckerVisitor.h"
Ted Kremenek97053092010-01-26 22:59:55 +000025#include "clang/Checker/Checkers/LocalCheckers.h"
Daniel Dunbarc4a1dea2008-08-11 05:35:13 +000026#include "clang/AST/DeclObjC.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000027#include "clang/AST/Expr.h"
Steve Narofff494b572008-05-29 21:12:08 +000028#include "clang/AST/ExprObjC.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000029#include "clang/AST/ASTContext.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000030
Ted Kremenek99c6ad32008-03-27 07:25:52 +000031using namespace clang;
Ted Kremenek52755612008-03-27 17:17:22 +000032
Steve Naroff14108da2009-07-10 23:34:53 +000033static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) {
Douglas Gregor04badcf2010-04-21 00:45:42 +000034 QualType T;
35 switch (ME->getReceiverKind()) {
36 case ObjCMessageExpr::Instance:
37 T = ME->getInstanceReceiver()->getType();
38 break;
Mike Stump1eb44332009-09-09 15:08:12 +000039
Douglas Gregor04badcf2010-04-21 00:45:42 +000040 case ObjCMessageExpr::SuperInstance:
41 T = ME->getSuperType();
42 break;
Mike Stump1eb44332009-09-09 15:08:12 +000043
Douglas Gregor04badcf2010-04-21 00:45:42 +000044 case ObjCMessageExpr::Class:
45 case ObjCMessageExpr::SuperClass:
46 return 0;
47 }
48
49 if (const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>())
Steve Naroff14108da2009-07-10 23:34:53 +000050 return PT->getInterfaceType();
Ted Kremenekc1ff3cd2008-04-30 22:48:21 +000051
Ted Kremenekc1ff3cd2008-04-30 22:48:21 +000052 return NULL;
Ted Kremenek4ba62832008-03-27 22:05:32 +000053}
54
Steve Naroff14108da2009-07-10 23:34:53 +000055static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
Daniel Dunbare013d682009-10-18 20:26:12 +000056 if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME))
57 return ReceiverType->getDecl()->getIdentifier()->getNameStart();
58 return NULL;
Ted Kremenek4ba62832008-03-27 22:05:32 +000059}
Ted Kremenek52755612008-03-27 17:17:22 +000060
Ted Kremenekf1ae7052008-04-03 17:57:38 +000061namespace {
Ted Kremenekb344f912008-09-21 19:01:39 +000062
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +000063class APIMisuse : public BugType {
Ted Kremenekf1ae7052008-04-03 17:57:38 +000064public:
Ted Kremenekcf118d42009-02-04 23:49:09 +000065 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
Ted Kremenekf1ae7052008-04-03 17:57:38 +000066};
Mike Stump1eb44332009-09-09 15:08:12 +000067
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +000068class BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenekcf118d42009-02-04 23:49:09 +000069 APIMisuse *BT;
70 BugReporter& BR;
Ted Kremenekf1ae7052008-04-03 17:57:38 +000071 ASTContext &Ctx;
Mike Stump1eb44332009-09-09 15:08:12 +000072
Daniel Dunbard777d582009-10-17 18:12:21 +000073 bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix);
Zhongxing Xu031ccc02009-08-06 12:48:26 +000074 bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME);
Mike Stump1eb44332009-09-09 15:08:12 +000075
76 void Warn(ExplodedNode* N, const Expr* E, const std::string& s);
Zhongxing Xu031ccc02009-08-06 12:48:26 +000077 void WarnNilArg(ExplodedNode* N, const Expr* E);
Mike Stump1eb44332009-09-09 15:08:12 +000078
Zhongxing Xu031ccc02009-08-06 12:48:26 +000079 bool CheckNilArg(ExplodedNode* N, unsigned Arg);
Ted Kremenekf1ae7052008-04-03 17:57:38 +000080
81public:
Mike Stump1eb44332009-09-09 15:08:12 +000082 BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br)
Ted Kremenek23ec48c2009-06-18 23:58:37 +000083 : BT(0), BR(br), Ctx(ctx) {}
Mike Stump1eb44332009-09-09 15:08:12 +000084
Zhongxing Xuc5619d92009-08-06 01:32:16 +000085 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump1eb44332009-09-09 15:08:12 +000086
87private:
88 void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekcf118d42009-02-04 23:49:09 +000089 std::string sbuf;
90 llvm::raw_string_ostream os(sbuf);
91 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
92 << ME->getSelector().getAsString() << "' cannot be nil.";
Mike Stump1eb44332009-09-09 15:08:12 +000093
Ted Kremenekcf118d42009-02-04 23:49:09 +000094 // Lazily create the BugType object for NilArg. This will be owned
95 // by the BugReporter object 'BR' once we call BR.EmitWarning.
96 if (!BT) BT = new APIMisuse("nil argument");
Mike Stump1eb44332009-09-09 15:08:12 +000097
Benjamin Kramer4988a9a2009-11-29 18:03:28 +000098 RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
Ted Kremenekcf118d42009-02-04 23:49:09 +000099 R->addRange(ME->getArg(Arg)->getSourceRange());
100 BR.EmitReport(R);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000101 }
102};
Mike Stump1eb44332009-09-09 15:08:12 +0000103
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000104} // end anonymous namespace
105
106
107GRSimpleAPICheck*
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000108clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) {
Mike Stump1eb44332009-09-09 15:08:12 +0000109 return new BasicObjCFoundationChecks(Ctx, BR);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000110}
111
112
113
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000114bool BasicObjCFoundationChecks::Audit(ExplodedNode* N,
Ted Kremenek4adc81e2008-08-13 04:27:00 +0000115 GRStateManager&) {
Mike Stump1eb44332009-09-09 15:08:12 +0000116
Ted Kremenek5f85e172009-07-22 22:35:28 +0000117 const ObjCMessageExpr* ME =
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000118 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000119
Steve Naroff14108da2009-07-10 23:34:53 +0000120 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
Mike Stump1eb44332009-09-09 15:08:12 +0000121
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000122 if (!ReceiverType)
Nuno Lopesf7427942008-05-20 17:33:56 +0000123 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000124
Daniel Dunbard777d582009-10-17 18:12:21 +0000125 if (isNSString(ReceiverType,
Daniel Dunbar01eb9b92009-10-18 21:17:35 +0000126 ReceiverType->getDecl()->getIdentifier()->getName()))
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000127 return AuditNSString(N, ME);
128
Nuno Lopesf7427942008-05-20 17:33:56 +0000129 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000130}
131
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000132static inline bool isNil(SVal X) {
Mike Stump1eb44332009-09-09 15:08:12 +0000133 return isa<loc::ConcreteInt>(X);
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000134}
135
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000136//===----------------------------------------------------------------------===//
137// Error reporting.
138//===----------------------------------------------------------------------===//
139
Zhongxing Xu031ccc02009-08-06 12:48:26 +0000140bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) {
Ted Kremenek5f85e172009-07-22 22:35:28 +0000141 const ObjCMessageExpr* ME =
Ted Kremenek4ba62832008-03-27 22:05:32 +0000142 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000143
Ted Kremenek5f85e172009-07-22 22:35:28 +0000144 const Expr * E = ME->getArg(Arg);
Mike Stump1eb44332009-09-09 15:08:12 +0000145
Ted Kremenek13976632010-02-08 16:18:51 +0000146 if (isNil(N->getState()->getSVal(E))) {
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000147 WarnNilArg(N, ME, Arg);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000148 return true;
149 }
Mike Stump1eb44332009-09-09 15:08:12 +0000150
Ted Kremenek4ba62832008-03-27 22:05:32 +0000151 return false;
152}
153
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000154//===----------------------------------------------------------------------===//
155// NSString checking.
156//===----------------------------------------------------------------------===//
157
Steve Naroff14108da2009-07-10 23:34:53 +0000158bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T,
Daniel Dunbard777d582009-10-17 18:12:21 +0000159 llvm::StringRef ClassName) {
160 return ClassName == "NSString" || ClassName == "NSMutableString";
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000161}
162
Mike Stump1eb44332009-09-09 15:08:12 +0000163bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N,
Ted Kremenek5f85e172009-07-22 22:35:28 +0000164 const ObjCMessageExpr* ME) {
Mike Stump1eb44332009-09-09 15:08:12 +0000165
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000166 Selector S = ME->getSelector();
Mike Stump1eb44332009-09-09 15:08:12 +0000167
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000168 if (S.isUnarySelector())
169 return false;
170
171 // FIXME: This is going to be really slow doing these checks with
172 // lexical comparisons.
Mike Stump1eb44332009-09-09 15:08:12 +0000173
Benjamin Kramer064fb202010-02-08 19:51:59 +0000174 std::string NameStr = S.getAsString();
175 llvm::StringRef Name(NameStr);
176 assert(!Name.empty());
Mike Stump1eb44332009-09-09 15:08:12 +0000177
Benjamin Kramer064fb202010-02-08 19:51:59 +0000178 // FIXME: Checking for initWithFormat: will not work in most cases
179 // yet because [NSString alloc] returns id, not NSString*. We will
180 // need support for tracking expected-type information in the analyzer
181 // to find these errors.
182 if (Name == "caseInsensitiveCompare:" ||
183 Name == "compare:" ||
184 Name == "compare:options:" ||
185 Name == "compare:options:range:" ||
186 Name == "compare:options:range:locale:" ||
187 Name == "componentsSeparatedByCharactersInSet:" ||
188 Name == "initWithFormat:")
189 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000190
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000191 return false;
192}
Ted Kremenek04bc8762008-06-26 23:59:48 +0000193
194//===----------------------------------------------------------------------===//
195// Error reporting.
196//===----------------------------------------------------------------------===//
197
198namespace {
Ted Kremenek04bc8762008-06-26 23:59:48 +0000199
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +0000200class AuditCFNumberCreate : public GRSimpleAPICheck {
Ted Kremenekcf118d42009-02-04 23:49:09 +0000201 APIMisuse* BT;
Mike Stump1eb44332009-09-09 15:08:12 +0000202
Ted Kremenek04bc8762008-06-26 23:59:48 +0000203 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
204 // it should always be passed with a call to Audit. The latter
205 // approach makes this class more stateless.
206 ASTContext& Ctx;
207 IdentifierInfo* II;
Ted Kremenekcf118d42009-02-04 23:49:09 +0000208 BugReporter& BR;
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000209
Ted Kremenek04bc8762008-06-26 23:59:48 +0000210public:
Mike Stump1eb44332009-09-09 15:08:12 +0000211 AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000212 : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
Mike Stump1eb44332009-09-09 15:08:12 +0000213
Ted Kremenekcf118d42009-02-04 23:49:09 +0000214 ~AuditCFNumberCreate() {}
Mike Stump1eb44332009-09-09 15:08:12 +0000215
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000216 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump1eb44332009-09-09 15:08:12 +0000217
Ted Kremenek04bc8762008-06-26 23:59:48 +0000218private:
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000219 void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N,
Mike Stump1eb44332009-09-09 15:08:12 +0000220 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000221};
222} // end anonymous namespace
223
224enum CFNumberType {
225 kCFNumberSInt8Type = 1,
226 kCFNumberSInt16Type = 2,
227 kCFNumberSInt32Type = 3,
228 kCFNumberSInt64Type = 4,
229 kCFNumberFloat32Type = 5,
230 kCFNumberFloat64Type = 6,
231 kCFNumberCharType = 7,
232 kCFNumberShortType = 8,
233 kCFNumberIntType = 9,
234 kCFNumberLongType = 10,
235 kCFNumberLongLongType = 11,
236 kCFNumberFloatType = 12,
237 kCFNumberDoubleType = 13,
238 kCFNumberCFIndexType = 14,
239 kCFNumberNSIntegerType = 15,
240 kCFNumberCGFloatType = 16
241};
242
243namespace {
244 template<typename T>
245 class Optional {
246 bool IsKnown;
247 T Val;
248 public:
249 Optional() : IsKnown(false), Val(0) {}
250 Optional(const T& val) : IsKnown(true), Val(val) {}
Mike Stump1eb44332009-09-09 15:08:12 +0000251
Ted Kremenek04bc8762008-06-26 23:59:48 +0000252 bool isKnown() const { return IsKnown; }
253
254 const T& getValue() const {
255 assert (isKnown());
256 return Val;
257 }
258
259 operator const T&() const {
260 return getValue();
261 }
262 };
263}
264
265static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
Nuno Lopes2550d702009-12-23 17:49:57 +0000266 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump1eb44332009-09-09 15:08:12 +0000267
Ted Kremenek04bc8762008-06-26 23:59:48 +0000268 if (i < kCFNumberCharType)
269 return FixedSize[i-1];
Mike Stump1eb44332009-09-09 15:08:12 +0000270
Ted Kremenek04bc8762008-06-26 23:59:48 +0000271 QualType T;
Mike Stump1eb44332009-09-09 15:08:12 +0000272
Ted Kremenek04bc8762008-06-26 23:59:48 +0000273 switch (i) {
274 case kCFNumberCharType: T = Ctx.CharTy; break;
275 case kCFNumberShortType: T = Ctx.ShortTy; break;
276 case kCFNumberIntType: T = Ctx.IntTy; break;
277 case kCFNumberLongType: T = Ctx.LongTy; break;
278 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
279 case kCFNumberFloatType: T = Ctx.FloatTy; break;
280 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
281 case kCFNumberCFIndexType:
282 case kCFNumberNSIntegerType:
283 case kCFNumberCGFloatType:
Mike Stump1eb44332009-09-09 15:08:12 +0000284 // FIXME: We need a way to map from names to Type*.
Ted Kremenek04bc8762008-06-26 23:59:48 +0000285 default:
286 return Optional<uint64_t>();
287 }
Mike Stump1eb44332009-09-09 15:08:12 +0000288
Ted Kremenek04bc8762008-06-26 23:59:48 +0000289 return Ctx.getTypeSize(T);
290}
291
292#if 0
293static const char* GetCFNumberTypeStr(uint64_t i) {
294 static const char* Names[] = {
295 "kCFNumberSInt8Type",
296 "kCFNumberSInt16Type",
297 "kCFNumberSInt32Type",
298 "kCFNumberSInt64Type",
299 "kCFNumberFloat32Type",
300 "kCFNumberFloat64Type",
301 "kCFNumberCharType",
302 "kCFNumberShortType",
303 "kCFNumberIntType",
304 "kCFNumberLongType",
305 "kCFNumberLongLongType",
306 "kCFNumberFloatType",
307 "kCFNumberDoubleType",
308 "kCFNumberCFIndexType",
309 "kCFNumberNSIntegerType",
310 "kCFNumberCGFloatType"
311 };
Mike Stump1eb44332009-09-09 15:08:12 +0000312
Ted Kremenek04bc8762008-06-26 23:59:48 +0000313 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
314}
315#endif
316
Mike Stump1eb44332009-09-09 15:08:12 +0000317bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
Ted Kremenek5f85e172009-07-22 22:35:28 +0000318 const CallExpr* CE =
319 cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000320 const Expr* Callee = CE->getCallee();
Ted Kremenek13976632010-02-08 16:18:51 +0000321 SVal CallV = N->getState()->getSVal(Callee);
Zhongxing Xu369f4472009-04-20 05:24:46 +0000322 const FunctionDecl* FD = CallV.getAsFunctionDecl();
Ted Kremenek04bc8762008-06-26 23:59:48 +0000323
Zhongxing Xu369f4472009-04-20 05:24:46 +0000324 if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
Ted Kremenek04bc8762008-06-26 23:59:48 +0000325 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000326
Ted Kremenek04bc8762008-06-26 23:59:48 +0000327 // Get the value of the "theType" argument.
Ted Kremenek13976632010-02-08 16:18:51 +0000328 SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
Mike Stump1eb44332009-09-09 15:08:12 +0000329
Ted Kremenek04bc8762008-06-26 23:59:48 +0000330 // FIXME: We really should allow ranges of valid theType values, and
331 // bifurcate the state appropriately.
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000332 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Mike Stump1eb44332009-09-09 15:08:12 +0000333
Ted Kremenek04bc8762008-06-26 23:59:48 +0000334 if (!V)
335 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000336
Ted Kremenek04bc8762008-06-26 23:59:48 +0000337 uint64_t NumberKind = V->getValue().getLimitedValue();
338 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000339
Ted Kremenek04bc8762008-06-26 23:59:48 +0000340 // FIXME: In some cases we can emit an error.
341 if (!TargetSize.isKnown())
342 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000343
Ted Kremenek04bc8762008-06-26 23:59:48 +0000344 // Look at the value of the integer being passed by reference. Essentially
345 // we want to catch cases where the value passed in is not equal to the
346 // size of the type being created.
Ted Kremenek13976632010-02-08 16:18:51 +0000347 SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
Mike Stump1eb44332009-09-09 15:08:12 +0000348
Ted Kremenek04bc8762008-06-26 23:59:48 +0000349 // FIXME: Eventually we should handle arbitrary locations. We can do this
350 // by having an enhanced memory model that does low-level typing.
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000351 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000352
353 if (!LV)
354 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000355
Zhongxing Xu479529e2009-11-10 02:17:20 +0000356 const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
Ted Kremenek5e77eba2009-07-29 18:17:40 +0000357
358 if (!R)
359 return false;
360
Zhongxing Xua82d8aa2009-05-09 03:57:34 +0000361 QualType T = Ctx.getCanonicalType(R->getValueType(Ctx));
Mike Stump1eb44332009-09-09 15:08:12 +0000362
Ted Kremenek04bc8762008-06-26 23:59:48 +0000363 // FIXME: If the pointee isn't an integer type, should we flag a warning?
364 // People can do weird stuff with pointers.
Mike Stump1eb44332009-09-09 15:08:12 +0000365
366 if (!T->isIntegerType())
Ted Kremenek04bc8762008-06-26 23:59:48 +0000367 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000368
Ted Kremenek04bc8762008-06-26 23:59:48 +0000369 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump1eb44332009-09-09 15:08:12 +0000370
Ted Kremenek04bc8762008-06-26 23:59:48 +0000371 // CHECK: is SourceSize == TargetSize
Mike Stump1eb44332009-09-09 15:08:12 +0000372
Ted Kremenek04bc8762008-06-26 23:59:48 +0000373 if (SourceSize == TargetSize)
374 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000375
Ted Kremenek9e240492008-10-04 05:50:14 +0000376 AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000377
Ted Kremenek04bc8762008-06-26 23:59:48 +0000378 // FIXME: We can actually create an abstract "CFNumber" object that has
379 // the bits initialized to the provided values.
380 return SourceSize < TargetSize;
381}
382
Ted Kremenek5f85e172009-07-22 22:35:28 +0000383void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex,
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000384 ExplodedNode *N,
Ted Kremenek04bc8762008-06-26 23:59:48 +0000385 uint64_t SourceSize, uint64_t TargetSize,
386 uint64_t NumberKind) {
Mike Stump1eb44332009-09-09 15:08:12 +0000387
Ted Kremenekcf118d42009-02-04 23:49:09 +0000388 std::string sbuf;
389 llvm::raw_string_ostream os(sbuf);
Mike Stump1eb44332009-09-09 15:08:12 +0000390
Ted Kremenek04bc8762008-06-26 23:59:48 +0000391 os << (SourceSize == 8 ? "An " : "A ")
392 << SourceSize << " bit integer is used to initialize a CFNumber "
393 "object that represents "
394 << (TargetSize == 8 ? "an " : "a ")
Mike Stump1eb44332009-09-09 15:08:12 +0000395 << TargetSize << " bit integer. ";
Ted Kremenek04bc8762008-06-26 23:59:48 +0000396
397 if (SourceSize < TargetSize)
398 os << (TargetSize - SourceSize)
Mike Stump1eb44332009-09-09 15:08:12 +0000399 << " bits of the CFNumber value will be garbage." ;
Ted Kremenek04bc8762008-06-26 23:59:48 +0000400 else
401 os << (SourceSize - TargetSize)
402 << " bits of the input integer will be lost.";
Mike Stump1eb44332009-09-09 15:08:12 +0000403
Ted Kremenekcf118d42009-02-04 23:49:09 +0000404 // Lazily create the BugType object. This will be owned
405 // by the BugReporter object 'BR' once we call BR.EmitWarning.
406 if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate");
Benjamin Kramer4988a9a2009-11-29 18:03:28 +0000407 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
Ted Kremenekcf118d42009-02-04 23:49:09 +0000408 report->addRange(Ex->getSourceRange());
409 BR.EmitReport(report);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000410}
411
412GRSimpleAPICheck*
Mike Stump1eb44332009-09-09 15:08:12 +0000413clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000414 return new AuditCFNumberCreate(Ctx, BR);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000415}
416
Ted Kremenek78d46242008-07-22 16:21:24 +0000417//===----------------------------------------------------------------------===//
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000418// CFRetain/CFRelease auditing for null arguments.
419//===----------------------------------------------------------------------===//
420
421namespace {
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +0000422class AuditCFRetainRelease : public GRSimpleAPICheck {
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000423 APIMisuse *BT;
Mike Stump1eb44332009-09-09 15:08:12 +0000424
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000425 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
426 // it should always be passed with a call to Audit. The latter
427 // approach makes this class more stateless.
428 ASTContext& Ctx;
429 IdentifierInfo *Retain, *Release;
430 BugReporter& BR;
Mike Stump1eb44332009-09-09 15:08:12 +0000431
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000432public:
Mike Stump1eb44332009-09-09 15:08:12 +0000433 AuditCFRetainRelease(ASTContext& ctx, BugReporter& br)
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000434 : BT(0), Ctx(ctx),
435 Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")),
436 BR(br){}
Mike Stump1eb44332009-09-09 15:08:12 +0000437
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000438 ~AuditCFRetainRelease() {}
Mike Stump1eb44332009-09-09 15:08:12 +0000439
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000440 bool Audit(ExplodedNode* N, GRStateManager&);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000441};
442} // end anonymous namespace
443
444
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000445bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) {
Ted Kremenek5f85e172009-07-22 22:35:28 +0000446 const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000447
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000448 // If the CallExpr doesn't have exactly 1 argument just give up checking.
449 if (CE->getNumArgs() != 1)
450 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000451
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000452 // Check if we called CFRetain/CFRelease.
453 const GRState* state = N->getState();
Ted Kremenek13976632010-02-08 16:18:51 +0000454 SVal X = state->getSVal(CE->getCallee());
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000455 const FunctionDecl* FD = X.getAsFunctionDecl();
Mike Stump1eb44332009-09-09 15:08:12 +0000456
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000457 if (!FD)
458 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000459
460 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000461 if (!(FuncII == Retain || FuncII == Release))
462 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000463
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000464 // Finally, check if the argument is NULL.
465 // FIXME: We should be able to bifurcate the state here, as a successful
466 // check will result in the value not being NULL afterwards.
467 // FIXME: Need a way to register vistors for the BugReporter. Would like
468 // to benefit from the same diagnostics that regular null dereference
469 // reporting has.
470 if (state->getStateManager().isEqual(state, CE->getArg(0), 0)) {
471 if (!BT)
472 BT = new APIMisuse("null passed to CFRetain/CFRelease");
Mike Stump1eb44332009-09-09 15:08:12 +0000473
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000474 const char *description = (FuncII == Retain)
475 ? "Null pointer argument in call to CFRetain"
476 : "Null pointer argument in call to CFRelease";
477
478 RangedBugReport *report = new RangedBugReport(*BT, description, N);
479 report->addRange(CE->getArg(0)->getSourceRange());
480 BR.EmitReport(report);
481 return true;
482 }
483
484 return false;
485}
Mike Stump1eb44332009-09-09 15:08:12 +0000486
487
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000488GRSimpleAPICheck*
Mike Stump1eb44332009-09-09 15:08:12 +0000489clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000490 return new AuditCFRetainRelease(Ctx, BR);
491}
492
493//===----------------------------------------------------------------------===//
Ted Kremenek50e837b2009-11-20 05:27:05 +0000494// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
495//===----------------------------------------------------------------------===//
496
497namespace {
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +0000498class ClassReleaseChecker :
Ted Kremenek50e837b2009-11-20 05:27:05 +0000499 public CheckerVisitor<ClassReleaseChecker> {
500 Selector releaseS;
501 Selector retainS;
502 Selector autoreleaseS;
503 Selector drainS;
504 BugType *BT;
505public:
506 ClassReleaseChecker(ASTContext &Ctx)
507 : releaseS(GetNullarySelector("release", Ctx)),
508 retainS(GetNullarySelector("retain", Ctx)),
509 autoreleaseS(GetNullarySelector("autorelease", Ctx)),
510 drainS(GetNullarySelector("drain", Ctx)),
511 BT(0) {}
512
513 static void *getTag() { static int x = 0; return &x; }
514
515 void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);
516};
517}
518
519void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C,
520 const ObjCMessageExpr *ME) {
Douglas Gregor04badcf2010-04-21 00:45:42 +0000521 ObjCInterfaceDecl *Class = 0;
522 switch (ME->getReceiverKind()) {
523 case ObjCMessageExpr::Class:
John McCall506b57e2010-05-17 21:00:27 +0000524 Class = ME->getClassReceiver()->getAs<ObjCObjectType>()->getInterface();
Douglas Gregor04badcf2010-04-21 00:45:42 +0000525 break;
526
527 case ObjCMessageExpr::SuperClass:
John McCall506b57e2010-05-17 21:00:27 +0000528 Class = ME->getSuperType()->getAs<ObjCObjectType>()->getInterface();
Douglas Gregor04badcf2010-04-21 00:45:42 +0000529 break;
530
531 case ObjCMessageExpr::Instance:
532 case ObjCMessageExpr::SuperInstance:
Ted Kremenek50e837b2009-11-20 05:27:05 +0000533 return;
Douglas Gregor04badcf2010-04-21 00:45:42 +0000534 }
535
Ted Kremenek50e837b2009-11-20 05:27:05 +0000536 Selector S = ME->getSelector();
Benjamin Kramer921ddc42009-11-20 10:03:00 +0000537 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremenek50e837b2009-11-20 05:27:05 +0000538 return;
539
540 if (!BT)
541 BT = new APIMisuse("message incorrectly sent to class instead of class "
542 "instance");
543
Ted Kremenek19d67b52009-11-23 22:22:01 +0000544 ExplodedNode *N = C.GenerateNode();
545
Ted Kremenek50e837b2009-11-20 05:27:05 +0000546 if (!N)
547 return;
548
Ted Kremenek50e837b2009-11-20 05:27:05 +0000549 llvm::SmallString<200> buf;
550 llvm::raw_svector_ostream os(buf);
551
552 os << "The '" << S.getAsString() << "' message should be sent to instances "
Douglas Gregor04badcf2010-04-21 00:45:42 +0000553 "of class '" << Class->getName()
Ted Kremenek50e837b2009-11-20 05:27:05 +0000554 << "' and not the class directly";
555
556 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
557 report->addRange(ME->getSourceRange());
558 C.EmitReport(report);
559}
560
561//===----------------------------------------------------------------------===//
Ted Kremenek78d46242008-07-22 16:21:24 +0000562// Check registration.
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000563//===----------------------------------------------------------------------===//
Ted Kremenek78d46242008-07-22 16:21:24 +0000564
Zhongxing Xu5ab128b2009-08-21 02:18:44 +0000565void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) {
Ted Kremenek78d46242008-07-22 16:21:24 +0000566 ASTContext& Ctx = Eng.getContext();
Ted Kremenekcf118d42009-02-04 23:49:09 +0000567 BugReporter &BR = Eng.getBugReporter();
Ted Kremenek78d46242008-07-22 16:21:24 +0000568
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000569 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
Ted Kremenek78d46242008-07-22 16:21:24 +0000570 Stmt::ObjCMessageExprClass);
Mike Stump1eb44332009-09-09 15:08:12 +0000571 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000572 Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass);
Mike Stump1eb44332009-09-09 15:08:12 +0000573
Zhongxing Xu5ab128b2009-08-21 02:18:44 +0000574 RegisterNSErrorChecks(BR, Eng, D);
Ted Kremenek54cb7cc2009-11-03 08:03:59 +0000575 RegisterNSAutoreleasePoolChecks(Eng);
Ted Kremenek50e837b2009-11-20 05:27:05 +0000576 Eng.registerCheck(new ClassReleaseChecker(Ctx));
Ted Kremenek78d46242008-07-22 16:21:24 +0000577}