blob: c4d73ab9555eb92c6161606c1dfa16e6d425827d [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 Kremenekc0414922008-03-27 07:25:52 +000018#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
19#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h"
Ted Kremenek1f352db2008-07-22 16:21:24 +000020#include "clang/Analysis/PathSensitive/GRExprEngine.h"
Ted Kremenek5ab5a1b2008-08-13 04:27:00 +000021#include "clang/Analysis/PathSensitive/GRState.h"
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000022#include "clang/Analysis/PathSensitive/BugReporter.h"
Ted Kremenek5ca90a22008-10-04 05:50:14 +000023#include "clang/Analysis/PathSensitive/MemRegion.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000024#include "clang/Analysis/PathDiagnostic.h"
Ted Kremenek1f352db2008-07-22 16:21:24 +000025#include "clang/Analysis/LocalCheckers.h"
Daniel Dunbar6e8aa532008-08-11 05:35:13 +000026#include "clang/AST/DeclObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000027#include "clang/AST/Expr.h"
Steve Naroff021ca182008-05-29 21:12:08 +000028#include "clang/AST/ExprObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000029#include "clang/AST/ASTContext.h"
30#include "llvm/Support/Compiler.h"
31
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) {
48 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
Ted Kremenek276278e2008-03-27 22:05:32 +000049 return ReceiverType ? ReceiverType->getDecl()->getIdentifier()->getName()
50 : NULL;
51}
Ted Kremeneka4d60b62008-03-27 17:17:22 +000052
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000053namespace {
Ted Kremenek638e2802008-09-21 19:01:39 +000054
Ted Kremenekfc5d0672009-02-04 23:49:09 +000055class VISIBILITY_HIDDEN 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
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000060class VISIBILITY_HIDDEN 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
Ted Kremenekfc5d0672009-02-04 23:49:09 +000090 RangedBugReport *R = new RangedBugReport(*BT, os.str().c_str(), N);
91 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,
118 ReceiverType->getDecl()->getIdentifier()->getNameStr()))
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 Kremenek095f1a92009-06-18 23:58:37 +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
Chris Lattnere4b95692008-11-24 03:33:13 +0000166 std::string name = S.getAsString();
Ted Kremenek2e4e7cc2008-03-27 21:23:57 +0000167 assert (!name.empty());
168 const char* cstr = &name[0];
169 unsigned len = name.size();
Mike Stump11289f42009-09-09 15:08:12 +0000170
Ted Kremenek2e4e7cc2008-03-27 21:23:57 +0000171 switch (len) {
172 default:
173 break;
Mike Stump11289f42009-09-09 15:08:12 +0000174 case 8:
Ted Kremenek276278e2008-03-27 22:05:32 +0000175 if (!strcmp(cstr, "compare:"))
176 return CheckNilArg(N, 0);
Mike Stump11289f42009-09-09 15:08:12 +0000177
Ted Kremenek276278e2008-03-27 22:05:32 +0000178 break;
Mike Stump11289f42009-09-09 15:08:12 +0000179
Ted Kremenekc7194242008-03-28 16:09:38 +0000180 case 15:
181 // FIXME: Checking for initWithFormat: will not work in most cases
182 // yet because [NSString alloc] returns id, not NSString*. We will
183 // need support for tracking expected-type information in the analyzer
184 // to find these errors.
185 if (!strcmp(cstr, "initWithFormat:"))
186 return CheckNilArg(N, 0);
Mike Stump11289f42009-09-09 15:08:12 +0000187
Ted Kremenekc7194242008-03-28 16:09:38 +0000188 break;
Mike Stump11289f42009-09-09 15:08:12 +0000189
Ted Kremenek276278e2008-03-27 22:05:32 +0000190 case 16:
191 if (!strcmp(cstr, "compare:options:"))
192 return CheckNilArg(N, 0);
Mike Stump11289f42009-09-09 15:08:12 +0000193
Ted Kremenek2e4e7cc2008-03-27 21:23:57 +0000194 break;
Mike Stump11289f42009-09-09 15:08:12 +0000195
Ted Kremenek276278e2008-03-27 22:05:32 +0000196 case 22:
197 if (!strcmp(cstr, "compare:options:range:"))
198 return CheckNilArg(N, 0);
Mike Stump11289f42009-09-09 15:08:12 +0000199
Ted Kremenek276278e2008-03-27 22:05:32 +0000200 break;
Mike Stump11289f42009-09-09 15:08:12 +0000201
Ted Kremenek276278e2008-03-27 22:05:32 +0000202 case 23:
Mike Stump11289f42009-09-09 15:08:12 +0000203
Ted Kremenek276278e2008-03-27 22:05:32 +0000204 if (!strcmp(cstr, "caseInsensitiveCompare:"))
205 return CheckNilArg(N, 0);
Mike Stump11289f42009-09-09 15:08:12 +0000206
Ted Kremenek276278e2008-03-27 22:05:32 +0000207 break;
Ted Kremenekc7194242008-03-28 16:09:38 +0000208
Ted Kremenek276278e2008-03-27 22:05:32 +0000209 case 29:
210 if (!strcmp(cstr, "compare:options:range:locale:"))
211 return CheckNilArg(N, 0);
Mike Stump11289f42009-09-09 15:08:12 +0000212
213 break;
214
Ted Kremenek276278e2008-03-27 22:05:32 +0000215 case 37:
216 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
217 return CheckNilArg(N, 0);
Mike Stump11289f42009-09-09 15:08:12 +0000218
219 break;
Ted Kremenekc0414922008-03-27 07:25:52 +0000220 }
Mike Stump11289f42009-09-09 15:08:12 +0000221
Ted Kremenekc0414922008-03-27 07:25:52 +0000222 return false;
223}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000224
225//===----------------------------------------------------------------------===//
226// Error reporting.
227//===----------------------------------------------------------------------===//
228
229namespace {
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000230
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000231class VISIBILITY_HIDDEN AuditCFNumberCreate : public GRSimpleAPICheck {
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000232 APIMisuse* BT;
Mike Stump11289f42009-09-09 15:08:12 +0000233
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000234 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
235 // it should always be passed with a call to Audit. The latter
236 // approach makes this class more stateless.
237 ASTContext& Ctx;
238 IdentifierInfo* II;
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000239 BugReporter& BR;
Ted Kremenek095f1a92009-06-18 23:58:37 +0000240
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000241public:
Mike Stump11289f42009-09-09 15:08:12 +0000242 AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
Ted Kremenek095f1a92009-06-18 23:58:37 +0000243 : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
Mike Stump11289f42009-09-09 15:08:12 +0000244
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000245 ~AuditCFNumberCreate() {}
Mike Stump11289f42009-09-09 15:08:12 +0000246
Zhongxing Xu20227f72009-08-06 01:32:16 +0000247 bool Audit(ExplodedNode* N, GRStateManager&);
Mike Stump11289f42009-09-09 15:08:12 +0000248
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000249private:
Zhongxing Xu20227f72009-08-06 01:32:16 +0000250 void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N,
Mike Stump11289f42009-09-09 15:08:12 +0000251 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000252};
253} // end anonymous namespace
254
255enum CFNumberType {
256 kCFNumberSInt8Type = 1,
257 kCFNumberSInt16Type = 2,
258 kCFNumberSInt32Type = 3,
259 kCFNumberSInt64Type = 4,
260 kCFNumberFloat32Type = 5,
261 kCFNumberFloat64Type = 6,
262 kCFNumberCharType = 7,
263 kCFNumberShortType = 8,
264 kCFNumberIntType = 9,
265 kCFNumberLongType = 10,
266 kCFNumberLongLongType = 11,
267 kCFNumberFloatType = 12,
268 kCFNumberDoubleType = 13,
269 kCFNumberCFIndexType = 14,
270 kCFNumberNSIntegerType = 15,
271 kCFNumberCGFloatType = 16
272};
273
274namespace {
275 template<typename T>
276 class Optional {
277 bool IsKnown;
278 T Val;
279 public:
280 Optional() : IsKnown(false), Val(0) {}
281 Optional(const T& val) : IsKnown(true), Val(val) {}
Mike Stump11289f42009-09-09 15:08:12 +0000282
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000283 bool isKnown() const { return IsKnown; }
284
285 const T& getValue() const {
286 assert (isKnown());
287 return Val;
288 }
289
290 operator const T&() const {
291 return getValue();
292 }
293 };
294}
295
296static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
297 static unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump11289f42009-09-09 15:08:12 +0000298
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000299 if (i < kCFNumberCharType)
300 return FixedSize[i-1];
Mike Stump11289f42009-09-09 15:08:12 +0000301
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000302 QualType T;
Mike Stump11289f42009-09-09 15:08:12 +0000303
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000304 switch (i) {
305 case kCFNumberCharType: T = Ctx.CharTy; break;
306 case kCFNumberShortType: T = Ctx.ShortTy; break;
307 case kCFNumberIntType: T = Ctx.IntTy; break;
308 case kCFNumberLongType: T = Ctx.LongTy; break;
309 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
310 case kCFNumberFloatType: T = Ctx.FloatTy; break;
311 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
312 case kCFNumberCFIndexType:
313 case kCFNumberNSIntegerType:
314 case kCFNumberCGFloatType:
Mike Stump11289f42009-09-09 15:08:12 +0000315 // FIXME: We need a way to map from names to Type*.
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000316 default:
317 return Optional<uint64_t>();
318 }
Mike Stump11289f42009-09-09 15:08:12 +0000319
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000320 return Ctx.getTypeSize(T);
321}
322
323#if 0
324static const char* GetCFNumberTypeStr(uint64_t i) {
325 static const char* Names[] = {
326 "kCFNumberSInt8Type",
327 "kCFNumberSInt16Type",
328 "kCFNumberSInt32Type",
329 "kCFNumberSInt64Type",
330 "kCFNumberFloat32Type",
331 "kCFNumberFloat64Type",
332 "kCFNumberCharType",
333 "kCFNumberShortType",
334 "kCFNumberIntType",
335 "kCFNumberLongType",
336 "kCFNumberLongLongType",
337 "kCFNumberFloatType",
338 "kCFNumberDoubleType",
339 "kCFNumberCFIndexType",
340 "kCFNumberNSIntegerType",
341 "kCFNumberCGFloatType"
342 };
Mike Stump11289f42009-09-09 15:08:12 +0000343
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000344 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
345}
346#endif
347
Mike Stump11289f42009-09-09 15:08:12 +0000348bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000349 const CallExpr* CE =
350 cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump11289f42009-09-09 15:08:12 +0000351 const Expr* Callee = CE->getCallee();
352 SVal CallV = N->getState()->getSVal(Callee);
Zhongxing Xuac129432009-04-20 05:24:46 +0000353 const FunctionDecl* FD = CallV.getAsFunctionDecl();
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000354
Zhongxing Xuac129432009-04-20 05:24:46 +0000355 if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000356 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000357
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000358 // Get the value of the "theType" argument.
Ted Kremenek095f1a92009-06-18 23:58:37 +0000359 SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
Mike Stump11289f42009-09-09 15:08:12 +0000360
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000361 // FIXME: We really should allow ranges of valid theType values, and
362 // bifurcate the state appropriately.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000363 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Mike Stump11289f42009-09-09 15:08:12 +0000364
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000365 if (!V)
366 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000367
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000368 uint64_t NumberKind = V->getValue().getLimitedValue();
369 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000370
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000371 // FIXME: In some cases we can emit an error.
372 if (!TargetSize.isKnown())
373 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000374
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000375 // Look at the value of the integer being passed by reference. Essentially
376 // we want to catch cases where the value passed in is not equal to the
377 // size of the type being created.
Ted Kremenek095f1a92009-06-18 23:58:37 +0000378 SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
Mike Stump11289f42009-09-09 15:08:12 +0000379
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000380 // FIXME: Eventually we should handle arbitrary locations. We can do this
381 // by having an enhanced memory model that does low-level typing.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000382 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000383
384 if (!LV)
385 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000386
Ted Kremenek87a7a452009-07-29 18:17:40 +0000387 const TypedRegion* R = dyn_cast<TypedRegion>(LV->getBaseRegion());
388
389 if (!R)
390 return false;
391
Zhongxing Xu34d04b32009-05-09 03:57:34 +0000392 QualType T = Ctx.getCanonicalType(R->getValueType(Ctx));
Mike Stump11289f42009-09-09 15:08:12 +0000393
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000394 // FIXME: If the pointee isn't an integer type, should we flag a warning?
395 // People can do weird stuff with pointers.
Mike Stump11289f42009-09-09 15:08:12 +0000396
397 if (!T->isIntegerType())
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000398 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000399
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000400 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump11289f42009-09-09 15:08:12 +0000401
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000402 // CHECK: is SourceSize == TargetSize
Mike Stump11289f42009-09-09 15:08:12 +0000403
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000404 if (SourceSize == TargetSize)
405 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000406
Ted Kremenek5ca90a22008-10-04 05:50:14 +0000407 AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000408
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000409 // FIXME: We can actually create an abstract "CFNumber" object that has
410 // the bits initialized to the provided values.
411 return SourceSize < TargetSize;
412}
413
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000414void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex,
Zhongxing Xu20227f72009-08-06 01:32:16 +0000415 ExplodedNode *N,
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000416 uint64_t SourceSize, uint64_t TargetSize,
417 uint64_t NumberKind) {
Mike Stump11289f42009-09-09 15:08:12 +0000418
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000419 std::string sbuf;
420 llvm::raw_string_ostream os(sbuf);
Mike Stump11289f42009-09-09 15:08:12 +0000421
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000422 os << (SourceSize == 8 ? "An " : "A ")
423 << SourceSize << " bit integer is used to initialize a CFNumber "
424 "object that represents "
425 << (TargetSize == 8 ? "an " : "a ")
Mike Stump11289f42009-09-09 15:08:12 +0000426 << TargetSize << " bit integer. ";
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000427
428 if (SourceSize < TargetSize)
429 os << (TargetSize - SourceSize)
Mike Stump11289f42009-09-09 15:08:12 +0000430 << " bits of the CFNumber value will be garbage." ;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000431 else
432 os << (SourceSize - TargetSize)
433 << " bits of the input integer will be lost.";
Mike Stump11289f42009-09-09 15:08:12 +0000434
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000435 // Lazily create the BugType object. This will be owned
436 // by the BugReporter object 'BR' once we call BR.EmitWarning.
437 if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate");
438 RangedBugReport *report = new RangedBugReport(*BT, os.str().c_str(), N);
439 report->addRange(Ex->getSourceRange());
440 BR.EmitReport(report);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000441}
442
443GRSimpleAPICheck*
Mike Stump11289f42009-09-09 15:08:12 +0000444clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenek095f1a92009-06-18 23:58:37 +0000445 return new AuditCFNumberCreate(Ctx, BR);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000446}
447
Ted Kremenek1f352db2008-07-22 16:21:24 +0000448//===----------------------------------------------------------------------===//
Ted Kremenekc057f412009-07-14 00:43:42 +0000449// CFRetain/CFRelease auditing for null arguments.
450//===----------------------------------------------------------------------===//
451
452namespace {
453class VISIBILITY_HIDDEN AuditCFRetainRelease : public GRSimpleAPICheck {
454 APIMisuse *BT;
Mike Stump11289f42009-09-09 15:08:12 +0000455
Ted Kremenekc057f412009-07-14 00:43:42 +0000456 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
457 // it should always be passed with a call to Audit. The latter
458 // approach makes this class more stateless.
459 ASTContext& Ctx;
460 IdentifierInfo *Retain, *Release;
461 BugReporter& BR;
Mike Stump11289f42009-09-09 15:08:12 +0000462
Ted Kremenekc057f412009-07-14 00:43:42 +0000463public:
Mike Stump11289f42009-09-09 15:08:12 +0000464 AuditCFRetainRelease(ASTContext& ctx, BugReporter& br)
Ted Kremenekc057f412009-07-14 00:43:42 +0000465 : BT(0), Ctx(ctx),
466 Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")),
467 BR(br){}
Mike Stump11289f42009-09-09 15:08:12 +0000468
Ted Kremenekc057f412009-07-14 00:43:42 +0000469 ~AuditCFRetainRelease() {}
Mike Stump11289f42009-09-09 15:08:12 +0000470
Zhongxing Xu20227f72009-08-06 01:32:16 +0000471 bool Audit(ExplodedNode* N, GRStateManager&);
Ted Kremenekc057f412009-07-14 00:43:42 +0000472};
473} // end anonymous namespace
474
475
Zhongxing Xu20227f72009-08-06 01:32:16 +0000476bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) {
Ted Kremenekbfd28fd2009-07-22 22:35:28 +0000477 const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Mike Stump11289f42009-09-09 15:08:12 +0000478
Ted Kremenekc057f412009-07-14 00:43:42 +0000479 // If the CallExpr doesn't have exactly 1 argument just give up checking.
480 if (CE->getNumArgs() != 1)
481 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000482
Ted Kremenekc057f412009-07-14 00:43:42 +0000483 // Check if we called CFRetain/CFRelease.
484 const GRState* state = N->getState();
485 SVal X = state->getSVal(CE->getCallee());
486 const FunctionDecl* FD = X.getAsFunctionDecl();
Mike Stump11289f42009-09-09 15:08:12 +0000487
Ted Kremenekc057f412009-07-14 00:43:42 +0000488 if (!FD)
489 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000490
491 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenekc057f412009-07-14 00:43:42 +0000492 if (!(FuncII == Retain || FuncII == Release))
493 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000494
Ted Kremenekc057f412009-07-14 00:43:42 +0000495 // Finally, check if the argument is NULL.
496 // FIXME: We should be able to bifurcate the state here, as a successful
497 // check will result in the value not being NULL afterwards.
498 // FIXME: Need a way to register vistors for the BugReporter. Would like
499 // to benefit from the same diagnostics that regular null dereference
500 // reporting has.
501 if (state->getStateManager().isEqual(state, CE->getArg(0), 0)) {
502 if (!BT)
503 BT = new APIMisuse("null passed to CFRetain/CFRelease");
Mike Stump11289f42009-09-09 15:08:12 +0000504
Ted Kremenekc057f412009-07-14 00:43:42 +0000505 const char *description = (FuncII == Retain)
506 ? "Null pointer argument in call to CFRetain"
507 : "Null pointer argument in call to CFRelease";
508
509 RangedBugReport *report = new RangedBugReport(*BT, description, N);
510 report->addRange(CE->getArg(0)->getSourceRange());
511 BR.EmitReport(report);
512 return true;
513 }
514
515 return false;
516}
Mike Stump11289f42009-09-09 15:08:12 +0000517
518
Ted Kremenekc057f412009-07-14 00:43:42 +0000519GRSimpleAPICheck*
Mike Stump11289f42009-09-09 15:08:12 +0000520clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) {
Ted Kremenekc057f412009-07-14 00:43:42 +0000521 return new AuditCFRetainRelease(Ctx, BR);
522}
523
524//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000525// Check registration.
Ted Kremenekc057f412009-07-14 00:43:42 +0000526//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000527
Zhongxing Xu6be1a4e2009-08-21 02:18:44 +0000528void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) {
Ted Kremenek1f352db2008-07-22 16:21:24 +0000529 ASTContext& Ctx = Eng.getContext();
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000530 BugReporter &BR = Eng.getBugReporter();
Ted Kremenek1f352db2008-07-22 16:21:24 +0000531
Ted Kremenek095f1a92009-06-18 23:58:37 +0000532 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
Ted Kremenek1f352db2008-07-22 16:21:24 +0000533 Stmt::ObjCMessageExprClass);
Mike Stump11289f42009-09-09 15:08:12 +0000534 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
Ted Kremenekc057f412009-07-14 00:43:42 +0000535 Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass);
Mike Stump11289f42009-09-09 15:08:12 +0000536
Zhongxing Xu6be1a4e2009-08-21 02:18:44 +0000537 RegisterNSErrorChecks(BR, Eng, D);
Ted Kremenek1f352db2008-07-22 16:21:24 +0000538}