blob: 810d0fbb997a2545d7da0c78064753cf803f7c9f [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) {
34 const Expr* Receiver = ME->getReceiver();
Mike Stump1eb44332009-09-09 15:08:12 +000035
Ted Kremenek4ba62832008-03-27 22:05:32 +000036 if (!Receiver)
37 return NULL;
Mike Stump1eb44332009-09-09 15:08:12 +000038
Steve Naroff14108da2009-07-10 23:34:53 +000039 if (const ObjCObjectPointerType *PT =
John McCall183700f2009-09-21 23:43:11 +000040 Receiver->getType()->getAs<ObjCObjectPointerType>())
Steve Naroff14108da2009-07-10 23:34:53 +000041 return PT->getInterfaceType();
Ted Kremenekc1ff3cd2008-04-30 22:48:21 +000042
Ted Kremenekc1ff3cd2008-04-30 22:48:21 +000043 return NULL;
Ted Kremenek4ba62832008-03-27 22:05:32 +000044}
45
Steve Naroff14108da2009-07-10 23:34:53 +000046static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
Daniel Dunbare013d682009-10-18 20:26:12 +000047 if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME))
48 return ReceiverType->getDecl()->getIdentifier()->getNameStart();
49 return NULL;
Ted Kremenek4ba62832008-03-27 22:05:32 +000050}
Ted Kremenek52755612008-03-27 17:17:22 +000051
Ted Kremenekf1ae7052008-04-03 17:57:38 +000052namespace {
Ted Kremenekb344f912008-09-21 19:01:39 +000053
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +000054class APIMisuse : public BugType {
Ted Kremenekf1ae7052008-04-03 17:57:38 +000055public:
Ted Kremenekcf118d42009-02-04 23:49:09 +000056 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
Ted Kremenekf1ae7052008-04-03 17:57:38 +000057};
Mike Stump1eb44332009-09-09 15:08:12 +000058
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +000059class BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenekcf118d42009-02-04 23:49:09 +000060 APIMisuse *BT;
61 BugReporter& BR;
Ted Kremenekf1ae7052008-04-03 17:57:38 +000062 ASTContext &Ctx;
Mike Stump1eb44332009-09-09 15:08:12 +000063
Daniel Dunbard777d582009-10-17 18:12:21 +000064 bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix);
Zhongxing Xu031ccc02009-08-06 12:48:26 +000065 bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME);
Mike Stump1eb44332009-09-09 15:08:12 +000066
67 void Warn(ExplodedNode* N, const Expr* E, const std::string& s);
Zhongxing Xu031ccc02009-08-06 12:48:26 +000068 void WarnNilArg(ExplodedNode* N, const Expr* E);
Mike Stump1eb44332009-09-09 15:08:12 +000069
Zhongxing Xu031ccc02009-08-06 12:48:26 +000070 bool CheckNilArg(ExplodedNode* N, unsigned Arg);
Ted Kremenekf1ae7052008-04-03 17:57:38 +000071
72public:
Mike Stump1eb44332009-09-09 15:08:12 +000073 BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br)
Ted Kremenek23ec48c2009-06-18 23:58:37 +000074 : BT(0), BR(br), Ctx(ctx) {}
Mike Stump1eb44332009-09-09 15:08:12 +000075
Zhongxing Xuc5619d92009-08-06 01:32:16 +000076 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump1eb44332009-09-09 15:08:12 +000077
78private:
79 void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekcf118d42009-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 Stump1eb44332009-09-09 15:08:12 +000084
Ted Kremenekcf118d42009-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 Stump1eb44332009-09-09 15:08:12 +000088
Benjamin Kramer4988a9a2009-11-29 18:03:28 +000089 RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
Ted Kremenekcf118d42009-02-04 23:49:09 +000090 R->addRange(ME->getArg(Arg)->getSourceRange());
91 BR.EmitReport(R);
Ted Kremenekf1ae7052008-04-03 17:57:38 +000092 }
93};
Mike Stump1eb44332009-09-09 15:08:12 +000094
Ted Kremenekf1ae7052008-04-03 17:57:38 +000095} // end anonymous namespace
96
97
98GRSimpleAPICheck*
Ted Kremenek23ec48c2009-06-18 23:58:37 +000099clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) {
Mike Stump1eb44332009-09-09 15:08:12 +0000100 return new BasicObjCFoundationChecks(Ctx, BR);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000101}
102
103
104
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000105bool BasicObjCFoundationChecks::Audit(ExplodedNode* N,
Ted Kremenek4adc81e2008-08-13 04:27:00 +0000106 GRStateManager&) {
Mike Stump1eb44332009-09-09 15:08:12 +0000107
Ted Kremenek5f85e172009-07-22 22:35:28 +0000108 const ObjCMessageExpr* ME =
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000109 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000110
Steve Naroff14108da2009-07-10 23:34:53 +0000111 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
Mike Stump1eb44332009-09-09 15:08:12 +0000112
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000113 if (!ReceiverType)
Nuno Lopesf7427942008-05-20 17:33:56 +0000114 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000115
Daniel Dunbard777d582009-10-17 18:12:21 +0000116 if (isNSString(ReceiverType,
Daniel Dunbar01eb9b92009-10-18 21:17:35 +0000117 ReceiverType->getDecl()->getIdentifier()->getName()))
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000118 return AuditNSString(N, ME);
119
Nuno Lopesf7427942008-05-20 17:33:56 +0000120 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000121}
122
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000123static inline bool isNil(SVal X) {
Mike Stump1eb44332009-09-09 15:08:12 +0000124 return isa<loc::ConcreteInt>(X);
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000125}
126
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000127//===----------------------------------------------------------------------===//
128// Error reporting.
129//===----------------------------------------------------------------------===//
130
Zhongxing Xu031ccc02009-08-06 12:48:26 +0000131bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) {
Ted Kremenek5f85e172009-07-22 22:35:28 +0000132 const ObjCMessageExpr* ME =
Ted Kremenek4ba62832008-03-27 22:05:32 +0000133 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000134
Ted Kremenek5f85e172009-07-22 22:35:28 +0000135 const Expr * E = ME->getArg(Arg);
Mike Stump1eb44332009-09-09 15:08:12 +0000136
Ted Kremenek13976632010-02-08 16:18:51 +0000137 if (isNil(N->getState()->getSVal(E))) {
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000138 WarnNilArg(N, ME, Arg);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000139 return true;
140 }
Mike Stump1eb44332009-09-09 15:08:12 +0000141
Ted Kremenek4ba62832008-03-27 22:05:32 +0000142 return false;
143}
144
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000145//===----------------------------------------------------------------------===//
146// NSString checking.
147//===----------------------------------------------------------------------===//
148
Steve Naroff14108da2009-07-10 23:34:53 +0000149bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T,
Daniel Dunbard777d582009-10-17 18:12:21 +0000150 llvm::StringRef ClassName) {
151 return ClassName == "NSString" || ClassName == "NSMutableString";
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000152}
153
Mike Stump1eb44332009-09-09 15:08:12 +0000154bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N,
Ted Kremenek5f85e172009-07-22 22:35:28 +0000155 const ObjCMessageExpr* ME) {
Mike Stump1eb44332009-09-09 15:08:12 +0000156
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000157 Selector S = ME->getSelector();
Mike Stump1eb44332009-09-09 15:08:12 +0000158
Ted Kremenek99c6ad32008-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 Stump1eb44332009-09-09 15:08:12 +0000164
Benjamin Kramer064fb202010-02-08 19:51:59 +0000165 std::string NameStr = S.getAsString();
166 llvm::StringRef Name(NameStr);
167 assert(!Name.empty());
Mike Stump1eb44332009-09-09 15:08:12 +0000168
Benjamin Kramer064fb202010-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 Stump1eb44332009-09-09 15:08:12 +0000181
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000182 return false;
183}
Ted Kremenek04bc8762008-06-26 23:59:48 +0000184
185//===----------------------------------------------------------------------===//
186// Error reporting.
187//===----------------------------------------------------------------------===//
188
189namespace {
Ted Kremenek04bc8762008-06-26 23:59:48 +0000190
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +0000191class AuditCFNumberCreate : public GRSimpleAPICheck {
Ted Kremenekcf118d42009-02-04 23:49:09 +0000192 APIMisuse* BT;
Mike Stump1eb44332009-09-09 15:08:12 +0000193
Ted Kremenek04bc8762008-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 Kremenekcf118d42009-02-04 23:49:09 +0000199 BugReporter& BR;
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000200
Ted Kremenek04bc8762008-06-26 23:59:48 +0000201public:
Mike Stump1eb44332009-09-09 15:08:12 +0000202 AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000203 : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
Mike Stump1eb44332009-09-09 15:08:12 +0000204
Ted Kremenekcf118d42009-02-04 23:49:09 +0000205 ~AuditCFNumberCreate() {}
Mike Stump1eb44332009-09-09 15:08:12 +0000206
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000207 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump1eb44332009-09-09 15:08:12 +0000208
Ted Kremenek04bc8762008-06-26 23:59:48 +0000209private:
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000210 void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N,
Mike Stump1eb44332009-09-09 15:08:12 +0000211 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000242
Ted Kremenek04bc8762008-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 Lopes2550d702009-12-23 17:49:57 +0000257 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump1eb44332009-09-09 15:08:12 +0000258
Ted Kremenek04bc8762008-06-26 23:59:48 +0000259 if (i < kCFNumberCharType)
260 return FixedSize[i-1];
Mike Stump1eb44332009-09-09 15:08:12 +0000261
Ted Kremenek04bc8762008-06-26 23:59:48 +0000262 QualType T;
Mike Stump1eb44332009-09-09 15:08:12 +0000263
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000275 // FIXME: We need a way to map from names to Type*.
Ted Kremenek04bc8762008-06-26 23:59:48 +0000276 default:
277 return Optional<uint64_t>();
278 }
Mike Stump1eb44332009-09-09 15:08:12 +0000279
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000303
Ted Kremenek04bc8762008-06-26 23:59:48 +0000304 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
305}
306#endif
307
Mike Stump1eb44332009-09-09 15:08:12 +0000308bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
Ted Kremenek5f85e172009-07-22 22:35:28 +0000309 const CallExpr* CE =
310 cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000311 const Expr* Callee = CE->getCallee();
Ted Kremenek13976632010-02-08 16:18:51 +0000312 SVal CallV = N->getState()->getSVal(Callee);
Zhongxing Xu369f4472009-04-20 05:24:46 +0000313 const FunctionDecl* FD = CallV.getAsFunctionDecl();
Ted Kremenek04bc8762008-06-26 23:59:48 +0000314
Zhongxing Xu369f4472009-04-20 05:24:46 +0000315 if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
Ted Kremenek04bc8762008-06-26 23:59:48 +0000316 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000317
Ted Kremenek04bc8762008-06-26 23:59:48 +0000318 // Get the value of the "theType" argument.
Ted Kremenek13976632010-02-08 16:18:51 +0000319 SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
Mike Stump1eb44332009-09-09 15:08:12 +0000320
Ted Kremenek04bc8762008-06-26 23:59:48 +0000321 // FIXME: We really should allow ranges of valid theType values, and
322 // bifurcate the state appropriately.
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000323 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Mike Stump1eb44332009-09-09 15:08:12 +0000324
Ted Kremenek04bc8762008-06-26 23:59:48 +0000325 if (!V)
326 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000327
Ted Kremenek04bc8762008-06-26 23:59:48 +0000328 uint64_t NumberKind = V->getValue().getLimitedValue();
329 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000330
Ted Kremenek04bc8762008-06-26 23:59:48 +0000331 // FIXME: In some cases we can emit an error.
332 if (!TargetSize.isKnown())
333 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000334
Ted Kremenek04bc8762008-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 Kremenek13976632010-02-08 16:18:51 +0000338 SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
Mike Stump1eb44332009-09-09 15:08:12 +0000339
Ted Kremenek04bc8762008-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 Xu1c96b242008-10-17 05:57:07 +0000342 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000343
344 if (!LV)
345 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000346
Zhongxing Xu479529e2009-11-10 02:17:20 +0000347 const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
Ted Kremenek5e77eba2009-07-29 18:17:40 +0000348
349 if (!R)
350 return false;
351
Zhongxing Xua82d8aa2009-05-09 03:57:34 +0000352 QualType T = Ctx.getCanonicalType(R->getValueType(Ctx));
Mike Stump1eb44332009-09-09 15:08:12 +0000353
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000356
357 if (!T->isIntegerType())
Ted Kremenek04bc8762008-06-26 23:59:48 +0000358 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000359
Ted Kremenek04bc8762008-06-26 23:59:48 +0000360 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump1eb44332009-09-09 15:08:12 +0000361
Ted Kremenek04bc8762008-06-26 23:59:48 +0000362 // CHECK: is SourceSize == TargetSize
Mike Stump1eb44332009-09-09 15:08:12 +0000363
Ted Kremenek04bc8762008-06-26 23:59:48 +0000364 if (SourceSize == TargetSize)
365 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000366
Ted Kremenek9e240492008-10-04 05:50:14 +0000367 AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000368
Ted Kremenek04bc8762008-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 Kremenek5f85e172009-07-22 22:35:28 +0000374void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex,
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000375 ExplodedNode *N,
Ted Kremenek04bc8762008-06-26 23:59:48 +0000376 uint64_t SourceSize, uint64_t TargetSize,
377 uint64_t NumberKind) {
Mike Stump1eb44332009-09-09 15:08:12 +0000378
Ted Kremenekcf118d42009-02-04 23:49:09 +0000379 std::string sbuf;
380 llvm::raw_string_ostream os(sbuf);
Mike Stump1eb44332009-09-09 15:08:12 +0000381
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000386 << TargetSize << " bit integer. ";
Ted Kremenek04bc8762008-06-26 23:59:48 +0000387
388 if (SourceSize < TargetSize)
389 os << (TargetSize - SourceSize)
Mike Stump1eb44332009-09-09 15:08:12 +0000390 << " bits of the CFNumber value will be garbage." ;
Ted Kremenek04bc8762008-06-26 23:59:48 +0000391 else
392 os << (SourceSize - TargetSize)
393 << " bits of the input integer will be lost.";
Mike Stump1eb44332009-09-09 15:08:12 +0000394
Ted Kremenekcf118d42009-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 Kramer4988a9a2009-11-29 18:03:28 +0000398 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
Ted Kremenekcf118d42009-02-04 23:49:09 +0000399 report->addRange(Ex->getSourceRange());
400 BR.EmitReport(report);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000401}
402
403GRSimpleAPICheck*
Mike Stump1eb44332009-09-09 15:08:12 +0000404clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000405 return new AuditCFNumberCreate(Ctx, BR);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000406}
407
Ted Kremenek78d46242008-07-22 16:21:24 +0000408//===----------------------------------------------------------------------===//
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000409// CFRetain/CFRelease auditing for null arguments.
410//===----------------------------------------------------------------------===//
411
412namespace {
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +0000413class AuditCFRetainRelease : public GRSimpleAPICheck {
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000414 APIMisuse *BT;
Mike Stump1eb44332009-09-09 15:08:12 +0000415
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000422
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000423public:
Mike Stump1eb44332009-09-09 15:08:12 +0000424 AuditCFRetainRelease(ASTContext& ctx, BugReporter& br)
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000428
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000429 ~AuditCFRetainRelease() {}
Mike Stump1eb44332009-09-09 15:08:12 +0000430
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000431 bool Audit(ExplodedNode* N, GRStateManager&);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000432};
433} // end anonymous namespace
434
435
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000436bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) {
Ted Kremenek5f85e172009-07-22 22:35:28 +0000437 const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump1eb44332009-09-09 15:08:12 +0000438
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000442
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000443 // Check if we called CFRetain/CFRelease.
444 const GRState* state = N->getState();
Ted Kremenek13976632010-02-08 16:18:51 +0000445 SVal X = state->getSVal(CE->getCallee());
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000446 const FunctionDecl* FD = X.getAsFunctionDecl();
Mike Stump1eb44332009-09-09 15:08:12 +0000447
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000448 if (!FD)
449 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000450
451 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000452 if (!(FuncII == Retain || FuncII == Release))
453 return false;
Mike Stump1eb44332009-09-09 15:08:12 +0000454
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000464
Ted Kremenek79b4f7d2009-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 Stump1eb44332009-09-09 15:08:12 +0000477
478
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000479GRSimpleAPICheck*
Mike Stump1eb44332009-09-09 15:08:12 +0000480clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000481 return new AuditCFRetainRelease(Ctx, BR);
482}
483
484//===----------------------------------------------------------------------===//
Ted Kremenek50e837b2009-11-20 05:27:05 +0000485// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
486//===----------------------------------------------------------------------===//
487
488namespace {
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +0000489class ClassReleaseChecker :
Ted Kremenek50e837b2009-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 Kramer921ddc42009-11-20 10:03:00 +0000518 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremenek50e837b2009-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 Kremenek19d67b52009-11-23 22:22:01 +0000525 ExplodedNode *N = C.GenerateNode();
526
Ted Kremenek50e837b2009-11-20 05:27:05 +0000527 if (!N)
528 return;
529
Ted Kremenek50e837b2009-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 Kremenek78d46242008-07-22 16:21:24 +0000543// Check registration.
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000544//===----------------------------------------------------------------------===//
Ted Kremenek78d46242008-07-22 16:21:24 +0000545
Zhongxing Xu5ab128b2009-08-21 02:18:44 +0000546void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) {
Ted Kremenek78d46242008-07-22 16:21:24 +0000547 ASTContext& Ctx = Eng.getContext();
Ted Kremenekcf118d42009-02-04 23:49:09 +0000548 BugReporter &BR = Eng.getBugReporter();
Ted Kremenek78d46242008-07-22 16:21:24 +0000549
Ted Kremenek23ec48c2009-06-18 23:58:37 +0000550 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
Ted Kremenek78d46242008-07-22 16:21:24 +0000551 Stmt::ObjCMessageExprClass);
Mike Stump1eb44332009-09-09 15:08:12 +0000552 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000553 Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass);
Mike Stump1eb44332009-09-09 15:08:12 +0000554
Zhongxing Xu5ab128b2009-08-21 02:18:44 +0000555 RegisterNSErrorChecks(BR, Eng, D);
Ted Kremenek54cb7cc2009-11-03 08:03:59 +0000556 RegisterNSAutoreleasePoolChecks(Eng);
Ted Kremenek50e837b2009-11-20 05:27:05 +0000557 Eng.registerCheck(new ClassReleaseChecker(Ctx));
Ted Kremenek78d46242008-07-22 16:21:24 +0000558}