blob: 810d0fbb997a2545d7da0c78064753cf803f7c9f [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) {
34 const Expr* Receiver = ME->getReceiver();
Mike Stump11289f42009-09-09 15:08:12 +000035
Ted Kremenek276278e2008-03-27 22:05:32 +000036 if (!Receiver)
37 return NULL;
Mike Stump11289f42009-09-09 15:08:12 +000038
Steve Naroff7cae42b2009-07-10 23:34:53 +000039 if (const ObjCObjectPointerType *PT =
John McCall9dd450b2009-09-21 23:43:11 +000040 Receiver->getType()->getAs<ObjCObjectPointerType>())
Steve Naroff7cae42b2009-07-10 23:34:53 +000041 return PT->getInterfaceType();
Ted Kremenekf20e2282008-04-30 22:48:21 +000042
Ted Kremenekf20e2282008-04-30 22:48:21 +000043 return NULL;
Ted Kremenek276278e2008-03-27 22:05:32 +000044}
45
Steve Naroff7cae42b2009-07-10 23:34:53 +000046static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
Daniel Dunbar2c422dc92009-10-18 20:26:12 +000047 if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME))
48 return ReceiverType->getDecl()->getIdentifier()->getNameStart();
49 return NULL;
Ted Kremenek276278e2008-03-27 22:05:32 +000050}
Ted Kremeneka4d60b62008-03-27 17:17:22 +000051
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000052namespace {
Ted Kremenek638e2802008-09-21 19:01:39 +000053
Kovarththanan Rajaratnam65c65662009-11-28 06:07:30 +000054class APIMisuse : public BugType {
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000055public:
Ted Kremenekfc5d0672009-02-04 23:49:09 +000056 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000057};
Mike Stump11289f42009-09-09 15:08:12 +000058
Kovarththanan Rajaratnam65c65662009-11-28 06:07:30 +000059class BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenekfc5d0672009-02-04 23:49:09 +000060 APIMisuse *BT;
61 BugReporter& BR;
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000062 ASTContext &Ctx;
Mike Stump11289f42009-09-09 15:08:12 +000063
Daniel Dunbar403073e2009-10-17 18:12:21 +000064 bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix);
Zhongxing Xu107f7592009-08-06 12:48:26 +000065 bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME);
Mike Stump11289f42009-09-09 15:08:12 +000066
67 void Warn(ExplodedNode* N, const Expr* E, const std::string& s);
Zhongxing Xu107f7592009-08-06 12:48:26 +000068 void WarnNilArg(ExplodedNode* N, const Expr* E);
Mike Stump11289f42009-09-09 15:08:12 +000069
Zhongxing Xu107f7592009-08-06 12:48:26 +000070 bool CheckNilArg(ExplodedNode* N, unsigned Arg);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000071
72public:
Mike Stump11289f42009-09-09 15:08:12 +000073 BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br)
Ted Kremenek095f1a92009-06-18 23:58:37 +000074 : BT(0), BR(br), Ctx(ctx) {}
Mike Stump11289f42009-09-09 15:08:12 +000075
Zhongxing Xu20227f72009-08-06 01:32:16 +000076 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump11289f42009-09-09 15:08:12 +000077
78private:
79 void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekfc5d0672009-02-04 23:49:09 +000080 std::string sbuf;
81 llvm::raw_string_ostream os(sbuf);
82 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
83 << ME->getSelector().getAsString() << "' cannot be nil.";
Mike Stump11289f42009-09-09 15:08:12 +000084
Ted Kremenekfc5d0672009-02-04 23:49:09 +000085 // Lazily create the BugType object for NilArg. This will be owned
86 // by the BugReporter object 'BR' once we call BR.EmitWarning.
87 if (!BT) BT = new APIMisuse("nil argument");
Mike Stump11289f42009-09-09 15:08:12 +000088
Benjamin Kramerff3750f2009-11-29 18:03:28 +000089 RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
Ted Kremenekfc5d0672009-02-04 23:49:09 +000090 R->addRange(ME->getArg(Arg)->getSourceRange());
91 BR.EmitReport(R);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000092 }
93};
Mike Stump11289f42009-09-09 15:08:12 +000094
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000095} // end anonymous namespace
96
97
98GRSimpleAPICheck*
Ted Kremenek095f1a92009-06-18 23:58:37 +000099clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) {
Mike Stump11289f42009-09-09 15:08:12 +0000100 return new BasicObjCFoundationChecks(Ctx, BR);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000101}
102
103
104
Zhongxing Xu20227f72009-08-06 01:32:16 +0000105bool BasicObjCFoundationChecks::Audit(ExplodedNode* N,
Ted Kremenek5ab5a1b2008-08-13 04:27:00 +0000106 GRStateManager&) {
Mike Stump11289f42009-09-09 15:08:12 +0000107
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000108 const ObjCMessageExpr* ME =
Ted Kremenekc0414922008-03-27 07:25:52 +0000109 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenekc0414922008-03-27 07:25:52 +0000110
Steve Naroff7cae42b2009-07-10 23:34:53 +0000111 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
Mike Stump11289f42009-09-09 15:08:12 +0000112
Ted Kremenekc0414922008-03-27 07:25:52 +0000113 if (!ReceiverType)
Nuno Lopes652eaab2008-05-20 17:33:56 +0000114 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000115
Daniel Dunbar403073e2009-10-17 18:12:21 +0000116 if (isNSString(ReceiverType,
Daniel Dunbar07d07852009-10-18 21:17:35 +0000117 ReceiverType->getDecl()->getIdentifier()->getName()))
Ted Kremenekc0414922008-03-27 07:25:52 +0000118 return AuditNSString(N, ME);
119
Nuno Lopes652eaab2008-05-20 17:33:56 +0000120 return false;
Ted Kremenekc0414922008-03-27 07:25:52 +0000121}
122
Zhongxing Xu27f17422008-10-17 05:57:07 +0000123static inline bool isNil(SVal X) {
Mike Stump11289f42009-09-09 15:08:12 +0000124 return isa<loc::ConcreteInt>(X);
Ted Kremenek27156c82008-03-27 21:15:17 +0000125}
126
Ted Kremenekc0414922008-03-27 07:25:52 +0000127//===----------------------------------------------------------------------===//
128// Error reporting.
129//===----------------------------------------------------------------------===//
130
Zhongxing Xu107f7592009-08-06 12:48:26 +0000131bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) {
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000132 const ObjCMessageExpr* ME =
Ted Kremenek276278e2008-03-27 22:05:32 +0000133 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump11289f42009-09-09 15:08:12 +0000134
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000135 const Expr * E = ME->getArg(Arg);
Mike Stump11289f42009-09-09 15:08:12 +0000136
Ted Kremenek57f09892010-02-08 16:18:51 +0000137 if (isNil(N->getState()->getSVal(E))) {
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000138 WarnNilArg(N, ME, Arg);
Ted Kremenek276278e2008-03-27 22:05:32 +0000139 return true;
140 }
Mike Stump11289f42009-09-09 15:08:12 +0000141
Ted Kremenek276278e2008-03-27 22:05:32 +0000142 return false;
143}
144
Ted Kremenekc0414922008-03-27 07:25:52 +0000145//===----------------------------------------------------------------------===//
146// NSString checking.
147//===----------------------------------------------------------------------===//
148
Steve Naroff7cae42b2009-07-10 23:34:53 +0000149bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T,
Daniel Dunbar403073e2009-10-17 18:12:21 +0000150 llvm::StringRef ClassName) {
151 return ClassName == "NSString" || ClassName == "NSMutableString";
Ted Kremenekc0414922008-03-27 07:25:52 +0000152}
153
Mike Stump11289f42009-09-09 15:08:12 +0000154bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N,
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000155 const ObjCMessageExpr* ME) {
Mike Stump11289f42009-09-09 15:08:12 +0000156
Ted Kremenekc0414922008-03-27 07:25:52 +0000157 Selector S = ME->getSelector();
Mike Stump11289f42009-09-09 15:08:12 +0000158
Ted Kremenekc0414922008-03-27 07:25:52 +0000159 if (S.isUnarySelector())
160 return false;
161
162 // FIXME: This is going to be really slow doing these checks with
163 // lexical comparisons.
Mike Stump11289f42009-09-09 15:08:12 +0000164
Benjamin Kramer16fe0bc2010-02-08 19:51:59 +0000165 std::string NameStr = S.getAsString();
166 llvm::StringRef Name(NameStr);
167 assert(!Name.empty());
Mike Stump11289f42009-09-09 15:08:12 +0000168
Benjamin Kramer16fe0bc2010-02-08 19:51:59 +0000169 // FIXME: Checking for initWithFormat: will not work in most cases
170 // yet because [NSString alloc] returns id, not NSString*. We will
171 // need support for tracking expected-type information in the analyzer
172 // to find these errors.
173 if (Name == "caseInsensitiveCompare:" ||
174 Name == "compare:" ||
175 Name == "compare:options:" ||
176 Name == "compare:options:range:" ||
177 Name == "compare:options:range:locale:" ||
178 Name == "componentsSeparatedByCharactersInSet:" ||
179 Name == "initWithFormat:")
180 return CheckNilArg(N, 0);
Mike Stump11289f42009-09-09 15:08:12 +0000181
Ted Kremenekc0414922008-03-27 07:25:52 +0000182 return false;
183}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000184
185//===----------------------------------------------------------------------===//
186// Error reporting.
187//===----------------------------------------------------------------------===//
188
189namespace {
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000190
Kovarththanan Rajaratnam65c65662009-11-28 06:07:30 +0000191class AuditCFNumberCreate : public GRSimpleAPICheck {
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000192 APIMisuse* BT;
Mike Stump11289f42009-09-09 15:08:12 +0000193
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000194 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
195 // it should always be passed with a call to Audit. The latter
196 // approach makes this class more stateless.
197 ASTContext& Ctx;
198 IdentifierInfo* II;
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000199 BugReporter& BR;
Ted Kremenek095f1a92009-06-18 23:58:37 +0000200
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000201public:
Mike Stump11289f42009-09-09 15:08:12 +0000202 AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
Ted Kremenek095f1a92009-06-18 23:58:37 +0000203 : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
Mike Stump11289f42009-09-09 15:08:12 +0000204
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000205 ~AuditCFNumberCreate() {}
Mike Stump11289f42009-09-09 15:08:12 +0000206
Zhongxing Xu20227f72009-08-06 01:32:16 +0000207 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump11289f42009-09-09 15:08:12 +0000208
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000209private:
Zhongxing Xu20227f72009-08-06 01:32:16 +0000210 void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N,
Mike Stump11289f42009-09-09 15:08:12 +0000211 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000212};
213} // end anonymous namespace
214
215enum CFNumberType {
216 kCFNumberSInt8Type = 1,
217 kCFNumberSInt16Type = 2,
218 kCFNumberSInt32Type = 3,
219 kCFNumberSInt64Type = 4,
220 kCFNumberFloat32Type = 5,
221 kCFNumberFloat64Type = 6,
222 kCFNumberCharType = 7,
223 kCFNumberShortType = 8,
224 kCFNumberIntType = 9,
225 kCFNumberLongType = 10,
226 kCFNumberLongLongType = 11,
227 kCFNumberFloatType = 12,
228 kCFNumberDoubleType = 13,
229 kCFNumberCFIndexType = 14,
230 kCFNumberNSIntegerType = 15,
231 kCFNumberCGFloatType = 16
232};
233
234namespace {
235 template<typename T>
236 class Optional {
237 bool IsKnown;
238 T Val;
239 public:
240 Optional() : IsKnown(false), Val(0) {}
241 Optional(const T& val) : IsKnown(true), Val(val) {}
Mike Stump11289f42009-09-09 15:08:12 +0000242
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000243 bool isKnown() const { return IsKnown; }
244
245 const T& getValue() const {
246 assert (isKnown());
247 return Val;
248 }
249
250 operator const T&() const {
251 return getValue();
252 }
253 };
254}
255
256static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
Nuno Lopescfca1f02009-12-23 17:49:57 +0000257 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump11289f42009-09-09 15:08:12 +0000258
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000259 if (i < kCFNumberCharType)
260 return FixedSize[i-1];
Mike Stump11289f42009-09-09 15:08:12 +0000261
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000262 QualType T;
Mike Stump11289f42009-09-09 15:08:12 +0000263
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000264 switch (i) {
265 case kCFNumberCharType: T = Ctx.CharTy; break;
266 case kCFNumberShortType: T = Ctx.ShortTy; break;
267 case kCFNumberIntType: T = Ctx.IntTy; break;
268 case kCFNumberLongType: T = Ctx.LongTy; break;
269 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
270 case kCFNumberFloatType: T = Ctx.FloatTy; break;
271 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
272 case kCFNumberCFIndexType:
273 case kCFNumberNSIntegerType:
274 case kCFNumberCGFloatType:
Mike Stump11289f42009-09-09 15:08:12 +0000275 // FIXME: We need a way to map from names to Type*.
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000276 default:
277 return Optional<uint64_t>();
278 }
Mike Stump11289f42009-09-09 15:08:12 +0000279
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000280 return Ctx.getTypeSize(T);
281}
282
283#if 0
284static const char* GetCFNumberTypeStr(uint64_t i) {
285 static const char* Names[] = {
286 "kCFNumberSInt8Type",
287 "kCFNumberSInt16Type",
288 "kCFNumberSInt32Type",
289 "kCFNumberSInt64Type",
290 "kCFNumberFloat32Type",
291 "kCFNumberFloat64Type",
292 "kCFNumberCharType",
293 "kCFNumberShortType",
294 "kCFNumberIntType",
295 "kCFNumberLongType",
296 "kCFNumberLongLongType",
297 "kCFNumberFloatType",
298 "kCFNumberDoubleType",
299 "kCFNumberCFIndexType",
300 "kCFNumberNSIntegerType",
301 "kCFNumberCGFloatType"
302 };
Mike Stump11289f42009-09-09 15:08:12 +0000303
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000304 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
305}
306#endif
307
Mike Stump11289f42009-09-09 15:08:12 +0000308bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000309 const CallExpr* CE =
310 cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump11289f42009-09-09 15:08:12 +0000311 const Expr* Callee = CE->getCallee();
Ted Kremenek57f09892010-02-08 16:18:51 +0000312 SVal CallV = N->getState()->getSVal(Callee);
Zhongxing Xuac129432009-04-20 05:24:46 +0000313 const FunctionDecl* FD = CallV.getAsFunctionDecl();
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000314
Zhongxing Xuac129432009-04-20 05:24:46 +0000315 if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000316 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000317
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000318 // Get the value of the "theType" argument.
Ted Kremenek57f09892010-02-08 16:18:51 +0000319 SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
Mike Stump11289f42009-09-09 15:08:12 +0000320
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000321 // FIXME: We really should allow ranges of valid theType values, and
322 // bifurcate the state appropriately.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000323 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Mike Stump11289f42009-09-09 15:08:12 +0000324
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000325 if (!V)
326 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000327
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000328 uint64_t NumberKind = V->getValue().getLimitedValue();
329 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000330
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000331 // FIXME: In some cases we can emit an error.
332 if (!TargetSize.isKnown())
333 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000334
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000335 // Look at the value of the integer being passed by reference. Essentially
336 // we want to catch cases where the value passed in is not equal to the
337 // size of the type being created.
Ted Kremenek57f09892010-02-08 16:18:51 +0000338 SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
Mike Stump11289f42009-09-09 15:08:12 +0000339
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000340 // FIXME: Eventually we should handle arbitrary locations. We can do this
341 // by having an enhanced memory model that does low-level typing.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000342 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000343
344 if (!LV)
345 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000346
Zhongxing Xuf8f3f9d2009-11-10 02:17:20 +0000347 const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
Ted Kremenek87a7a452009-07-29 18:17:40 +0000348
349 if (!R)
350 return false;
351
Zhongxing Xu34d04b32009-05-09 03:57:34 +0000352 QualType T = Ctx.getCanonicalType(R->getValueType(Ctx));
Mike Stump11289f42009-09-09 15:08:12 +0000353
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000354 // FIXME: If the pointee isn't an integer type, should we flag a warning?
355 // People can do weird stuff with pointers.
Mike Stump11289f42009-09-09 15:08:12 +0000356
357 if (!T->isIntegerType())
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000358 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000359
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000360 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump11289f42009-09-09 15:08:12 +0000361
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000362 // CHECK: is SourceSize == TargetSize
Mike Stump11289f42009-09-09 15:08:12 +0000363
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000364 if (SourceSize == TargetSize)
365 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000366
Ted Kremenek5ca90a22008-10-04 05:50:14 +0000367 AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000368
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000369 // FIXME: We can actually create an abstract "CFNumber" object that has
370 // the bits initialized to the provided values.
371 return SourceSize < TargetSize;
372}
373
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000374void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex,
Zhongxing Xu20227f72009-08-06 01:32:16 +0000375 ExplodedNode *N,
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000376 uint64_t SourceSize, uint64_t TargetSize,
377 uint64_t NumberKind) {
Mike Stump11289f42009-09-09 15:08:12 +0000378
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000379 std::string sbuf;
380 llvm::raw_string_ostream os(sbuf);
Mike Stump11289f42009-09-09 15:08:12 +0000381
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000382 os << (SourceSize == 8 ? "An " : "A ")
383 << SourceSize << " bit integer is used to initialize a CFNumber "
384 "object that represents "
385 << (TargetSize == 8 ? "an " : "a ")
Mike Stump11289f42009-09-09 15:08:12 +0000386 << TargetSize << " bit integer. ";
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000387
388 if (SourceSize < TargetSize)
389 os << (TargetSize - SourceSize)
Mike Stump11289f42009-09-09 15:08:12 +0000390 << " bits of the CFNumber value will be garbage." ;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000391 else
392 os << (SourceSize - TargetSize)
393 << " bits of the input integer will be lost.";
Mike Stump11289f42009-09-09 15:08:12 +0000394
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000395 // Lazily create the BugType object. This will be owned
396 // by the BugReporter object 'BR' once we call BR.EmitWarning.
397 if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate");
Benjamin Kramerff3750f2009-11-29 18:03:28 +0000398 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000399 report->addRange(Ex->getSourceRange());
400 BR.EmitReport(report);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000401}
402
403GRSimpleAPICheck*
Mike Stump11289f42009-09-09 15:08:12 +0000404clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek095f1a92009-06-18 23:58:37 +0000405 return new AuditCFNumberCreate(Ctx, BR);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000406}
407
Ted Kremenek1f352db2008-07-22 16:21:24 +0000408//===----------------------------------------------------------------------===//
Ted Kremenekc057f412009-07-14 00:43:42 +0000409// CFRetain/CFRelease auditing for null arguments.
410//===----------------------------------------------------------------------===//
411
412namespace {
Kovarththanan Rajaratnam65c65662009-11-28 06:07:30 +0000413class AuditCFRetainRelease : public GRSimpleAPICheck {
Ted Kremenekc057f412009-07-14 00:43:42 +0000414 APIMisuse *BT;
Mike Stump11289f42009-09-09 15:08:12 +0000415
Ted Kremenekc057f412009-07-14 00:43:42 +0000416 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
417 // it should always be passed with a call to Audit. The latter
418 // approach makes this class more stateless.
419 ASTContext& Ctx;
420 IdentifierInfo *Retain, *Release;
421 BugReporter& BR;
Mike Stump11289f42009-09-09 15:08:12 +0000422
Ted Kremenekc057f412009-07-14 00:43:42 +0000423public:
Mike Stump11289f42009-09-09 15:08:12 +0000424 AuditCFRetainRelease(ASTContext& ctx, BugReporter& br)
Ted Kremenekc057f412009-07-14 00:43:42 +0000425 : BT(0), Ctx(ctx),
426 Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")),
427 BR(br){}
Mike Stump11289f42009-09-09 15:08:12 +0000428
Ted Kremenekc057f412009-07-14 00:43:42 +0000429 ~AuditCFRetainRelease() {}
Mike Stump11289f42009-09-09 15:08:12 +0000430
Zhongxing Xu20227f72009-08-06 01:32:16 +0000431 bool Audit(ExplodedNode* N, GRStateManager&);
Ted Kremenekc057f412009-07-14 00:43:42 +0000432};
433} // end anonymous namespace
434
435
Zhongxing Xu20227f72009-08-06 01:32:16 +0000436bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) {
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000437 const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump11289f42009-09-09 15:08:12 +0000438
Ted Kremenekc057f412009-07-14 00:43:42 +0000439 // If the CallExpr doesn't have exactly 1 argument just give up checking.
440 if (CE->getNumArgs() != 1)
441 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000442
Ted Kremenekc057f412009-07-14 00:43:42 +0000443 // Check if we called CFRetain/CFRelease.
444 const GRState* state = N->getState();
Ted Kremenek57f09892010-02-08 16:18:51 +0000445 SVal X = state->getSVal(CE->getCallee());
Ted Kremenekc057f412009-07-14 00:43:42 +0000446 const FunctionDecl* FD = X.getAsFunctionDecl();
Mike Stump11289f42009-09-09 15:08:12 +0000447
Ted Kremenekc057f412009-07-14 00:43:42 +0000448 if (!FD)
449 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000450
451 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenekc057f412009-07-14 00:43:42 +0000452 if (!(FuncII == Retain || FuncII == Release))
453 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000454
Ted Kremenekc057f412009-07-14 00:43:42 +0000455 // Finally, check if the argument is NULL.
456 // FIXME: We should be able to bifurcate the state here, as a successful
457 // check will result in the value not being NULL afterwards.
458 // FIXME: Need a way to register vistors for the BugReporter. Would like
459 // to benefit from the same diagnostics that regular null dereference
460 // reporting has.
461 if (state->getStateManager().isEqual(state, CE->getArg(0), 0)) {
462 if (!BT)
463 BT = new APIMisuse("null passed to CFRetain/CFRelease");
Mike Stump11289f42009-09-09 15:08:12 +0000464
Ted Kremenekc057f412009-07-14 00:43:42 +0000465 const char *description = (FuncII == Retain)
466 ? "Null pointer argument in call to CFRetain"
467 : "Null pointer argument in call to CFRelease";
468
469 RangedBugReport *report = new RangedBugReport(*BT, description, N);
470 report->addRange(CE->getArg(0)->getSourceRange());
471 BR.EmitReport(report);
472 return true;
473 }
474
475 return false;
476}
Mike Stump11289f42009-09-09 15:08:12 +0000477
478
Ted Kremenekc057f412009-07-14 00:43:42 +0000479GRSimpleAPICheck*
Mike Stump11289f42009-09-09 15:08:12 +0000480clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenekc057f412009-07-14 00:43:42 +0000481 return new AuditCFRetainRelease(Ctx, BR);
482}
483
484//===----------------------------------------------------------------------===//
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000485// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
486//===----------------------------------------------------------------------===//
487
488namespace {
Kovarththanan Rajaratnam65c65662009-11-28 06:07:30 +0000489class ClassReleaseChecker :
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000490 public CheckerVisitor<ClassReleaseChecker> {
491 Selector releaseS;
492 Selector retainS;
493 Selector autoreleaseS;
494 Selector drainS;
495 BugType *BT;
496public:
497 ClassReleaseChecker(ASTContext &Ctx)
498 : releaseS(GetNullarySelector("release", Ctx)),
499 retainS(GetNullarySelector("retain", Ctx)),
500 autoreleaseS(GetNullarySelector("autorelease", Ctx)),
501 drainS(GetNullarySelector("drain", Ctx)),
502 BT(0) {}
503
504 static void *getTag() { static int x = 0; return &x; }
505
506 void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);
507};
508}
509
510void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C,
511 const ObjCMessageExpr *ME) {
512
513 const IdentifierInfo *ClsName = ME->getClassName();
514 if (!ClsName)
515 return;
516
517 Selector S = ME->getSelector();
Benjamin Kramer7d875c72009-11-20 10:03:00 +0000518 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000519 return;
520
521 if (!BT)
522 BT = new APIMisuse("message incorrectly sent to class instead of class "
523 "instance");
524
Ted Kremenekf5735152009-11-23 22:22:01 +0000525 ExplodedNode *N = C.GenerateNode();
526
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000527 if (!N)
528 return;
529
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000530 llvm::SmallString<200> buf;
531 llvm::raw_svector_ostream os(buf);
532
533 os << "The '" << S.getAsString() << "' message should be sent to instances "
534 "of class '" << ClsName->getName()
535 << "' and not the class directly";
536
537 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
538 report->addRange(ME->getSourceRange());
539 C.EmitReport(report);
540}
541
542//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000543// Check registration.
Ted Kremenekc057f412009-07-14 00:43:42 +0000544//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000545
Zhongxing Xu6be1a4e2009-08-21 02:18:44 +0000546void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) {
Ted Kremenek1f352db2008-07-22 16:21:24 +0000547 ASTContext& Ctx = Eng.getContext();
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000548 BugReporter &BR = Eng.getBugReporter();
Ted Kremenek1f352db2008-07-22 16:21:24 +0000549
Ted Kremenek095f1a92009-06-18 23:58:37 +0000550 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
Ted Kremenek1f352db2008-07-22 16:21:24 +0000551 Stmt::ObjCMessageExprClass);
Mike Stump11289f42009-09-09 15:08:12 +0000552 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
Ted Kremenekc057f412009-07-14 00:43:42 +0000553 Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass);
Mike Stump11289f42009-09-09 15:08:12 +0000554
Zhongxing Xu6be1a4e2009-08-21 02:18:44 +0000555 RegisterNSErrorChecks(BR, Eng, D);
Ted Kremenek18c7cee2009-11-03 08:03:59 +0000556 RegisterNSAutoreleasePoolChecks(Eng);
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000557 Eng.registerCheck(new ClassReleaseChecker(Ctx));
Ted Kremenek1f352db2008-07-22 16:21:24 +0000558}