blob: d6c09a2e04a62d9115566458a8f76793b208dd76 [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"
Ted Kremenekfe0fc402010-01-25 17:10:22 +000022#include "clang/Checker/BugReporter/BugReporter.h"
Ted Kremenekd6b87082010-01-25 04:41:41 +000023#include "clang/Checker/PathSensitive/MemRegion.h"
Ted Kremenekfe0fc402010-01-25 17:10:22 +000024#include "clang/Checker/BugReporter/PathDiagnostic.h"
Ted Kremenekd6b87082010-01-25 04:41:41 +000025#include "clang/Checker/PathSensitive/CheckerVisitor.h"
Ted Kremenek6296e092010-01-26 22:59:55 +000026#include "clang/Checker/Checkers/LocalCheckers.h"
Daniel Dunbar6e8aa532008-08-11 05:35:13 +000027#include "clang/AST/DeclObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000028#include "clang/AST/Expr.h"
Steve Naroff021ca182008-05-29 21:12:08 +000029#include "clang/AST/ExprObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000030#include "clang/AST/ASTContext.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000031
Ted Kremenekc0414922008-03-27 07:25:52 +000032using namespace clang;
Ted Kremeneka4d60b62008-03-27 17:17:22 +000033
Steve Naroff7cae42b2009-07-10 23:34:53 +000034static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) {
35 const Expr* Receiver = ME->getReceiver();
Mike Stump11289f42009-09-09 15:08:12 +000036
Ted Kremenek276278e2008-03-27 22:05:32 +000037 if (!Receiver)
38 return NULL;
Mike Stump11289f42009-09-09 15:08:12 +000039
Steve Naroff7cae42b2009-07-10 23:34:53 +000040 if (const ObjCObjectPointerType *PT =
John McCall9dd450b2009-09-21 23:43:11 +000041 Receiver->getType()->getAs<ObjCObjectPointerType>())
Steve Naroff7cae42b2009-07-10 23:34:53 +000042 return PT->getInterfaceType();
Ted Kremenekf20e2282008-04-30 22:48:21 +000043
Ted Kremenekf20e2282008-04-30 22:48:21 +000044 return NULL;
Ted Kremenek276278e2008-03-27 22:05:32 +000045}
46
Steve Naroff7cae42b2009-07-10 23:34:53 +000047static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
Daniel Dunbar2c422dc92009-10-18 20:26:12 +000048 if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME))
49 return ReceiverType->getDecl()->getIdentifier()->getNameStart();
50 return NULL;
Ted Kremenek276278e2008-03-27 22:05:32 +000051}
Ted Kremeneka4d60b62008-03-27 17:17:22 +000052
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000053namespace {
Ted Kremenek638e2802008-09-21 19:01:39 +000054
Kovarththanan Rajaratnam65c65662009-11-28 06:07:30 +000055class APIMisuse : public BugType {
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000056public:
Ted Kremenekfc5d0672009-02-04 23:49:09 +000057 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000058};
Mike Stump11289f42009-09-09 15:08:12 +000059
Kovarththanan Rajaratnam65c65662009-11-28 06:07:30 +000060class BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenekfc5d0672009-02-04 23:49:09 +000061 APIMisuse *BT;
62 BugReporter& BR;
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000063 ASTContext &Ctx;
Mike Stump11289f42009-09-09 15:08:12 +000064
Daniel Dunbar403073e2009-10-17 18:12:21 +000065 bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix);
Zhongxing Xu107f7592009-08-06 12:48:26 +000066 bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME);
Mike Stump11289f42009-09-09 15:08:12 +000067
68 void Warn(ExplodedNode* N, const Expr* E, const std::string& s);
Zhongxing Xu107f7592009-08-06 12:48:26 +000069 void WarnNilArg(ExplodedNode* N, const Expr* E);
Mike Stump11289f42009-09-09 15:08:12 +000070
Zhongxing Xu107f7592009-08-06 12:48:26 +000071 bool CheckNilArg(ExplodedNode* N, unsigned Arg);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000072
73public:
Mike Stump11289f42009-09-09 15:08:12 +000074 BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br)
Ted Kremenek095f1a92009-06-18 23:58:37 +000075 : BT(0), BR(br), Ctx(ctx) {}
Mike Stump11289f42009-09-09 15:08:12 +000076
Zhongxing Xu20227f72009-08-06 01:32:16 +000077 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump11289f42009-09-09 15:08:12 +000078
79private:
80 void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekfc5d0672009-02-04 23:49:09 +000081 std::string sbuf;
82 llvm::raw_string_ostream os(sbuf);
83 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
84 << ME->getSelector().getAsString() << "' cannot be nil.";
Mike Stump11289f42009-09-09 15:08:12 +000085
Ted Kremenekfc5d0672009-02-04 23:49:09 +000086 // Lazily create the BugType object for NilArg. This will be owned
87 // by the BugReporter object 'BR' once we call BR.EmitWarning.
88 if (!BT) BT = new APIMisuse("nil argument");
Mike Stump11289f42009-09-09 15:08:12 +000089
Benjamin Kramerff3750f2009-11-29 18:03:28 +000090 RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
Ted Kremenekfc5d0672009-02-04 23:49:09 +000091 R->addRange(ME->getArg(Arg)->getSourceRange());
92 BR.EmitReport(R);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000093 }
94};
Mike Stump11289f42009-09-09 15:08:12 +000095
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000096} // end anonymous namespace
97
98
99GRSimpleAPICheck*
Ted Kremenek095f1a92009-06-18 23:58:37 +0000100clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) {
Mike Stump11289f42009-09-09 15:08:12 +0000101 return new BasicObjCFoundationChecks(Ctx, BR);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000102}
103
104
105
Zhongxing Xu20227f72009-08-06 01:32:16 +0000106bool BasicObjCFoundationChecks::Audit(ExplodedNode* N,
Ted Kremenek5ab5a1b2008-08-13 04:27:00 +0000107 GRStateManager&) {
Mike Stump11289f42009-09-09 15:08:12 +0000108
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000109 const ObjCMessageExpr* ME =
Ted Kremenekc0414922008-03-27 07:25:52 +0000110 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenekc0414922008-03-27 07:25:52 +0000111
Steve Naroff7cae42b2009-07-10 23:34:53 +0000112 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
Mike Stump11289f42009-09-09 15:08:12 +0000113
Ted Kremenekc0414922008-03-27 07:25:52 +0000114 if (!ReceiverType)
Nuno Lopes652eaab2008-05-20 17:33:56 +0000115 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000116
Daniel Dunbar403073e2009-10-17 18:12:21 +0000117 if (isNSString(ReceiverType,
Daniel Dunbar07d07852009-10-18 21:17:35 +0000118 ReceiverType->getDecl()->getIdentifier()->getName()))
Ted Kremenekc0414922008-03-27 07:25:52 +0000119 return AuditNSString(N, ME);
120
Nuno Lopes652eaab2008-05-20 17:33:56 +0000121 return false;
Ted Kremenekc0414922008-03-27 07:25:52 +0000122}
123
Zhongxing Xu27f17422008-10-17 05:57:07 +0000124static inline bool isNil(SVal X) {
Mike Stump11289f42009-09-09 15:08:12 +0000125 return isa<loc::ConcreteInt>(X);
Ted Kremenek27156c82008-03-27 21:15:17 +0000126}
127
Ted Kremenekc0414922008-03-27 07:25:52 +0000128//===----------------------------------------------------------------------===//
129// Error reporting.
130//===----------------------------------------------------------------------===//
131
Zhongxing Xu107f7592009-08-06 12:48:26 +0000132bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) {
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000133 const ObjCMessageExpr* ME =
Ted Kremenek276278e2008-03-27 22:05:32 +0000134 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump11289f42009-09-09 15:08:12 +0000135
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000136 const Expr * E = ME->getArg(Arg);
Mike Stump11289f42009-09-09 15:08:12 +0000137
Ted Kremenek57f09892010-02-08 16:18:51 +0000138 if (isNil(N->getState()->getSVal(E))) {
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000139 WarnNilArg(N, ME, Arg);
Ted Kremenek276278e2008-03-27 22:05:32 +0000140 return true;
141 }
Mike Stump11289f42009-09-09 15:08:12 +0000142
Ted Kremenek276278e2008-03-27 22:05:32 +0000143 return false;
144}
145
Ted Kremenekc0414922008-03-27 07:25:52 +0000146//===----------------------------------------------------------------------===//
147// NSString checking.
148//===----------------------------------------------------------------------===//
149
Steve Naroff7cae42b2009-07-10 23:34:53 +0000150bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T,
Daniel Dunbar403073e2009-10-17 18:12:21 +0000151 llvm::StringRef ClassName) {
152 return ClassName == "NSString" || ClassName == "NSMutableString";
Ted Kremenekc0414922008-03-27 07:25:52 +0000153}
154
Mike Stump11289f42009-09-09 15:08:12 +0000155bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N,
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000156 const ObjCMessageExpr* ME) {
Mike Stump11289f42009-09-09 15:08:12 +0000157
Ted Kremenekc0414922008-03-27 07:25:52 +0000158 Selector S = ME->getSelector();
Mike Stump11289f42009-09-09 15:08:12 +0000159
Ted Kremenekc0414922008-03-27 07:25:52 +0000160 if (S.isUnarySelector())
161 return false;
162
163 // FIXME: This is going to be really slow doing these checks with
164 // lexical comparisons.
Mike Stump11289f42009-09-09 15:08:12 +0000165
Benjamin Kramer16fe0bc2010-02-08 19:51:59 +0000166 std::string NameStr = S.getAsString();
167 llvm::StringRef Name(NameStr);
168 assert(!Name.empty());
Mike Stump11289f42009-09-09 15:08:12 +0000169
Benjamin Kramer16fe0bc2010-02-08 19:51:59 +0000170 // FIXME: Checking for initWithFormat: will not work in most cases
171 // yet because [NSString alloc] returns id, not NSString*. We will
172 // need support for tracking expected-type information in the analyzer
173 // to find these errors.
174 if (Name == "caseInsensitiveCompare:" ||
175 Name == "compare:" ||
176 Name == "compare:options:" ||
177 Name == "compare:options:range:" ||
178 Name == "compare:options:range:locale:" ||
179 Name == "componentsSeparatedByCharactersInSet:" ||
180 Name == "initWithFormat:")
181 return CheckNilArg(N, 0);
Mike Stump11289f42009-09-09 15:08:12 +0000182
Ted Kremenekc0414922008-03-27 07:25:52 +0000183 return false;
184}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000185
186//===----------------------------------------------------------------------===//
187// Error reporting.
188//===----------------------------------------------------------------------===//
189
190namespace {
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000191
Kovarththanan Rajaratnam65c65662009-11-28 06:07:30 +0000192class AuditCFNumberCreate : public GRSimpleAPICheck {
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000193 APIMisuse* BT;
Mike Stump11289f42009-09-09 15:08:12 +0000194
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000195 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
196 // it should always be passed with a call to Audit. The latter
197 // approach makes this class more stateless.
198 ASTContext& Ctx;
199 IdentifierInfo* II;
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000200 BugReporter& BR;
Ted Kremenek095f1a92009-06-18 23:58:37 +0000201
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000202public:
Mike Stump11289f42009-09-09 15:08:12 +0000203 AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
Ted Kremenek095f1a92009-06-18 23:58:37 +0000204 : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
Mike Stump11289f42009-09-09 15:08:12 +0000205
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000206 ~AuditCFNumberCreate() {}
Mike Stump11289f42009-09-09 15:08:12 +0000207
Zhongxing Xu20227f72009-08-06 01:32:16 +0000208 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump11289f42009-09-09 15:08:12 +0000209
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000210private:
Zhongxing Xu20227f72009-08-06 01:32:16 +0000211 void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N,
Mike Stump11289f42009-09-09 15:08:12 +0000212 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000213};
214} // end anonymous namespace
215
216enum CFNumberType {
217 kCFNumberSInt8Type = 1,
218 kCFNumberSInt16Type = 2,
219 kCFNumberSInt32Type = 3,
220 kCFNumberSInt64Type = 4,
221 kCFNumberFloat32Type = 5,
222 kCFNumberFloat64Type = 6,
223 kCFNumberCharType = 7,
224 kCFNumberShortType = 8,
225 kCFNumberIntType = 9,
226 kCFNumberLongType = 10,
227 kCFNumberLongLongType = 11,
228 kCFNumberFloatType = 12,
229 kCFNumberDoubleType = 13,
230 kCFNumberCFIndexType = 14,
231 kCFNumberNSIntegerType = 15,
232 kCFNumberCGFloatType = 16
233};
234
235namespace {
236 template<typename T>
237 class Optional {
238 bool IsKnown;
239 T Val;
240 public:
241 Optional() : IsKnown(false), Val(0) {}
242 Optional(const T& val) : IsKnown(true), Val(val) {}
Mike Stump11289f42009-09-09 15:08:12 +0000243
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000244 bool isKnown() const { return IsKnown; }
245
246 const T& getValue() const {
247 assert (isKnown());
248 return Val;
249 }
250
251 operator const T&() const {
252 return getValue();
253 }
254 };
255}
256
257static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
Nuno Lopescfca1f02009-12-23 17:49:57 +0000258 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump11289f42009-09-09 15:08:12 +0000259
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000260 if (i < kCFNumberCharType)
261 return FixedSize[i-1];
Mike Stump11289f42009-09-09 15:08:12 +0000262
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000263 QualType T;
Mike Stump11289f42009-09-09 15:08:12 +0000264
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000265 switch (i) {
266 case kCFNumberCharType: T = Ctx.CharTy; break;
267 case kCFNumberShortType: T = Ctx.ShortTy; break;
268 case kCFNumberIntType: T = Ctx.IntTy; break;
269 case kCFNumberLongType: T = Ctx.LongTy; break;
270 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
271 case kCFNumberFloatType: T = Ctx.FloatTy; break;
272 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
273 case kCFNumberCFIndexType:
274 case kCFNumberNSIntegerType:
275 case kCFNumberCGFloatType:
Mike Stump11289f42009-09-09 15:08:12 +0000276 // FIXME: We need a way to map from names to Type*.
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000277 default:
278 return Optional<uint64_t>();
279 }
Mike Stump11289f42009-09-09 15:08:12 +0000280
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000281 return Ctx.getTypeSize(T);
282}
283
284#if 0
285static const char* GetCFNumberTypeStr(uint64_t i) {
286 static const char* Names[] = {
287 "kCFNumberSInt8Type",
288 "kCFNumberSInt16Type",
289 "kCFNumberSInt32Type",
290 "kCFNumberSInt64Type",
291 "kCFNumberFloat32Type",
292 "kCFNumberFloat64Type",
293 "kCFNumberCharType",
294 "kCFNumberShortType",
295 "kCFNumberIntType",
296 "kCFNumberLongType",
297 "kCFNumberLongLongType",
298 "kCFNumberFloatType",
299 "kCFNumberDoubleType",
300 "kCFNumberCFIndexType",
301 "kCFNumberNSIntegerType",
302 "kCFNumberCGFloatType"
303 };
Mike Stump11289f42009-09-09 15:08:12 +0000304
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000305 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
306}
307#endif
308
Mike Stump11289f42009-09-09 15:08:12 +0000309bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000310 const CallExpr* CE =
311 cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump11289f42009-09-09 15:08:12 +0000312 const Expr* Callee = CE->getCallee();
Ted Kremenek57f09892010-02-08 16:18:51 +0000313 SVal CallV = N->getState()->getSVal(Callee);
Zhongxing Xuac129432009-04-20 05:24:46 +0000314 const FunctionDecl* FD = CallV.getAsFunctionDecl();
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000315
Zhongxing Xuac129432009-04-20 05:24:46 +0000316 if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000317 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000318
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000319 // Get the value of the "theType" argument.
Ted Kremenek57f09892010-02-08 16:18:51 +0000320 SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
Mike Stump11289f42009-09-09 15:08:12 +0000321
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000322 // FIXME: We really should allow ranges of valid theType values, and
323 // bifurcate the state appropriately.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000324 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Mike Stump11289f42009-09-09 15:08:12 +0000325
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000326 if (!V)
327 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000328
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000329 uint64_t NumberKind = V->getValue().getLimitedValue();
330 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000331
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000332 // FIXME: In some cases we can emit an error.
333 if (!TargetSize.isKnown())
334 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000335
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000336 // Look at the value of the integer being passed by reference. Essentially
337 // we want to catch cases where the value passed in is not equal to the
338 // size of the type being created.
Ted Kremenek57f09892010-02-08 16:18:51 +0000339 SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
Mike Stump11289f42009-09-09 15:08:12 +0000340
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000341 // FIXME: Eventually we should handle arbitrary locations. We can do this
342 // by having an enhanced memory model that does low-level typing.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000343 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000344
345 if (!LV)
346 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000347
Zhongxing Xuf8f3f9d2009-11-10 02:17:20 +0000348 const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
Ted Kremenek87a7a452009-07-29 18:17:40 +0000349
350 if (!R)
351 return false;
352
Zhongxing Xu34d04b32009-05-09 03:57:34 +0000353 QualType T = Ctx.getCanonicalType(R->getValueType(Ctx));
Mike Stump11289f42009-09-09 15:08:12 +0000354
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000355 // FIXME: If the pointee isn't an integer type, should we flag a warning?
356 // People can do weird stuff with pointers.
Mike Stump11289f42009-09-09 15:08:12 +0000357
358 if (!T->isIntegerType())
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000359 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000360
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000361 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump11289f42009-09-09 15:08:12 +0000362
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000363 // CHECK: is SourceSize == TargetSize
Mike Stump11289f42009-09-09 15:08:12 +0000364
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000365 if (SourceSize == TargetSize)
366 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000367
Ted Kremenek5ca90a22008-10-04 05:50:14 +0000368 AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000369
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000370 // FIXME: We can actually create an abstract "CFNumber" object that has
371 // the bits initialized to the provided values.
372 return SourceSize < TargetSize;
373}
374
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000375void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex,
Zhongxing Xu20227f72009-08-06 01:32:16 +0000376 ExplodedNode *N,
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000377 uint64_t SourceSize, uint64_t TargetSize,
378 uint64_t NumberKind) {
Mike Stump11289f42009-09-09 15:08:12 +0000379
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000380 std::string sbuf;
381 llvm::raw_string_ostream os(sbuf);
Mike Stump11289f42009-09-09 15:08:12 +0000382
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000383 os << (SourceSize == 8 ? "An " : "A ")
384 << SourceSize << " bit integer is used to initialize a CFNumber "
385 "object that represents "
386 << (TargetSize == 8 ? "an " : "a ")
Mike Stump11289f42009-09-09 15:08:12 +0000387 << TargetSize << " bit integer. ";
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000388
389 if (SourceSize < TargetSize)
390 os << (TargetSize - SourceSize)
Mike Stump11289f42009-09-09 15:08:12 +0000391 << " bits of the CFNumber value will be garbage." ;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000392 else
393 os << (SourceSize - TargetSize)
394 << " bits of the input integer will be lost.";
Mike Stump11289f42009-09-09 15:08:12 +0000395
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000396 // Lazily create the BugType object. This will be owned
397 // by the BugReporter object 'BR' once we call BR.EmitWarning.
398 if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate");
Benjamin Kramerff3750f2009-11-29 18:03:28 +0000399 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000400 report->addRange(Ex->getSourceRange());
401 BR.EmitReport(report);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000402}
403
404GRSimpleAPICheck*
Mike Stump11289f42009-09-09 15:08:12 +0000405clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek095f1a92009-06-18 23:58:37 +0000406 return new AuditCFNumberCreate(Ctx, BR);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000407}
408
Ted Kremenek1f352db2008-07-22 16:21:24 +0000409//===----------------------------------------------------------------------===//
Ted Kremenekc057f412009-07-14 00:43:42 +0000410// CFRetain/CFRelease auditing for null arguments.
411//===----------------------------------------------------------------------===//
412
413namespace {
Kovarththanan Rajaratnam65c65662009-11-28 06:07:30 +0000414class AuditCFRetainRelease : public GRSimpleAPICheck {
Ted Kremenekc057f412009-07-14 00:43:42 +0000415 APIMisuse *BT;
Mike Stump11289f42009-09-09 15:08:12 +0000416
Ted Kremenekc057f412009-07-14 00:43:42 +0000417 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
418 // it should always be passed with a call to Audit. The latter
419 // approach makes this class more stateless.
420 ASTContext& Ctx;
421 IdentifierInfo *Retain, *Release;
422 BugReporter& BR;
Mike Stump11289f42009-09-09 15:08:12 +0000423
Ted Kremenekc057f412009-07-14 00:43:42 +0000424public:
Mike Stump11289f42009-09-09 15:08:12 +0000425 AuditCFRetainRelease(ASTContext& ctx, BugReporter& br)
Ted Kremenekc057f412009-07-14 00:43:42 +0000426 : BT(0), Ctx(ctx),
427 Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")),
428 BR(br){}
Mike Stump11289f42009-09-09 15:08:12 +0000429
Ted Kremenekc057f412009-07-14 00:43:42 +0000430 ~AuditCFRetainRelease() {}
Mike Stump11289f42009-09-09 15:08:12 +0000431
Zhongxing Xu20227f72009-08-06 01:32:16 +0000432 bool Audit(ExplodedNode* N, GRStateManager&);
Ted Kremenekc057f412009-07-14 00:43:42 +0000433};
434} // end anonymous namespace
435
436
Zhongxing Xu20227f72009-08-06 01:32:16 +0000437bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) {
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000438 const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump11289f42009-09-09 15:08:12 +0000439
Ted Kremenekc057f412009-07-14 00:43:42 +0000440 // If the CallExpr doesn't have exactly 1 argument just give up checking.
441 if (CE->getNumArgs() != 1)
442 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000443
Ted Kremenekc057f412009-07-14 00:43:42 +0000444 // Check if we called CFRetain/CFRelease.
445 const GRState* state = N->getState();
Ted Kremenek57f09892010-02-08 16:18:51 +0000446 SVal X = state->getSVal(CE->getCallee());
Ted Kremenekc057f412009-07-14 00:43:42 +0000447 const FunctionDecl* FD = X.getAsFunctionDecl();
Mike Stump11289f42009-09-09 15:08:12 +0000448
Ted Kremenekc057f412009-07-14 00:43:42 +0000449 if (!FD)
450 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000451
452 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenekc057f412009-07-14 00:43:42 +0000453 if (!(FuncII == Retain || FuncII == Release))
454 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000455
Ted Kremenekc057f412009-07-14 00:43:42 +0000456 // Finally, check if the argument is NULL.
457 // FIXME: We should be able to bifurcate the state here, as a successful
458 // check will result in the value not being NULL afterwards.
459 // FIXME: Need a way to register vistors for the BugReporter. Would like
460 // to benefit from the same diagnostics that regular null dereference
461 // reporting has.
462 if (state->getStateManager().isEqual(state, CE->getArg(0), 0)) {
463 if (!BT)
464 BT = new APIMisuse("null passed to CFRetain/CFRelease");
Mike Stump11289f42009-09-09 15:08:12 +0000465
Ted Kremenekc057f412009-07-14 00:43:42 +0000466 const char *description = (FuncII == Retain)
467 ? "Null pointer argument in call to CFRetain"
468 : "Null pointer argument in call to CFRelease";
469
470 RangedBugReport *report = new RangedBugReport(*BT, description, N);
471 report->addRange(CE->getArg(0)->getSourceRange());
472 BR.EmitReport(report);
473 return true;
474 }
475
476 return false;
477}
Mike Stump11289f42009-09-09 15:08:12 +0000478
479
Ted Kremenekc057f412009-07-14 00:43:42 +0000480GRSimpleAPICheck*
Mike Stump11289f42009-09-09 15:08:12 +0000481clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenekc057f412009-07-14 00:43:42 +0000482 return new AuditCFRetainRelease(Ctx, BR);
483}
484
485//===----------------------------------------------------------------------===//
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000486// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
487//===----------------------------------------------------------------------===//
488
489namespace {
Kovarththanan Rajaratnam65c65662009-11-28 06:07:30 +0000490class ClassReleaseChecker :
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000491 public CheckerVisitor<ClassReleaseChecker> {
492 Selector releaseS;
493 Selector retainS;
494 Selector autoreleaseS;
495 Selector drainS;
496 BugType *BT;
497public:
498 ClassReleaseChecker(ASTContext &Ctx)
499 : releaseS(GetNullarySelector("release", Ctx)),
500 retainS(GetNullarySelector("retain", Ctx)),
501 autoreleaseS(GetNullarySelector("autorelease", Ctx)),
502 drainS(GetNullarySelector("drain", Ctx)),
503 BT(0) {}
504
505 static void *getTag() { static int x = 0; return &x; }
506
507 void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);
508};
509}
510
511void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C,
512 const ObjCMessageExpr *ME) {
513
514 const IdentifierInfo *ClsName = ME->getClassName();
515 if (!ClsName)
516 return;
517
518 Selector S = ME->getSelector();
Benjamin Kramer7d875c72009-11-20 10:03:00 +0000519 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000520 return;
521
522 if (!BT)
523 BT = new APIMisuse("message incorrectly sent to class instead of class "
524 "instance");
525
Ted Kremenekf5735152009-11-23 22:22:01 +0000526 ExplodedNode *N = C.GenerateNode();
527
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000528 if (!N)
529 return;
530
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000531 llvm::SmallString<200> buf;
532 llvm::raw_svector_ostream os(buf);
533
534 os << "The '" << S.getAsString() << "' message should be sent to instances "
535 "of class '" << ClsName->getName()
536 << "' and not the class directly";
537
538 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
539 report->addRange(ME->getSourceRange());
540 C.EmitReport(report);
541}
542
543//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000544// Check registration.
Ted Kremenekc057f412009-07-14 00:43:42 +0000545//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000546
Zhongxing Xu6be1a4e2009-08-21 02:18:44 +0000547void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) {
Ted Kremenek1f352db2008-07-22 16:21:24 +0000548 ASTContext& Ctx = Eng.getContext();
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000549 BugReporter &BR = Eng.getBugReporter();
Ted Kremenek1f352db2008-07-22 16:21:24 +0000550
Ted Kremenek095f1a92009-06-18 23:58:37 +0000551 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
Ted Kremenek1f352db2008-07-22 16:21:24 +0000552 Stmt::ObjCMessageExprClass);
Mike Stump11289f42009-09-09 15:08:12 +0000553 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
Ted Kremenekc057f412009-07-14 00:43:42 +0000554 Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass);
Mike Stump11289f42009-09-09 15:08:12 +0000555
Zhongxing Xu6be1a4e2009-08-21 02:18:44 +0000556 RegisterNSErrorChecks(BR, Eng, D);
Ted Kremenek18c7cee2009-11-03 08:03:59 +0000557 RegisterNSAutoreleasePoolChecks(Eng);
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000558 Eng.registerCheck(new ClassReleaseChecker(Ctx));
Ted Kremenek1f352db2008-07-22 16:21:24 +0000559}