blob: d6c09a2e04a62d9115566458a8f76793b208dd76 [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"
Ted Kremenek6b676302010-01-25 17:10:22 +000022#include "clang/Checker/BugReporter/BugReporter.h"
Ted Kremenek1309f9a2010-01-25 04:41:41 +000023#include "clang/Checker/PathSensitive/MemRegion.h"
Ted Kremenek6b676302010-01-25 17:10:22 +000024#include "clang/Checker/BugReporter/PathDiagnostic.h"
Ted Kremenek1309f9a2010-01-25 04:41:41 +000025#include "clang/Checker/PathSensitive/CheckerVisitor.h"
Ted Kremenek97053092010-01-26 22:59:55 +000026#include "clang/Checker/Checkers/LocalCheckers.h"
Daniel Dunbarc4a1dea2008-08-11 05:35:13 +000027#include "clang/AST/DeclObjC.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000028#include "clang/AST/Expr.h"
Steve Narofff494b572008-05-29 21:12:08 +000029#include "clang/AST/ExprObjC.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000030#include "clang/AST/ASTContext.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000031
Ted Kremenek99c6ad32008-03-27 07:25:52 +000032using namespace clang;
Ted Kremenek52755612008-03-27 17:17:22 +000033
Steve Naroff14108da2009-07-10 23:34:53 +000034static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) {
35 const Expr* Receiver = ME->getReceiver();
Mike Stump1eb44332009-09-09 15:08:12 +000036
Ted Kremenek4ba62832008-03-27 22:05:32 +000037 if (!Receiver)
38 return NULL;
Mike Stump1eb44332009-09-09 15:08:12 +000039
Steve Naroff14108da2009-07-10 23:34:53 +000040 if (const ObjCObjectPointerType *PT =
John McCall183700f2009-09-21 23:43:11 +000041 Receiver->getType()->getAs<ObjCObjectPointerType>())
Steve Naroff14108da2009-07-10 23:34:53 +000042 return PT->getInterfaceType();
Ted Kremenekc1ff3cd2008-04-30 22:48:21 +000043
Ted Kremenekc1ff3cd2008-04-30 22:48:21 +000044 return NULL;
Ted Kremenek4ba62832008-03-27 22:05:32 +000045}
46
Steve Naroff14108da2009-07-10 23:34:53 +000047static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
Daniel Dunbare013d682009-10-18 20:26:12 +000048 if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME))
49 return ReceiverType->getDecl()->getIdentifier()->getNameStart();
50 return NULL;
Ted Kremenek4ba62832008-03-27 22:05:32 +000051}
Ted Kremenek52755612008-03-27 17:17:22 +000052
Ted Kremenekf1ae7052008-04-03 17:57:38 +000053namespace {
Ted Kremenekb344f912008-09-21 19:01:39 +000054
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +000055class APIMisuse : public BugType {
Ted Kremenekf1ae7052008-04-03 17:57:38 +000056public:
Ted Kremenekcf118d42009-02-04 23:49:09 +000057 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
Ted Kremenekf1ae7052008-04-03 17:57:38 +000058};
Mike Stump1eb44332009-09-09 15:08:12 +000059
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +000060class BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenekcf118d42009-02-04 23:49:09 +000061 APIMisuse *BT;
62 BugReporter& BR;
Ted Kremenekf1ae7052008-04-03 17:57:38 +000063 ASTContext &Ctx;
Mike Stump1eb44332009-09-09 15:08:12 +000064
Daniel Dunbard777d582009-10-17 18:12:21 +000065 bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix);
Zhongxing Xu031ccc02009-08-06 12:48:26 +000066 bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME);
Mike Stump1eb44332009-09-09 15:08:12 +000067
68 void Warn(ExplodedNode* N, const Expr* E, const std::string& s);
Zhongxing Xu031ccc02009-08-06 12:48:26 +000069 void WarnNilArg(ExplodedNode* N, const Expr* E);
Mike Stump1eb44332009-09-09 15:08:12 +000070
Zhongxing Xu031ccc02009-08-06 12:48:26 +000071 bool CheckNilArg(ExplodedNode* N, unsigned Arg);
Ted Kremenekf1ae7052008-04-03 17:57:38 +000072
73public:
Mike Stump1eb44332009-09-09 15:08:12 +000074 BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br)
Ted Kremenek23ec48c2009-06-18 23:58:37 +000075 : BT(0), BR(br), Ctx(ctx) {}
Mike Stump1eb44332009-09-09 15:08:12 +000076
Zhongxing Xuc5619d92009-08-06 01:32:16 +000077 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump1eb44332009-09-09 15:08:12 +000078
79private:
80 void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekcf118d42009-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 Stump1eb44332009-09-09 15:08:12 +000085
Ted Kremenekcf118d42009-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 Stump1eb44332009-09-09 15:08:12 +000089
Benjamin Kramer4988a9a2009-11-29 18:03:28 +000090 RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
Ted Kremenekcf118d42009-02-04 23:49:09 +000091 R->addRange(ME->getArg(Arg)->getSourceRange());
92 BR.EmitReport(R);
Ted Kremenekf1ae7052008-04-03 17:57:38 +000093 }
94};
Mike Stump1eb44332009-09-09 15:08:12 +000095
Ted Kremenekf1ae7052008-04-03 17:57:38 +000096} // end anonymous namespace
97
98
99GRSimpleAPICheck*
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000100clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) {
Mike Stump1eb44332009-09-09 15:08:12 +0000101 return new BasicObjCFoundationChecks(Ctx, BR);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000102}
103
104
105
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000106bool BasicObjCFoundationChecks::Audit(ExplodedNode* N,
Ted Kremenek4adc81e2008-08-13 04:27:00 +0000107 GRStateManager&) {
Mike Stump1eb44332009-09-09 15:08:12 +0000108
Ted Kremenek5f85e172009-07-22 22:35:28 +0000109 const ObjCMessageExpr* ME =
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000110 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000111
Steve Naroff14108da2009-07-10 23:34:53 +0000112 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
Mike Stump1eb44332009-09-09 15:08:12 +0000113
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000114 if (!ReceiverType)
Nuno Lopesf7427942008-05-20 17:33:56 +0000115 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000116
Daniel Dunbard777d582009-10-17 18:12:21 +0000117 if (isNSString(ReceiverType,
Daniel Dunbar01eb9b92009-10-18 21:17:35 +0000118 ReceiverType->getDecl()->getIdentifier()->getName()))
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000119 return AuditNSString(N, ME);
120
Nuno Lopesf7427942008-05-20 17:33:56 +0000121 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000122}
123
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000124static inline bool isNil(SVal X) {
Mike Stump1eb44332009-09-09 15:08:12 +0000125 return isa<loc::ConcreteInt>(X);
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000126}
127
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000128//===----------------------------------------------------------------------===//
129// Error reporting.
130//===----------------------------------------------------------------------===//
131
Zhongxing Xu031ccc02009-08-06 12:48:26 +0000132bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) {
Ted Kremenek5f85e172009-07-22 22:35:28 +0000133 const ObjCMessageExpr* ME =
Ted Kremenek4ba62832008-03-27 22:05:32 +0000134 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000135
Ted Kremenek5f85e172009-07-22 22:35:28 +0000136 const Expr * E = ME->getArg(Arg);
Mike Stump1eb44332009-09-09 15:08:12 +0000137
Ted Kremenek13976632010-02-08 16:18:51 +0000138 if (isNil(N->getState()->getSVal(E))) {
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000139 WarnNilArg(N, ME, Arg);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000140 return true;
141 }
Mike Stump1eb44332009-09-09 15:08:12 +0000142
Ted Kremenek4ba62832008-03-27 22:05:32 +0000143 return false;
144}
145
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000146//===----------------------------------------------------------------------===//
147// NSString checking.
148//===----------------------------------------------------------------------===//
149
Steve Naroff14108da2009-07-10 23:34:53 +0000150bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T,
Daniel Dunbard777d582009-10-17 18:12:21 +0000151 llvm::StringRef ClassName) {
152 return ClassName == "NSString" || ClassName == "NSMutableString";
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000153}
154
Mike Stump1eb44332009-09-09 15:08:12 +0000155bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N,
Ted Kremenek5f85e172009-07-22 22:35:28 +0000156 const ObjCMessageExpr* ME) {
Mike Stump1eb44332009-09-09 15:08:12 +0000157
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000158 Selector S = ME->getSelector();
Mike Stump1eb44332009-09-09 15:08:12 +0000159
Ted Kremenek99c6ad32008-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 Stump1eb44332009-09-09 15:08:12 +0000165
Benjamin Kramer064fb202010-02-08 19:51:59 +0000166 std::string NameStr = S.getAsString();
167 llvm::StringRef Name(NameStr);
168 assert(!Name.empty());
Mike Stump1eb44332009-09-09 15:08:12 +0000169
Benjamin Kramer064fb202010-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 Stump1eb44332009-09-09 15:08:12 +0000182
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000183 return false;
184}
Ted Kremenek04bc8762008-06-26 23:59:48 +0000185
186//===----------------------------------------------------------------------===//
187// Error reporting.
188//===----------------------------------------------------------------------===//
189
190namespace {
Ted Kremenek04bc8762008-06-26 23:59:48 +0000191
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +0000192class AuditCFNumberCreate : public GRSimpleAPICheck {
Ted Kremenekcf118d42009-02-04 23:49:09 +0000193 APIMisuse* BT;
Mike Stump1eb44332009-09-09 15:08:12 +0000194
Ted Kremenek04bc8762008-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 Kremenekcf118d42009-02-04 23:49:09 +0000200 BugReporter& BR;
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000201
Ted Kremenek04bc8762008-06-26 23:59:48 +0000202public:
Mike Stump1eb44332009-09-09 15:08:12 +0000203 AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000204 : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
Mike Stump1eb44332009-09-09 15:08:12 +0000205
Ted Kremenekcf118d42009-02-04 23:49:09 +0000206 ~AuditCFNumberCreate() {}
Mike Stump1eb44332009-09-09 15:08:12 +0000207
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000208 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump1eb44332009-09-09 15:08:12 +0000209
Ted Kremenek04bc8762008-06-26 23:59:48 +0000210private:
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000211 void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N,
Mike Stump1eb44332009-09-09 15:08:12 +0000212 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000243
Ted Kremenek04bc8762008-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 Lopes2550d702009-12-23 17:49:57 +0000258 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump1eb44332009-09-09 15:08:12 +0000259
Ted Kremenek04bc8762008-06-26 23:59:48 +0000260 if (i < kCFNumberCharType)
261 return FixedSize[i-1];
Mike Stump1eb44332009-09-09 15:08:12 +0000262
Ted Kremenek04bc8762008-06-26 23:59:48 +0000263 QualType T;
Mike Stump1eb44332009-09-09 15:08:12 +0000264
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000276 // FIXME: We need a way to map from names to Type*.
Ted Kremenek04bc8762008-06-26 23:59:48 +0000277 default:
278 return Optional<uint64_t>();
279 }
Mike Stump1eb44332009-09-09 15:08:12 +0000280
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000304
Ted Kremenek04bc8762008-06-26 23:59:48 +0000305 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
306}
307#endif
308
Mike Stump1eb44332009-09-09 15:08:12 +0000309bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
Ted Kremenek5f85e172009-07-22 22:35:28 +0000310 const CallExpr* CE =
311 cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000312 const Expr* Callee = CE->getCallee();
Ted Kremenek13976632010-02-08 16:18:51 +0000313 SVal CallV = N->getState()->getSVal(Callee);
Zhongxing Xu369f4472009-04-20 05:24:46 +0000314 const FunctionDecl* FD = CallV.getAsFunctionDecl();
Ted Kremenek04bc8762008-06-26 23:59:48 +0000315
Zhongxing Xu369f4472009-04-20 05:24:46 +0000316 if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
Ted Kremenek04bc8762008-06-26 23:59:48 +0000317 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000318
Ted Kremenek04bc8762008-06-26 23:59:48 +0000319 // Get the value of the "theType" argument.
Ted Kremenek13976632010-02-08 16:18:51 +0000320 SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
Mike Stump1eb44332009-09-09 15:08:12 +0000321
Ted Kremenek04bc8762008-06-26 23:59:48 +0000322 // FIXME: We really should allow ranges of valid theType values, and
323 // bifurcate the state appropriately.
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000324 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Mike Stump1eb44332009-09-09 15:08:12 +0000325
Ted Kremenek04bc8762008-06-26 23:59:48 +0000326 if (!V)
327 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000328
Ted Kremenek04bc8762008-06-26 23:59:48 +0000329 uint64_t NumberKind = V->getValue().getLimitedValue();
330 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000331
Ted Kremenek04bc8762008-06-26 23:59:48 +0000332 // FIXME: In some cases we can emit an error.
333 if (!TargetSize.isKnown())
334 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000335
Ted Kremenek04bc8762008-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 Kremenek13976632010-02-08 16:18:51 +0000339 SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
Mike Stump1eb44332009-09-09 15:08:12 +0000340
Ted Kremenek04bc8762008-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 Xu1c96b242008-10-17 05:57:07 +0000343 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000344
345 if (!LV)
346 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000347
Zhongxing Xu479529e2009-11-10 02:17:20 +0000348 const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
Ted Kremenek5e77eba2009-07-29 18:17:40 +0000349
350 if (!R)
351 return false;
352
Zhongxing Xua82d8aa2009-05-09 03:57:34 +0000353 QualType T = Ctx.getCanonicalType(R->getValueType(Ctx));
Mike Stump1eb44332009-09-09 15:08:12 +0000354
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000357
358 if (!T->isIntegerType())
Ted Kremenek04bc8762008-06-26 23:59:48 +0000359 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000360
Ted Kremenek04bc8762008-06-26 23:59:48 +0000361 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump1eb44332009-09-09 15:08:12 +0000362
Ted Kremenek04bc8762008-06-26 23:59:48 +0000363 // CHECK: is SourceSize == TargetSize
Mike Stump1eb44332009-09-09 15:08:12 +0000364
Ted Kremenek04bc8762008-06-26 23:59:48 +0000365 if (SourceSize == TargetSize)
366 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000367
Ted Kremenek9e240492008-10-04 05:50:14 +0000368 AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000369
Ted Kremenek04bc8762008-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 Kremenek5f85e172009-07-22 22:35:28 +0000375void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex,
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000376 ExplodedNode *N,
Ted Kremenek04bc8762008-06-26 23:59:48 +0000377 uint64_t SourceSize, uint64_t TargetSize,
378 uint64_t NumberKind) {
Mike Stump1eb44332009-09-09 15:08:12 +0000379
Ted Kremenekcf118d42009-02-04 23:49:09 +0000380 std::string sbuf;
381 llvm::raw_string_ostream os(sbuf);
Mike Stump1eb44332009-09-09 15:08:12 +0000382
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000387 << TargetSize << " bit integer. ";
Ted Kremenek04bc8762008-06-26 23:59:48 +0000388
389 if (SourceSize < TargetSize)
390 os << (TargetSize - SourceSize)
Mike Stump1eb44332009-09-09 15:08:12 +0000391 << " bits of the CFNumber value will be garbage." ;
Ted Kremenek04bc8762008-06-26 23:59:48 +0000392 else
393 os << (SourceSize - TargetSize)
394 << " bits of the input integer will be lost.";
Mike Stump1eb44332009-09-09 15:08:12 +0000395
Ted Kremenekcf118d42009-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 Kramer4988a9a2009-11-29 18:03:28 +0000399 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
Ted Kremenekcf118d42009-02-04 23:49:09 +0000400 report->addRange(Ex->getSourceRange());
401 BR.EmitReport(report);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000402}
403
404GRSimpleAPICheck*
Mike Stump1eb44332009-09-09 15:08:12 +0000405clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000406 return new AuditCFNumberCreate(Ctx, BR);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000407}
408
Ted Kremenek78d46242008-07-22 16:21:24 +0000409//===----------------------------------------------------------------------===//
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000410// CFRetain/CFRelease auditing for null arguments.
411//===----------------------------------------------------------------------===//
412
413namespace {
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +0000414class AuditCFRetainRelease : public GRSimpleAPICheck {
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000415 APIMisuse *BT;
Mike Stump1eb44332009-09-09 15:08:12 +0000416
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000423
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000424public:
Mike Stump1eb44332009-09-09 15:08:12 +0000425 AuditCFRetainRelease(ASTContext& ctx, BugReporter& br)
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000429
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000430 ~AuditCFRetainRelease() {}
Mike Stump1eb44332009-09-09 15:08:12 +0000431
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000432 bool Audit(ExplodedNode* N, GRStateManager&);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000433};
434} // end anonymous namespace
435
436
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000437bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) {
Ted Kremenek5f85e172009-07-22 22:35:28 +0000438 const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000439
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000443
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000444 // Check if we called CFRetain/CFRelease.
445 const GRState* state = N->getState();
Ted Kremenek13976632010-02-08 16:18:51 +0000446 SVal X = state->getSVal(CE->getCallee());
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000447 const FunctionDecl* FD = X.getAsFunctionDecl();
Mike Stump1eb44332009-09-09 15:08:12 +0000448
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000449 if (!FD)
450 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000451
452 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000453 if (!(FuncII == Retain || FuncII == Release))
454 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000455
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000465
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000478
479
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000480GRSimpleAPICheck*
Mike Stump1eb44332009-09-09 15:08:12 +0000481clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000482 return new AuditCFRetainRelease(Ctx, BR);
483}
484
485//===----------------------------------------------------------------------===//
Ted Kremenek50e837b2009-11-20 05:27:05 +0000486// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
487//===----------------------------------------------------------------------===//
488
489namespace {
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +0000490class ClassReleaseChecker :
Ted Kremenek50e837b2009-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 Kramer921ddc42009-11-20 10:03:00 +0000519 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremenek50e837b2009-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 Kremenek19d67b52009-11-23 22:22:01 +0000526 ExplodedNode *N = C.GenerateNode();
527
Ted Kremenek50e837b2009-11-20 05:27:05 +0000528 if (!N)
529 return;
530
Ted Kremenek50e837b2009-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 Kremenek78d46242008-07-22 16:21:24 +0000544// Check registration.
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000545//===----------------------------------------------------------------------===//
Ted Kremenek78d46242008-07-22 16:21:24 +0000546
Zhongxing Xu5ab128b2009-08-21 02:18:44 +0000547void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) {
Ted Kremenek78d46242008-07-22 16:21:24 +0000548 ASTContext& Ctx = Eng.getContext();
Ted Kremenekcf118d42009-02-04 23:49:09 +0000549 BugReporter &BR = Eng.getBugReporter();
Ted Kremenek78d46242008-07-22 16:21:24 +0000550
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000551 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
Ted Kremenek78d46242008-07-22 16:21:24 +0000552 Stmt::ObjCMessageExprClass);
Mike Stump1eb44332009-09-09 15:08:12 +0000553 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000554 Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass);
Mike Stump1eb44332009-09-09 15:08:12 +0000555
Zhongxing Xu5ab128b2009-08-21 02:18:44 +0000556 RegisterNSErrorChecks(BR, Eng, D);
Ted Kremenek54cb7cc2009-11-03 08:03:59 +0000557 RegisterNSAutoreleasePoolChecks(Eng);
Ted Kremenek50e837b2009-11-20 05:27:05 +0000558 Eng.registerCheck(new ClassReleaseChecker(Ctx));
Ted Kremenek78d46242008-07-22 16:21:24 +0000559}