blob: 3c1a6d1c8282fadf4373935d702084bc2ec2580a [file] [log] [blame]
Ted Kremenek99c6ad32008-03-27 07:25:52 +00001//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines BasicObjCFoundationChecks, a class that encapsulates
11// a set of simple checks to run on Objective-C code using Apple's Foundation
12// classes.
13//
14//===----------------------------------------------------------------------===//
15
Ted Kremenek52755612008-03-27 17:17:22 +000016#include "BasicObjCFoundationChecks.h"
17
Ted Kremenek1309f9a2010-01-25 04:41:41 +000018#include "clang/Checker/PathSensitive/ExplodedGraph.h"
19#include "clang/Checker/PathSensitive/GRSimpleAPICheck.h"
20#include "clang/Checker/PathSensitive/GRExprEngine.h"
21#include "clang/Checker/PathSensitive/GRState.h"
Benjamin Kramer5e2d2c22010-03-27 21:19:47 +000022#include "clang/Checker/BugReporter/BugType.h"
Ted Kremenek1309f9a2010-01-25 04:41:41 +000023#include "clang/Checker/PathSensitive/MemRegion.h"
Ted Kremenek1309f9a2010-01-25 04:41:41 +000024#include "clang/Checker/PathSensitive/CheckerVisitor.h"
Ted Kremenek97053092010-01-26 22:59:55 +000025#include "clang/Checker/Checkers/LocalCheckers.h"
Daniel Dunbarc4a1dea2008-08-11 05:35:13 +000026#include "clang/AST/DeclObjC.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000027#include "clang/AST/Expr.h"
Steve Narofff494b572008-05-29 21:12:08 +000028#include "clang/AST/ExprObjC.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000029#include "clang/AST/ASTContext.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000030
Ted Kremenek99c6ad32008-03-27 07:25:52 +000031using namespace clang;
Ted Kremenek52755612008-03-27 17:17:22 +000032
Steve Naroff14108da2009-07-10 23:34:53 +000033static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) {
Douglas Gregor04badcf2010-04-21 00:45:42 +000034 QualType T;
35 switch (ME->getReceiverKind()) {
36 case ObjCMessageExpr::Instance:
37 T = ME->getInstanceReceiver()->getType();
38 break;
Mike Stump1eb44332009-09-09 15:08:12 +000039
Douglas Gregor04badcf2010-04-21 00:45:42 +000040 case ObjCMessageExpr::SuperInstance:
41 T = ME->getSuperType();
42 break;
Mike Stump1eb44332009-09-09 15:08:12 +000043
Douglas Gregor04badcf2010-04-21 00:45:42 +000044 case ObjCMessageExpr::Class:
45 case ObjCMessageExpr::SuperClass:
46 return 0;
47 }
48
49 if (const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>())
Steve Naroff14108da2009-07-10 23:34:53 +000050 return PT->getInterfaceType();
Ted Kremenekc1ff3cd2008-04-30 22:48:21 +000051
Ted Kremenekc1ff3cd2008-04-30 22:48:21 +000052 return NULL;
Ted Kremenek4ba62832008-03-27 22:05:32 +000053}
54
Steve Naroff14108da2009-07-10 23:34:53 +000055static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
Daniel Dunbare013d682009-10-18 20:26:12 +000056 if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME))
57 return ReceiverType->getDecl()->getIdentifier()->getNameStart();
58 return NULL;
Ted Kremenek4ba62832008-03-27 22:05:32 +000059}
Ted Kremenek52755612008-03-27 17:17:22 +000060
Ted Kremenekf1ae7052008-04-03 17:57:38 +000061namespace {
Ted Kremenekb344f912008-09-21 19:01:39 +000062
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +000063class APIMisuse : public BugType {
Ted Kremenekf1ae7052008-04-03 17:57:38 +000064public:
Ted Kremenekcf118d42009-02-04 23:49:09 +000065 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
Ted Kremenekf1ae7052008-04-03 17:57:38 +000066};
Mike Stump1eb44332009-09-09 15:08:12 +000067
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +000068class BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenekcf118d42009-02-04 23:49:09 +000069 APIMisuse *BT;
70 BugReporter& BR;
Ted Kremenekf1ae7052008-04-03 17:57:38 +000071 ASTContext &Ctx;
Mike Stump1eb44332009-09-09 15:08:12 +000072
Daniel Dunbard777d582009-10-17 18:12:21 +000073 bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix);
Zhongxing Xu031ccc02009-08-06 12:48:26 +000074 bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME);
Mike Stump1eb44332009-09-09 15:08:12 +000075
Zhongxing Xu031ccc02009-08-06 12:48:26 +000076 bool CheckNilArg(ExplodedNode* N, unsigned Arg);
Ted Kremenekf1ae7052008-04-03 17:57:38 +000077
78public:
Mike Stump1eb44332009-09-09 15:08:12 +000079 BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br)
Ted Kremenek23ec48c2009-06-18 23:58:37 +000080 : BT(0), BR(br), Ctx(ctx) {}
Mike Stump1eb44332009-09-09 15:08:12 +000081
Zhongxing Xuc5619d92009-08-06 01:32:16 +000082 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump1eb44332009-09-09 15:08:12 +000083
84private:
85 void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekcf118d42009-02-04 23:49:09 +000086 std::string sbuf;
87 llvm::raw_string_ostream os(sbuf);
88 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
89 << ME->getSelector().getAsString() << "' cannot be nil.";
Mike Stump1eb44332009-09-09 15:08:12 +000090
Ted Kremenekcf118d42009-02-04 23:49:09 +000091 // Lazily create the BugType object for NilArg. This will be owned
92 // by the BugReporter object 'BR' once we call BR.EmitWarning.
93 if (!BT) BT = new APIMisuse("nil argument");
Mike Stump1eb44332009-09-09 15:08:12 +000094
Benjamin Kramer4988a9a2009-11-29 18:03:28 +000095 RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
Ted Kremenekcf118d42009-02-04 23:49:09 +000096 R->addRange(ME->getArg(Arg)->getSourceRange());
97 BR.EmitReport(R);
Ted Kremenekf1ae7052008-04-03 17:57:38 +000098 }
99};
Mike Stump1eb44332009-09-09 15:08:12 +0000100
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000101} // end anonymous namespace
102
103
104GRSimpleAPICheck*
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000105clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) {
Mike Stump1eb44332009-09-09 15:08:12 +0000106 return new BasicObjCFoundationChecks(Ctx, BR);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000107}
108
109
110
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000111bool BasicObjCFoundationChecks::Audit(ExplodedNode* N,
Ted Kremenek4adc81e2008-08-13 04:27:00 +0000112 GRStateManager&) {
Mike Stump1eb44332009-09-09 15:08:12 +0000113
Ted Kremenek5f85e172009-07-22 22:35:28 +0000114 const ObjCMessageExpr* ME =
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000115 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000116
Steve Naroff14108da2009-07-10 23:34:53 +0000117 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
Mike Stump1eb44332009-09-09 15:08:12 +0000118
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000119 if (!ReceiverType)
Nuno Lopesf7427942008-05-20 17:33:56 +0000120 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000121
Daniel Dunbard777d582009-10-17 18:12:21 +0000122 if (isNSString(ReceiverType,
Daniel Dunbar01eb9b92009-10-18 21:17:35 +0000123 ReceiverType->getDecl()->getIdentifier()->getName()))
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000124 return AuditNSString(N, ME);
125
Nuno Lopesf7427942008-05-20 17:33:56 +0000126 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000127}
128
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000129static inline bool isNil(SVal X) {
Mike Stump1eb44332009-09-09 15:08:12 +0000130 return isa<loc::ConcreteInt>(X);
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000131}
132
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000133//===----------------------------------------------------------------------===//
134// Error reporting.
135//===----------------------------------------------------------------------===//
136
Zhongxing Xu031ccc02009-08-06 12:48:26 +0000137bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) {
Ted Kremenek5f85e172009-07-22 22:35:28 +0000138 const ObjCMessageExpr* ME =
Ted Kremenek4ba62832008-03-27 22:05:32 +0000139 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000140
Ted Kremenek5f85e172009-07-22 22:35:28 +0000141 const Expr * E = ME->getArg(Arg);
Mike Stump1eb44332009-09-09 15:08:12 +0000142
Ted Kremenek13976632010-02-08 16:18:51 +0000143 if (isNil(N->getState()->getSVal(E))) {
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000144 WarnNilArg(N, ME, Arg);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000145 return true;
146 }
Mike Stump1eb44332009-09-09 15:08:12 +0000147
Ted Kremenek4ba62832008-03-27 22:05:32 +0000148 return false;
149}
150
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000151//===----------------------------------------------------------------------===//
152// NSString checking.
153//===----------------------------------------------------------------------===//
154
Steve Naroff14108da2009-07-10 23:34:53 +0000155bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T,
Daniel Dunbard777d582009-10-17 18:12:21 +0000156 llvm::StringRef ClassName) {
157 return ClassName == "NSString" || ClassName == "NSMutableString";
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000158}
159
Mike Stump1eb44332009-09-09 15:08:12 +0000160bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N,
Ted Kremenek5f85e172009-07-22 22:35:28 +0000161 const ObjCMessageExpr* ME) {
Mike Stump1eb44332009-09-09 15:08:12 +0000162
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000163 Selector S = ME->getSelector();
Mike Stump1eb44332009-09-09 15:08:12 +0000164
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000165 if (S.isUnarySelector())
166 return false;
167
168 // FIXME: This is going to be really slow doing these checks with
169 // lexical comparisons.
Mike Stump1eb44332009-09-09 15:08:12 +0000170
Benjamin Kramer064fb202010-02-08 19:51:59 +0000171 std::string NameStr = S.getAsString();
172 llvm::StringRef Name(NameStr);
173 assert(!Name.empty());
Mike Stump1eb44332009-09-09 15:08:12 +0000174
Benjamin Kramer064fb202010-02-08 19:51:59 +0000175 // FIXME: Checking for initWithFormat: will not work in most cases
176 // yet because [NSString alloc] returns id, not NSString*. We will
177 // need support for tracking expected-type information in the analyzer
178 // to find these errors.
179 if (Name == "caseInsensitiveCompare:" ||
180 Name == "compare:" ||
181 Name == "compare:options:" ||
182 Name == "compare:options:range:" ||
183 Name == "compare:options:range:locale:" ||
184 Name == "componentsSeparatedByCharactersInSet:" ||
185 Name == "initWithFormat:")
186 return CheckNilArg(N, 0);
Mike Stump1eb44332009-09-09 15:08:12 +0000187
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000188 return false;
189}
Ted Kremenek04bc8762008-06-26 23:59:48 +0000190
191//===----------------------------------------------------------------------===//
192// Error reporting.
193//===----------------------------------------------------------------------===//
194
195namespace {
Ted Kremenek04bc8762008-06-26 23:59:48 +0000196
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +0000197class AuditCFNumberCreate : public GRSimpleAPICheck {
Ted Kremenekcf118d42009-02-04 23:49:09 +0000198 APIMisuse* BT;
Mike Stump1eb44332009-09-09 15:08:12 +0000199
Ted Kremenek04bc8762008-06-26 23:59:48 +0000200 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
201 // it should always be passed with a call to Audit. The latter
202 // approach makes this class more stateless.
203 ASTContext& Ctx;
204 IdentifierInfo* II;
Ted Kremenekcf118d42009-02-04 23:49:09 +0000205 BugReporter& BR;
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000206
Ted Kremenek04bc8762008-06-26 23:59:48 +0000207public:
Mike Stump1eb44332009-09-09 15:08:12 +0000208 AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000209 : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
Mike Stump1eb44332009-09-09 15:08:12 +0000210
Ted Kremenekcf118d42009-02-04 23:49:09 +0000211 ~AuditCFNumberCreate() {}
Mike Stump1eb44332009-09-09 15:08:12 +0000212
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000213 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump1eb44332009-09-09 15:08:12 +0000214
Ted Kremenek04bc8762008-06-26 23:59:48 +0000215private:
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000216 void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N,
Mike Stump1eb44332009-09-09 15:08:12 +0000217 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000218};
219} // end anonymous namespace
220
221enum CFNumberType {
222 kCFNumberSInt8Type = 1,
223 kCFNumberSInt16Type = 2,
224 kCFNumberSInt32Type = 3,
225 kCFNumberSInt64Type = 4,
226 kCFNumberFloat32Type = 5,
227 kCFNumberFloat64Type = 6,
228 kCFNumberCharType = 7,
229 kCFNumberShortType = 8,
230 kCFNumberIntType = 9,
231 kCFNumberLongType = 10,
232 kCFNumberLongLongType = 11,
233 kCFNumberFloatType = 12,
234 kCFNumberDoubleType = 13,
235 kCFNumberCFIndexType = 14,
236 kCFNumberNSIntegerType = 15,
237 kCFNumberCGFloatType = 16
238};
239
240namespace {
241 template<typename T>
242 class Optional {
243 bool IsKnown;
244 T Val;
245 public:
246 Optional() : IsKnown(false), Val(0) {}
247 Optional(const T& val) : IsKnown(true), Val(val) {}
Mike Stump1eb44332009-09-09 15:08:12 +0000248
Ted Kremenek04bc8762008-06-26 23:59:48 +0000249 bool isKnown() const { return IsKnown; }
250
251 const T& getValue() const {
252 assert (isKnown());
253 return Val;
254 }
255
256 operator const T&() const {
257 return getValue();
258 }
259 };
260}
261
262static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
Nuno Lopes2550d702009-12-23 17:49:57 +0000263 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump1eb44332009-09-09 15:08:12 +0000264
Ted Kremenek04bc8762008-06-26 23:59:48 +0000265 if (i < kCFNumberCharType)
266 return FixedSize[i-1];
Mike Stump1eb44332009-09-09 15:08:12 +0000267
Ted Kremenek04bc8762008-06-26 23:59:48 +0000268 QualType T;
Mike Stump1eb44332009-09-09 15:08:12 +0000269
Ted Kremenek04bc8762008-06-26 23:59:48 +0000270 switch (i) {
271 case kCFNumberCharType: T = Ctx.CharTy; break;
272 case kCFNumberShortType: T = Ctx.ShortTy; break;
273 case kCFNumberIntType: T = Ctx.IntTy; break;
274 case kCFNumberLongType: T = Ctx.LongTy; break;
275 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
276 case kCFNumberFloatType: T = Ctx.FloatTy; break;
277 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
278 case kCFNumberCFIndexType:
279 case kCFNumberNSIntegerType:
280 case kCFNumberCGFloatType:
Mike Stump1eb44332009-09-09 15:08:12 +0000281 // FIXME: We need a way to map from names to Type*.
Ted Kremenek04bc8762008-06-26 23:59:48 +0000282 default:
283 return Optional<uint64_t>();
284 }
Mike Stump1eb44332009-09-09 15:08:12 +0000285
Ted Kremenek04bc8762008-06-26 23:59:48 +0000286 return Ctx.getTypeSize(T);
287}
288
289#if 0
290static const char* GetCFNumberTypeStr(uint64_t i) {
291 static const char* Names[] = {
292 "kCFNumberSInt8Type",
293 "kCFNumberSInt16Type",
294 "kCFNumberSInt32Type",
295 "kCFNumberSInt64Type",
296 "kCFNumberFloat32Type",
297 "kCFNumberFloat64Type",
298 "kCFNumberCharType",
299 "kCFNumberShortType",
300 "kCFNumberIntType",
301 "kCFNumberLongType",
302 "kCFNumberLongLongType",
303 "kCFNumberFloatType",
304 "kCFNumberDoubleType",
305 "kCFNumberCFIndexType",
306 "kCFNumberNSIntegerType",
307 "kCFNumberCGFloatType"
308 };
Mike Stump1eb44332009-09-09 15:08:12 +0000309
Ted Kremenek04bc8762008-06-26 23:59:48 +0000310 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
311}
312#endif
313
Mike Stump1eb44332009-09-09 15:08:12 +0000314bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
Ted Kremenek5f85e172009-07-22 22:35:28 +0000315 const CallExpr* CE =
316 cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000317 const Expr* Callee = CE->getCallee();
Ted Kremenek13976632010-02-08 16:18:51 +0000318 SVal CallV = N->getState()->getSVal(Callee);
Zhongxing Xu369f4472009-04-20 05:24:46 +0000319 const FunctionDecl* FD = CallV.getAsFunctionDecl();
Ted Kremenek04bc8762008-06-26 23:59:48 +0000320
Zhongxing Xu369f4472009-04-20 05:24:46 +0000321 if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
Ted Kremenek04bc8762008-06-26 23:59:48 +0000322 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000323
Ted Kremenek04bc8762008-06-26 23:59:48 +0000324 // Get the value of the "theType" argument.
Ted Kremenek13976632010-02-08 16:18:51 +0000325 SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
Mike Stump1eb44332009-09-09 15:08:12 +0000326
Ted Kremenek04bc8762008-06-26 23:59:48 +0000327 // FIXME: We really should allow ranges of valid theType values, and
328 // bifurcate the state appropriately.
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000329 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Mike Stump1eb44332009-09-09 15:08:12 +0000330
Ted Kremenek04bc8762008-06-26 23:59:48 +0000331 if (!V)
332 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000333
Ted Kremenek04bc8762008-06-26 23:59:48 +0000334 uint64_t NumberKind = V->getValue().getLimitedValue();
335 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000336
Ted Kremenek04bc8762008-06-26 23:59:48 +0000337 // FIXME: In some cases we can emit an error.
338 if (!TargetSize.isKnown())
339 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000340
Ted Kremenek04bc8762008-06-26 23:59:48 +0000341 // Look at the value of the integer being passed by reference. Essentially
342 // we want to catch cases where the value passed in is not equal to the
343 // size of the type being created.
Ted Kremenek13976632010-02-08 16:18:51 +0000344 SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
Mike Stump1eb44332009-09-09 15:08:12 +0000345
Ted Kremenek04bc8762008-06-26 23:59:48 +0000346 // FIXME: Eventually we should handle arbitrary locations. We can do this
347 // by having an enhanced memory model that does low-level typing.
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000348 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000349
350 if (!LV)
351 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000352
Zhongxing Xu479529e2009-11-10 02:17:20 +0000353 const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
Ted Kremenek5e77eba2009-07-29 18:17:40 +0000354
355 if (!R)
356 return false;
357
Zhongxing Xu018220c2010-08-11 06:10:55 +0000358 QualType T = Ctx.getCanonicalType(R->getValueType());
Mike Stump1eb44332009-09-09 15:08:12 +0000359
Ted Kremenek04bc8762008-06-26 23:59:48 +0000360 // FIXME: If the pointee isn't an integer type, should we flag a warning?
361 // People can do weird stuff with pointers.
Mike Stump1eb44332009-09-09 15:08:12 +0000362
363 if (!T->isIntegerType())
Ted Kremenek04bc8762008-06-26 23:59:48 +0000364 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000365
Ted Kremenek04bc8762008-06-26 23:59:48 +0000366 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump1eb44332009-09-09 15:08:12 +0000367
Ted Kremenek04bc8762008-06-26 23:59:48 +0000368 // CHECK: is SourceSize == TargetSize
Mike Stump1eb44332009-09-09 15:08:12 +0000369
Ted Kremenek04bc8762008-06-26 23:59:48 +0000370 if (SourceSize == TargetSize)
371 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000372
Ted Kremenek9e240492008-10-04 05:50:14 +0000373 AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000374
Ted Kremenek04bc8762008-06-26 23:59:48 +0000375 // FIXME: We can actually create an abstract "CFNumber" object that has
376 // the bits initialized to the provided values.
377 return SourceSize < TargetSize;
378}
379
Ted Kremenek5f85e172009-07-22 22:35:28 +0000380void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex,
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000381 ExplodedNode *N,
Ted Kremenek04bc8762008-06-26 23:59:48 +0000382 uint64_t SourceSize, uint64_t TargetSize,
383 uint64_t NumberKind) {
Mike Stump1eb44332009-09-09 15:08:12 +0000384
Ted Kremenekcf118d42009-02-04 23:49:09 +0000385 std::string sbuf;
386 llvm::raw_string_ostream os(sbuf);
Mike Stump1eb44332009-09-09 15:08:12 +0000387
Ted Kremenek04bc8762008-06-26 23:59:48 +0000388 os << (SourceSize == 8 ? "An " : "A ")
389 << SourceSize << " bit integer is used to initialize a CFNumber "
390 "object that represents "
391 << (TargetSize == 8 ? "an " : "a ")
Mike Stump1eb44332009-09-09 15:08:12 +0000392 << TargetSize << " bit integer. ";
Ted Kremenek04bc8762008-06-26 23:59:48 +0000393
394 if (SourceSize < TargetSize)
395 os << (TargetSize - SourceSize)
Mike Stump1eb44332009-09-09 15:08:12 +0000396 << " bits of the CFNumber value will be garbage." ;
Ted Kremenek04bc8762008-06-26 23:59:48 +0000397 else
398 os << (SourceSize - TargetSize)
399 << " bits of the input integer will be lost.";
Mike Stump1eb44332009-09-09 15:08:12 +0000400
Ted Kremenekcf118d42009-02-04 23:49:09 +0000401 // Lazily create the BugType object. This will be owned
402 // by the BugReporter object 'BR' once we call BR.EmitWarning.
403 if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate");
Benjamin Kramer4988a9a2009-11-29 18:03:28 +0000404 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
Ted Kremenekcf118d42009-02-04 23:49:09 +0000405 report->addRange(Ex->getSourceRange());
406 BR.EmitReport(report);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000407}
408
409GRSimpleAPICheck*
Mike Stump1eb44332009-09-09 15:08:12 +0000410clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000411 return new AuditCFNumberCreate(Ctx, BR);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000412}
413
Ted Kremenek78d46242008-07-22 16:21:24 +0000414//===----------------------------------------------------------------------===//
Jordy Rose61fb55c2010-07-06 02:34:42 +0000415// CFRetain/CFRelease checking for null arguments.
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000416//===----------------------------------------------------------------------===//
417
418namespace {
Jordy Rose61fb55c2010-07-06 02:34:42 +0000419class CFRetainReleaseChecker : public CheckerVisitor<CFRetainReleaseChecker> {
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000420 APIMisuse *BT;
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000421 IdentifierInfo *Retain, *Release;
Mike Stump1eb44332009-09-09 15:08:12 +0000422
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000423public:
Jordy Rose61fb55c2010-07-06 02:34:42 +0000424 CFRetainReleaseChecker(ASTContext& Ctx): BT(NULL),
425 Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease"))
426 {}
Mike Stump1eb44332009-09-09 15:08:12 +0000427
Jordy Rose61fb55c2010-07-06 02:34:42 +0000428 static void *getTag() { static int x = 0; return &x; }
Mike Stump1eb44332009-09-09 15:08:12 +0000429
Jordy Rose61fb55c2010-07-06 02:34:42 +0000430 void PreVisitCallExpr(CheckerContext& C, const CallExpr* CE);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000431};
432} // end anonymous namespace
433
434
Jordy Rose61fb55c2010-07-06 02:34:42 +0000435void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C,
436 const CallExpr* CE) {
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000437 // If the CallExpr doesn't have exactly 1 argument just give up checking.
438 if (CE->getNumArgs() != 1)
Jordy Rose61fb55c2010-07-06 02:34:42 +0000439 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000440
Jordy Rose61fb55c2010-07-06 02:34:42 +0000441 // Get the function declaration of the callee.
442 const GRState* state = C.getState();
Ted Kremenek13976632010-02-08 16:18:51 +0000443 SVal X = state->getSVal(CE->getCallee());
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000444 const FunctionDecl* FD = X.getAsFunctionDecl();
Mike Stump1eb44332009-09-09 15:08:12 +0000445
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000446 if (!FD)
Jordy Rose61fb55c2010-07-06 02:34:42 +0000447 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000448
Jordy Rose61fb55c2010-07-06 02:34:42 +0000449 // Check if we called CFRetain/CFRelease.
Mike Stump1eb44332009-09-09 15:08:12 +0000450 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000451 if (!(FuncII == Retain || FuncII == Release))
Jordy Rose61fb55c2010-07-06 02:34:42 +0000452 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000453
Jordy Rose61fb55c2010-07-06 02:34:42 +0000454 // FIXME: The rest of this just checks that the argument is non-null.
455 // It should probably be refactored and combined with AttrNonNullChecker.
456
457 // Get the argument's value.
458 const Expr *Arg = CE->getArg(0);
459 SVal ArgVal = state->getSVal(Arg);
460 DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
461 if (!DefArgVal)
462 return;
463
464 // Get a NULL value.
465 ValueManager &ValMgr = C.getValueManager();
466 DefinedSVal Zero = cast<DefinedSVal>(ValMgr.makeZeroVal(Arg->getType()));
467
468 // Make an expression asserting that they're equal.
469 SValuator &SVator = ValMgr.getSValuator();
470 DefinedOrUnknownSVal ArgIsNull = SVator.EvalEQ(state, Zero, *DefArgVal);
471
472 // Are they equal?
473 const GRState *stateTrue, *stateFalse;
474 llvm::tie(stateTrue, stateFalse) = state->Assume(ArgIsNull);
475
476 if (stateTrue && !stateFalse) {
477 ExplodedNode *N = C.GenerateSink(stateTrue);
478 if (!N)
479 return;
480
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000481 if (!BT)
482 BT = new APIMisuse("null passed to CFRetain/CFRelease");
Mike Stump1eb44332009-09-09 15:08:12 +0000483
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000484 const char *description = (FuncII == Retain)
485 ? "Null pointer argument in call to CFRetain"
486 : "Null pointer argument in call to CFRelease";
487
Jordy Rose61fb55c2010-07-06 02:34:42 +0000488 EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N);
489 report->addRange(Arg->getSourceRange());
490 report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg);
491
492 C.EmitReport(report);
493 return;
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000494 }
495
Jordy Rose61fb55c2010-07-06 02:34:42 +0000496 // From here on, we know the argument is non-null.
497 C.addTransition(stateFalse);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000498}
499
500//===----------------------------------------------------------------------===//
Ted Kremenek50e837b2009-11-20 05:27:05 +0000501// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
502//===----------------------------------------------------------------------===//
503
504namespace {
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +0000505class ClassReleaseChecker :
Ted Kremenek50e837b2009-11-20 05:27:05 +0000506 public CheckerVisitor<ClassReleaseChecker> {
507 Selector releaseS;
508 Selector retainS;
509 Selector autoreleaseS;
510 Selector drainS;
511 BugType *BT;
512public:
513 ClassReleaseChecker(ASTContext &Ctx)
514 : releaseS(GetNullarySelector("release", Ctx)),
515 retainS(GetNullarySelector("retain", Ctx)),
516 autoreleaseS(GetNullarySelector("autorelease", Ctx)),
517 drainS(GetNullarySelector("drain", Ctx)),
518 BT(0) {}
519
520 static void *getTag() { static int x = 0; return &x; }
521
522 void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);
523};
524}
525
526void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C,
527 const ObjCMessageExpr *ME) {
Douglas Gregor04badcf2010-04-21 00:45:42 +0000528 ObjCInterfaceDecl *Class = 0;
529 switch (ME->getReceiverKind()) {
530 case ObjCMessageExpr::Class:
John McCall506b57e2010-05-17 21:00:27 +0000531 Class = ME->getClassReceiver()->getAs<ObjCObjectType>()->getInterface();
Douglas Gregor04badcf2010-04-21 00:45:42 +0000532 break;
533
534 case ObjCMessageExpr::SuperClass:
John McCall506b57e2010-05-17 21:00:27 +0000535 Class = ME->getSuperType()->getAs<ObjCObjectType>()->getInterface();
Douglas Gregor04badcf2010-04-21 00:45:42 +0000536 break;
537
538 case ObjCMessageExpr::Instance:
539 case ObjCMessageExpr::SuperInstance:
Ted Kremenek50e837b2009-11-20 05:27:05 +0000540 return;
Douglas Gregor04badcf2010-04-21 00:45:42 +0000541 }
542
Ted Kremenek50e837b2009-11-20 05:27:05 +0000543 Selector S = ME->getSelector();
Benjamin Kramer921ddc42009-11-20 10:03:00 +0000544 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremenek50e837b2009-11-20 05:27:05 +0000545 return;
546
547 if (!BT)
548 BT = new APIMisuse("message incorrectly sent to class instead of class "
549 "instance");
550
Ted Kremenek19d67b52009-11-23 22:22:01 +0000551 ExplodedNode *N = C.GenerateNode();
552
Ted Kremenek50e837b2009-11-20 05:27:05 +0000553 if (!N)
554 return;
555
Ted Kremenek50e837b2009-11-20 05:27:05 +0000556 llvm::SmallString<200> buf;
557 llvm::raw_svector_ostream os(buf);
558
559 os << "The '" << S.getAsString() << "' message should be sent to instances "
Douglas Gregor04badcf2010-04-21 00:45:42 +0000560 "of class '" << Class->getName()
Ted Kremenek50e837b2009-11-20 05:27:05 +0000561 << "' and not the class directly";
562
563 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
564 report->addRange(ME->getSourceRange());
565 C.EmitReport(report);
566}
567
568//===----------------------------------------------------------------------===//
Ted Kremenek78d46242008-07-22 16:21:24 +0000569// Check registration.
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000570//===----------------------------------------------------------------------===//
Ted Kremenek78d46242008-07-22 16:21:24 +0000571
Zhongxing Xu5ab128b2009-08-21 02:18:44 +0000572void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) {
Ted Kremenek78d46242008-07-22 16:21:24 +0000573 ASTContext& Ctx = Eng.getContext();
Ted Kremenekcf118d42009-02-04 23:49:09 +0000574 BugReporter &BR = Eng.getBugReporter();
Ted Kremenek78d46242008-07-22 16:21:24 +0000575
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000576 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
Ted Kremenek78d46242008-07-22 16:21:24 +0000577 Stmt::ObjCMessageExprClass);
Mike Stump1eb44332009-09-09 15:08:12 +0000578 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
Mike Stump1eb44332009-09-09 15:08:12 +0000579
Zhongxing Xu5ab128b2009-08-21 02:18:44 +0000580 RegisterNSErrorChecks(BR, Eng, D);
Ted Kremenek54cb7cc2009-11-03 08:03:59 +0000581 RegisterNSAutoreleasePoolChecks(Eng);
Jordy Rose61fb55c2010-07-06 02:34:42 +0000582
583 Eng.registerCheck(new CFRetainReleaseChecker(Ctx));
Ted Kremenek50e837b2009-11-20 05:27:05 +0000584 Eng.registerCheck(new ClassReleaseChecker(Ctx));
Ted Kremenek78d46242008-07-22 16:21:24 +0000585}