blob: b852e2ad9a488de06636fed5333ea65eb7e5030d [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 Kremenekd6b87082010-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 Kramerc0483222010-03-27 21:19:47 +000022#include "clang/Checker/BugReporter/BugType.h"
Ted Kremenekd6b87082010-01-25 04:41:41 +000023#include "clang/Checker/PathSensitive/MemRegion.h"
Ted Kremenekd6b87082010-01-25 04:41:41 +000024#include "clang/Checker/PathSensitive/CheckerVisitor.h"
Ted Kremenek6296e092010-01-26 22:59:55 +000025#include "clang/Checker/Checkers/LocalCheckers.h"
Daniel Dunbar6e8aa532008-08-11 05:35:13 +000026#include "clang/AST/DeclObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000027#include "clang/AST/Expr.h"
Steve Naroff021ca182008-05-29 21:12:08 +000028#include "clang/AST/ExprObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000029#include "clang/AST/ASTContext.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000030
Ted Kremenekc0414922008-03-27 07:25:52 +000031using namespace clang;
Ted Kremeneka4d60b62008-03-27 17:17:22 +000032
Steve Naroff7cae42b2009-07-10 23:34:53 +000033static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) {
Douglas Gregor9a129192010-04-21 00:45:42 +000034 QualType T;
35 switch (ME->getReceiverKind()) {
36 case ObjCMessageExpr::Instance:
37 T = ME->getInstanceReceiver()->getType();
38 break;
Mike Stump11289f42009-09-09 15:08:12 +000039
Douglas Gregor9a129192010-04-21 00:45:42 +000040 case ObjCMessageExpr::SuperInstance:
41 T = ME->getSuperType();
42 break;
Mike Stump11289f42009-09-09 15:08:12 +000043
Douglas Gregor9a129192010-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 Naroff7cae42b2009-07-10 23:34:53 +000050 return PT->getInterfaceType();
Ted Kremenekf20e2282008-04-30 22:48:21 +000051
Ted Kremenekf20e2282008-04-30 22:48:21 +000052 return NULL;
Ted Kremenek276278e2008-03-27 22:05:32 +000053}
54
Steve Naroff7cae42b2009-07-10 23:34:53 +000055static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
Daniel Dunbar2c422dc92009-10-18 20:26:12 +000056 if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME))
57 return ReceiverType->getDecl()->getIdentifier()->getNameStart();
58 return NULL;
Ted Kremenek276278e2008-03-27 22:05:32 +000059}
Ted Kremeneka4d60b62008-03-27 17:17:22 +000060
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000061namespace {
Ted Kremenek638e2802008-09-21 19:01:39 +000062
Kovarththanan Rajaratnam65c65662009-11-28 06:07:30 +000063class APIMisuse : public BugType {
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000064public:
Ted Kremenekfc5d0672009-02-04 23:49:09 +000065 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000066};
Mike Stump11289f42009-09-09 15:08:12 +000067
Kovarththanan Rajaratnam65c65662009-11-28 06:07:30 +000068class BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenekfc5d0672009-02-04 23:49:09 +000069 APIMisuse *BT;
70 BugReporter& BR;
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000071 ASTContext &Ctx;
Mike Stump11289f42009-09-09 15:08:12 +000072
Daniel Dunbar403073e2009-10-17 18:12:21 +000073 bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix);
Zhongxing Xu107f7592009-08-06 12:48:26 +000074 bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME);
Mike Stump11289f42009-09-09 15:08:12 +000075
76 void Warn(ExplodedNode* N, const Expr* E, const std::string& s);
Zhongxing Xu107f7592009-08-06 12:48:26 +000077 void WarnNilArg(ExplodedNode* N, const Expr* E);
Mike Stump11289f42009-09-09 15:08:12 +000078
Zhongxing Xu107f7592009-08-06 12:48:26 +000079 bool CheckNilArg(ExplodedNode* N, unsigned Arg);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000080
81public:
Mike Stump11289f42009-09-09 15:08:12 +000082 BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br)
Ted Kremenek095f1a92009-06-18 23:58:37 +000083 : BT(0), BR(br), Ctx(ctx) {}
Mike Stump11289f42009-09-09 15:08:12 +000084
Zhongxing Xu20227f72009-08-06 01:32:16 +000085 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump11289f42009-09-09 15:08:12 +000086
87private:
88 void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekfc5d0672009-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 Stump11289f42009-09-09 15:08:12 +000093
Ted Kremenekfc5d0672009-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 Stump11289f42009-09-09 15:08:12 +000097
Benjamin Kramerff3750f2009-11-29 18:03:28 +000098 RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
Ted Kremenekfc5d0672009-02-04 23:49:09 +000099 R->addRange(ME->getArg(Arg)->getSourceRange());
100 BR.EmitReport(R);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000101 }
102};
Mike Stump11289f42009-09-09 15:08:12 +0000103
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000104} // end anonymous namespace
105
106
107GRSimpleAPICheck*
Ted Kremenek095f1a92009-06-18 23:58:37 +0000108clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) {
Mike Stump11289f42009-09-09 15:08:12 +0000109 return new BasicObjCFoundationChecks(Ctx, BR);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000110}
111
112
113
Zhongxing Xu20227f72009-08-06 01:32:16 +0000114bool BasicObjCFoundationChecks::Audit(ExplodedNode* N,
Ted Kremenek5ab5a1b2008-08-13 04:27:00 +0000115 GRStateManager&) {
Mike Stump11289f42009-09-09 15:08:12 +0000116
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000117 const ObjCMessageExpr* ME =
Ted Kremenekc0414922008-03-27 07:25:52 +0000118 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenekc0414922008-03-27 07:25:52 +0000119
Steve Naroff7cae42b2009-07-10 23:34:53 +0000120 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
Mike Stump11289f42009-09-09 15:08:12 +0000121
Ted Kremenekc0414922008-03-27 07:25:52 +0000122 if (!ReceiverType)
Nuno Lopes652eaab2008-05-20 17:33:56 +0000123 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000124
Daniel Dunbar403073e2009-10-17 18:12:21 +0000125 if (isNSString(ReceiverType,
Daniel Dunbar07d07852009-10-18 21:17:35 +0000126 ReceiverType->getDecl()->getIdentifier()->getName()))
Ted Kremenekc0414922008-03-27 07:25:52 +0000127 return AuditNSString(N, ME);
128
Nuno Lopes652eaab2008-05-20 17:33:56 +0000129 return false;
Ted Kremenekc0414922008-03-27 07:25:52 +0000130}
131
Zhongxing Xu27f17422008-10-17 05:57:07 +0000132static inline bool isNil(SVal X) {
Mike Stump11289f42009-09-09 15:08:12 +0000133 return isa<loc::ConcreteInt>(X);
Ted Kremenek27156c82008-03-27 21:15:17 +0000134}
135
Ted Kremenekc0414922008-03-27 07:25:52 +0000136//===----------------------------------------------------------------------===//
137// Error reporting.
138//===----------------------------------------------------------------------===//
139
Zhongxing Xu107f7592009-08-06 12:48:26 +0000140bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) {
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000141 const ObjCMessageExpr* ME =
Ted Kremenek276278e2008-03-27 22:05:32 +0000142 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump11289f42009-09-09 15:08:12 +0000143
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000144 const Expr * E = ME->getArg(Arg);
Mike Stump11289f42009-09-09 15:08:12 +0000145
Ted Kremenek57f09892010-02-08 16:18:51 +0000146 if (isNil(N->getState()->getSVal(E))) {
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000147 WarnNilArg(N, ME, Arg);
Ted Kremenek276278e2008-03-27 22:05:32 +0000148 return true;
149 }
Mike Stump11289f42009-09-09 15:08:12 +0000150
Ted Kremenek276278e2008-03-27 22:05:32 +0000151 return false;
152}
153
Ted Kremenekc0414922008-03-27 07:25:52 +0000154//===----------------------------------------------------------------------===//
155// NSString checking.
156//===----------------------------------------------------------------------===//
157
Steve Naroff7cae42b2009-07-10 23:34:53 +0000158bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T,
Daniel Dunbar403073e2009-10-17 18:12:21 +0000159 llvm::StringRef ClassName) {
160 return ClassName == "NSString" || ClassName == "NSMutableString";
Ted Kremenekc0414922008-03-27 07:25:52 +0000161}
162
Mike Stump11289f42009-09-09 15:08:12 +0000163bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N,
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000164 const ObjCMessageExpr* ME) {
Mike Stump11289f42009-09-09 15:08:12 +0000165
Ted Kremenekc0414922008-03-27 07:25:52 +0000166 Selector S = ME->getSelector();
Mike Stump11289f42009-09-09 15:08:12 +0000167
Ted Kremenekc0414922008-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 Stump11289f42009-09-09 15:08:12 +0000173
Benjamin Kramer16fe0bc2010-02-08 19:51:59 +0000174 std::string NameStr = S.getAsString();
175 llvm::StringRef Name(NameStr);
176 assert(!Name.empty());
Mike Stump11289f42009-09-09 15:08:12 +0000177
Benjamin Kramer16fe0bc2010-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 Stump11289f42009-09-09 15:08:12 +0000190
Ted Kremenekc0414922008-03-27 07:25:52 +0000191 return false;
192}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000193
194//===----------------------------------------------------------------------===//
195// Error reporting.
196//===----------------------------------------------------------------------===//
197
198namespace {
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000199
Kovarththanan Rajaratnam65c65662009-11-28 06:07:30 +0000200class AuditCFNumberCreate : public GRSimpleAPICheck {
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000201 APIMisuse* BT;
Mike Stump11289f42009-09-09 15:08:12 +0000202
Ted Kremenekcf1ab192008-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 Kremenekfc5d0672009-02-04 23:49:09 +0000208 BugReporter& BR;
Ted Kremenek095f1a92009-06-18 23:58:37 +0000209
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000210public:
Mike Stump11289f42009-09-09 15:08:12 +0000211 AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
Ted Kremenek095f1a92009-06-18 23:58:37 +0000212 : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
Mike Stump11289f42009-09-09 15:08:12 +0000213
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000214 ~AuditCFNumberCreate() {}
Mike Stump11289f42009-09-09 15:08:12 +0000215
Zhongxing Xu20227f72009-08-06 01:32:16 +0000216 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump11289f42009-09-09 15:08:12 +0000217
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000218private:
Zhongxing Xu20227f72009-08-06 01:32:16 +0000219 void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N,
Mike Stump11289f42009-09-09 15:08:12 +0000220 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenekcf1ab192008-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 Stump11289f42009-09-09 15:08:12 +0000251
Ted Kremenekcf1ab192008-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 Lopescfca1f02009-12-23 17:49:57 +0000266 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump11289f42009-09-09 15:08:12 +0000267
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000268 if (i < kCFNumberCharType)
269 return FixedSize[i-1];
Mike Stump11289f42009-09-09 15:08:12 +0000270
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000271 QualType T;
Mike Stump11289f42009-09-09 15:08:12 +0000272
Ted Kremenekcf1ab192008-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 Stump11289f42009-09-09 15:08:12 +0000284 // FIXME: We need a way to map from names to Type*.
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000285 default:
286 return Optional<uint64_t>();
287 }
Mike Stump11289f42009-09-09 15:08:12 +0000288
Ted Kremenekcf1ab192008-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 Stump11289f42009-09-09 15:08:12 +0000312
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000313 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
314}
315#endif
316
Mike Stump11289f42009-09-09 15:08:12 +0000317bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000318 const CallExpr* CE =
319 cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump11289f42009-09-09 15:08:12 +0000320 const Expr* Callee = CE->getCallee();
Ted Kremenek57f09892010-02-08 16:18:51 +0000321 SVal CallV = N->getState()->getSVal(Callee);
Zhongxing Xuac129432009-04-20 05:24:46 +0000322 const FunctionDecl* FD = CallV.getAsFunctionDecl();
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000323
Zhongxing Xuac129432009-04-20 05:24:46 +0000324 if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000325 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000326
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000327 // Get the value of the "theType" argument.
Ted Kremenek57f09892010-02-08 16:18:51 +0000328 SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
Mike Stump11289f42009-09-09 15:08:12 +0000329
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000330 // FIXME: We really should allow ranges of valid theType values, and
331 // bifurcate the state appropriately.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000332 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Mike Stump11289f42009-09-09 15:08:12 +0000333
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000334 if (!V)
335 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000336
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000337 uint64_t NumberKind = V->getValue().getLimitedValue();
338 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000339
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000340 // FIXME: In some cases we can emit an error.
341 if (!TargetSize.isKnown())
342 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000343
Ted Kremenekcf1ab192008-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 Kremenek57f09892010-02-08 16:18:51 +0000347 SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
Mike Stump11289f42009-09-09 15:08:12 +0000348
Ted Kremenekcf1ab192008-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 Xu27f17422008-10-17 05:57:07 +0000351 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000352
353 if (!LV)
354 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000355
Zhongxing Xuf8f3f9d2009-11-10 02:17:20 +0000356 const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
Ted Kremenek87a7a452009-07-29 18:17:40 +0000357
358 if (!R)
359 return false;
360
Zhongxing Xu34d04b32009-05-09 03:57:34 +0000361 QualType T = Ctx.getCanonicalType(R->getValueType(Ctx));
Mike Stump11289f42009-09-09 15:08:12 +0000362
Ted Kremenekcf1ab192008-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 Stump11289f42009-09-09 15:08:12 +0000365
366 if (!T->isIntegerType())
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000367 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000368
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000369 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump11289f42009-09-09 15:08:12 +0000370
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000371 // CHECK: is SourceSize == TargetSize
Mike Stump11289f42009-09-09 15:08:12 +0000372
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000373 if (SourceSize == TargetSize)
374 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000375
Ted Kremenek5ca90a22008-10-04 05:50:14 +0000376 AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000377
Ted Kremenekcf1ab192008-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 Kremenekbfd28fd2009-07-22 22:35:28 +0000383void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex,
Zhongxing Xu20227f72009-08-06 01:32:16 +0000384 ExplodedNode *N,
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000385 uint64_t SourceSize, uint64_t TargetSize,
386 uint64_t NumberKind) {
Mike Stump11289f42009-09-09 15:08:12 +0000387
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000388 std::string sbuf;
389 llvm::raw_string_ostream os(sbuf);
Mike Stump11289f42009-09-09 15:08:12 +0000390
Ted Kremenekcf1ab192008-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 Stump11289f42009-09-09 15:08:12 +0000395 << TargetSize << " bit integer. ";
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000396
397 if (SourceSize < TargetSize)
398 os << (TargetSize - SourceSize)
Mike Stump11289f42009-09-09 15:08:12 +0000399 << " bits of the CFNumber value will be garbage." ;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000400 else
401 os << (SourceSize - TargetSize)
402 << " bits of the input integer will be lost.";
Mike Stump11289f42009-09-09 15:08:12 +0000403
Ted Kremenekfc5d0672009-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 Kramerff3750f2009-11-29 18:03:28 +0000407 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000408 report->addRange(Ex->getSourceRange());
409 BR.EmitReport(report);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000410}
411
412GRSimpleAPICheck*
Mike Stump11289f42009-09-09 15:08:12 +0000413clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek095f1a92009-06-18 23:58:37 +0000414 return new AuditCFNumberCreate(Ctx, BR);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000415}
416
Ted Kremenek1f352db2008-07-22 16:21:24 +0000417//===----------------------------------------------------------------------===//
Ted Kremenekc057f412009-07-14 00:43:42 +0000418// CFRetain/CFRelease auditing for null arguments.
419//===----------------------------------------------------------------------===//
420
421namespace {
Kovarththanan Rajaratnam65c65662009-11-28 06:07:30 +0000422class AuditCFRetainRelease : public GRSimpleAPICheck {
Ted Kremenekc057f412009-07-14 00:43:42 +0000423 APIMisuse *BT;
Mike Stump11289f42009-09-09 15:08:12 +0000424
Ted Kremenekc057f412009-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 Stump11289f42009-09-09 15:08:12 +0000431
Ted Kremenekc057f412009-07-14 00:43:42 +0000432public:
Mike Stump11289f42009-09-09 15:08:12 +0000433 AuditCFRetainRelease(ASTContext& ctx, BugReporter& br)
Ted Kremenekc057f412009-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 Stump11289f42009-09-09 15:08:12 +0000437
Ted Kremenekc057f412009-07-14 00:43:42 +0000438 ~AuditCFRetainRelease() {}
Mike Stump11289f42009-09-09 15:08:12 +0000439
Zhongxing Xu20227f72009-08-06 01:32:16 +0000440 bool Audit(ExplodedNode* N, GRStateManager&);
Ted Kremenekc057f412009-07-14 00:43:42 +0000441};
442} // end anonymous namespace
443
444
Zhongxing Xu20227f72009-08-06 01:32:16 +0000445bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) {
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000446 const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump11289f42009-09-09 15:08:12 +0000447
Ted Kremenekc057f412009-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 Stump11289f42009-09-09 15:08:12 +0000451
Ted Kremenekc057f412009-07-14 00:43:42 +0000452 // Check if we called CFRetain/CFRelease.
453 const GRState* state = N->getState();
Ted Kremenek57f09892010-02-08 16:18:51 +0000454 SVal X = state->getSVal(CE->getCallee());
Ted Kremenekc057f412009-07-14 00:43:42 +0000455 const FunctionDecl* FD = X.getAsFunctionDecl();
Mike Stump11289f42009-09-09 15:08:12 +0000456
Ted Kremenekc057f412009-07-14 00:43:42 +0000457 if (!FD)
458 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000459
460 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenekc057f412009-07-14 00:43:42 +0000461 if (!(FuncII == Retain || FuncII == Release))
462 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000463
Ted Kremenekc057f412009-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 Stump11289f42009-09-09 15:08:12 +0000473
Ted Kremenekc057f412009-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 Stump11289f42009-09-09 15:08:12 +0000486
487
Ted Kremenekc057f412009-07-14 00:43:42 +0000488GRSimpleAPICheck*
Mike Stump11289f42009-09-09 15:08:12 +0000489clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenekc057f412009-07-14 00:43:42 +0000490 return new AuditCFRetainRelease(Ctx, BR);
491}
492
493//===----------------------------------------------------------------------===//
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000494// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
495//===----------------------------------------------------------------------===//
496
497namespace {
Kovarththanan Rajaratnam65c65662009-11-28 06:07:30 +0000498class ClassReleaseChecker :
Ted Kremeneka4f7c182009-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 Gregor9a129192010-04-21 00:45:42 +0000521 ObjCInterfaceDecl *Class = 0;
522 switch (ME->getReceiverKind()) {
523 case ObjCMessageExpr::Class:
John McCall96fa4842010-05-17 21:00:27 +0000524 Class = ME->getClassReceiver()->getAs<ObjCObjectType>()->getInterface();
Douglas Gregor9a129192010-04-21 00:45:42 +0000525 break;
526
527 case ObjCMessageExpr::SuperClass:
John McCall96fa4842010-05-17 21:00:27 +0000528 Class = ME->getSuperType()->getAs<ObjCObjectType>()->getInterface();
Douglas Gregor9a129192010-04-21 00:45:42 +0000529 break;
530
531 case ObjCMessageExpr::Instance:
532 case ObjCMessageExpr::SuperInstance:
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000533 return;
Douglas Gregor9a129192010-04-21 00:45:42 +0000534 }
535
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000536 Selector S = ME->getSelector();
Benjamin Kramer7d875c72009-11-20 10:03:00 +0000537 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremeneka4f7c182009-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 Kremenekf5735152009-11-23 22:22:01 +0000544 ExplodedNode *N = C.GenerateNode();
545
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000546 if (!N)
547 return;
548
Ted Kremeneka4f7c182009-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 Gregor9a129192010-04-21 00:45:42 +0000553 "of class '" << Class->getName()
Ted Kremeneka4f7c182009-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 Kremenek1f352db2008-07-22 16:21:24 +0000562// Check registration.
Ted Kremenekc057f412009-07-14 00:43:42 +0000563//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000564
Zhongxing Xu6be1a4e2009-08-21 02:18:44 +0000565void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) {
Ted Kremenek1f352db2008-07-22 16:21:24 +0000566 ASTContext& Ctx = Eng.getContext();
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000567 BugReporter &BR = Eng.getBugReporter();
Ted Kremenek1f352db2008-07-22 16:21:24 +0000568
Ted Kremenek095f1a92009-06-18 23:58:37 +0000569 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
Ted Kremenek1f352db2008-07-22 16:21:24 +0000570 Stmt::ObjCMessageExprClass);
Mike Stump11289f42009-09-09 15:08:12 +0000571 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
Ted Kremenekc057f412009-07-14 00:43:42 +0000572 Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass);
Mike Stump11289f42009-09-09 15:08:12 +0000573
Zhongxing Xu6be1a4e2009-08-21 02:18:44 +0000574 RegisterNSErrorChecks(BR, Eng, D);
Ted Kremenek18c7cee2009-11-03 08:03:59 +0000575 RegisterNSAutoreleasePoolChecks(Eng);
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000576 Eng.registerCheck(new ClassReleaseChecker(Ctx));
Ted Kremenek1f352db2008-07-22 16:21:24 +0000577}