blob: 6dd0a8c01fe6f61bea2f9e2bdb385f8491c1c8e4 [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
Argyrios Kyrtzidis0b1ba622011-02-16 01:40:52 +000016#include "ClangSACheckers.h"
Ted Kremenek928c4152011-03-17 04:01:35 +000017#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +000018#include "clang/StaticAnalyzer/Core/Checker.h"
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +000019#include "clang/StaticAnalyzer/Core/CheckerManager.h"
Argyrios Kyrtzidis983326f2011-02-23 01:05:36 +000020#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
Ted Kremenek9b663712011-02-10 01:03:03 +000021#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
Ted Kremenek9b663712011-02-10 01:03:03 +000022#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
Jordy Rosed1e5a892011-09-02 08:02:59 +000023#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
Ted Kremenek18c66fd2011-08-15 22:09:50 +000024#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
Ted Kremenek9b663712011-02-10 01:03:03 +000025#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
26#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.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"
Benjamin Kramer8fe83e12012-02-04 13:45:25 +000031#include "llvm/ADT/SmallString.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000032
Ted Kremenek99c6ad32008-03-27 07:25:52 +000033using namespace clang;
Ted Kremenek9ef65372010-12-23 07:20:52 +000034using namespace ento;
Ted Kremenek52755612008-03-27 17:17:22 +000035
Ted Kremenek2ce2baa2010-10-20 23:38:56 +000036namespace {
37class APIMisuse : public BugType {
38public:
39 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
40};
41} // end anonymous namespace
42
43//===----------------------------------------------------------------------===//
44// Utility functions.
45//===----------------------------------------------------------------------===//
46
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +000047static const char* GetReceiverNameType(const ObjCMessage &msg) {
Anders Carlssonb62bdce2011-03-08 20:05:26 +000048 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
49 return ID->getIdentifier()->getNameStart();
50 return 0;
Ted Kremenek4ba62832008-03-27 22:05:32 +000051}
Ted Kremenek52755612008-03-27 17:17:22 +000052
Anders Carlssonb62bdce2011-03-08 20:05:26 +000053static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID,
Chris Lattner5f9e2722011-07-23 10:55:15 +000054 StringRef ClassName) {
Anders Carlssonb62bdce2011-03-08 20:05:26 +000055 if (ID->getIdentifier()->getName() == ClassName)
56 return true;
57
58 if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
59 return isReceiverClassOrSuperclass(Super, ClassName);
60
61 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +000062}
63
Zhongxing Xu1c96b242008-10-17 05:57:07 +000064static inline bool isNil(SVal X) {
Mike Stump1eb44332009-09-09 15:08:12 +000065 return isa<loc::ConcreteInt>(X);
Ted Kremeneke5d5c202008-03-27 21:15:17 +000066}
67
Ted Kremenek99c6ad32008-03-27 07:25:52 +000068//===----------------------------------------------------------------------===//
Ted Kremenek2ce2baa2010-10-20 23:38:56 +000069// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
Ted Kremenek99c6ad32008-03-27 07:25:52 +000070//===----------------------------------------------------------------------===//
71
Benjamin Kramercb9c0742010-10-22 16:33:16 +000072namespace {
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +000073 class NilArgChecker : public Checker<check::PreObjCMessage> {
Dylan Noblesmith6f42b622012-02-05 02:12:40 +000074 mutable OwningPtr<APIMisuse> BT;
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +000075
76 void WarnNilArg(CheckerContext &C,
77 const ObjCMessage &msg, unsigned Arg) const;
78
Benjamin Kramercb9c0742010-10-22 16:33:16 +000079 public:
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +000080 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
Benjamin Kramercb9c0742010-10-22 16:33:16 +000081 };
82}
Mike Stump1eb44332009-09-09 15:08:12 +000083
Ted Kremenek2ce2baa2010-10-20 23:38:56 +000084void NilArgChecker::WarnNilArg(CheckerContext &C,
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +000085 const ObjCMessage &msg,
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +000086 unsigned int Arg) const
Ted Kremenek2ce2baa2010-10-20 23:38:56 +000087{
88 if (!BT)
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +000089 BT.reset(new APIMisuse("nil argument"));
Ted Kremenek2ce2baa2010-10-20 23:38:56 +000090
Ted Kremenekd048c6e2010-12-20 21:19:09 +000091 if (ExplodedNode *N = C.generateSink()) {
Dylan Noblesmithf7ccbad2012-02-05 02:13:05 +000092 SmallString<128> sbuf;
Ted Kremenek2ce2baa2010-10-20 23:38:56 +000093 llvm::raw_svector_ostream os(sbuf);
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +000094 os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
95 << msg.getSelector().getAsString() << "' cannot be nil";
Mike Stump1eb44332009-09-09 15:08:12 +000096
Anna Zakse172e8b2011-08-17 23:00:25 +000097 BugReport *R = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +000098 R->addRange(msg.getArgSourceRange(Arg));
Ted Kremenek2ce2baa2010-10-20 23:38:56 +000099 C.EmitReport(R);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000100 }
Ted Kremenek4ba62832008-03-27 22:05:32 +0000101}
102
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000103void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
104 CheckerContext &C) const {
Anders Carlssonb62bdce2011-03-08 20:05:26 +0000105 const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
106 if (!ID)
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000107 return;
108
Anders Carlssonb62bdce2011-03-08 20:05:26 +0000109 if (isReceiverClassOrSuperclass(ID, "NSString")) {
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000110 Selector S = msg.getSelector();
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000111
112 if (S.isUnarySelector())
113 return;
114
115 // FIXME: This is going to be really slow doing these checks with
116 // lexical comparisons.
117
118 std::string NameStr = S.getAsString();
Chris Lattner5f9e2722011-07-23 10:55:15 +0000119 StringRef Name(NameStr);
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000120 assert(!Name.empty());
121
122 // FIXME: Checking for initWithFormat: will not work in most cases
123 // yet because [NSString alloc] returns id, not NSString*. We will
124 // need support for tracking expected-type information in the analyzer
125 // to find these errors.
126 if (Name == "caseInsensitiveCompare:" ||
127 Name == "compare:" ||
128 Name == "compare:options:" ||
129 Name == "compare:options:range:" ||
130 Name == "compare:options:range:locale:" ||
131 Name == "componentsSeparatedByCharactersInSet:" ||
132 Name == "initWithFormat:") {
Ted Kremenek5eca4822012-01-06 22:09:28 +0000133 if (isNil(msg.getArgSVal(0, C.getLocationContext(), C.getState())))
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000134 WarnNilArg(C, msg, 0);
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000135 }
136 }
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000137}
Ted Kremenek04bc8762008-06-26 23:59:48 +0000138
139//===----------------------------------------------------------------------===//
140// Error reporting.
141//===----------------------------------------------------------------------===//
142
143namespace {
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +0000144class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
Dylan Noblesmith6f42b622012-02-05 02:12:40 +0000145 mutable OwningPtr<APIMisuse> BT;
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000146 mutable IdentifierInfo* II;
Ted Kremenek04bc8762008-06-26 23:59:48 +0000147public:
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000148 CFNumberCreateChecker() : II(0) {}
149
150 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
151
Ted Kremenek04bc8762008-06-26 23:59:48 +0000152private:
Ted Kremenek9c378f72011-08-12 23:37:29 +0000153 void EmitError(const TypedRegion* R, const Expr *Ex,
Mike Stump1eb44332009-09-09 15:08:12 +0000154 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000155};
156} // end anonymous namespace
157
158enum CFNumberType {
159 kCFNumberSInt8Type = 1,
160 kCFNumberSInt16Type = 2,
161 kCFNumberSInt32Type = 3,
162 kCFNumberSInt64Type = 4,
163 kCFNumberFloat32Type = 5,
164 kCFNumberFloat64Type = 6,
165 kCFNumberCharType = 7,
166 kCFNumberShortType = 8,
167 kCFNumberIntType = 9,
168 kCFNumberLongType = 10,
169 kCFNumberLongLongType = 11,
170 kCFNumberFloatType = 12,
171 kCFNumberDoubleType = 13,
172 kCFNumberCFIndexType = 14,
173 kCFNumberNSIntegerType = 15,
174 kCFNumberCGFloatType = 16
175};
176
177namespace {
178 template<typename T>
179 class Optional {
180 bool IsKnown;
181 T Val;
182 public:
183 Optional() : IsKnown(false), Val(0) {}
184 Optional(const T& val) : IsKnown(true), Val(val) {}
Mike Stump1eb44332009-09-09 15:08:12 +0000185
Ted Kremenek04bc8762008-06-26 23:59:48 +0000186 bool isKnown() const { return IsKnown; }
187
188 const T& getValue() const {
189 assert (isKnown());
190 return Val;
191 }
192
193 operator const T&() const {
194 return getValue();
195 }
196 };
197}
198
Ted Kremenek9c378f72011-08-12 23:37:29 +0000199static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
Nuno Lopes2550d702009-12-23 17:49:57 +0000200 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump1eb44332009-09-09 15:08:12 +0000201
Ted Kremenek04bc8762008-06-26 23:59:48 +0000202 if (i < kCFNumberCharType)
203 return FixedSize[i-1];
Mike Stump1eb44332009-09-09 15:08:12 +0000204
Ted Kremenek04bc8762008-06-26 23:59:48 +0000205 QualType T;
Mike Stump1eb44332009-09-09 15:08:12 +0000206
Ted Kremenek04bc8762008-06-26 23:59:48 +0000207 switch (i) {
208 case kCFNumberCharType: T = Ctx.CharTy; break;
209 case kCFNumberShortType: T = Ctx.ShortTy; break;
210 case kCFNumberIntType: T = Ctx.IntTy; break;
211 case kCFNumberLongType: T = Ctx.LongTy; break;
212 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
213 case kCFNumberFloatType: T = Ctx.FloatTy; break;
214 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
215 case kCFNumberCFIndexType:
216 case kCFNumberNSIntegerType:
217 case kCFNumberCGFloatType:
Mike Stump1eb44332009-09-09 15:08:12 +0000218 // FIXME: We need a way to map from names to Type*.
Ted Kremenek04bc8762008-06-26 23:59:48 +0000219 default:
220 return Optional<uint64_t>();
221 }
Mike Stump1eb44332009-09-09 15:08:12 +0000222
Ted Kremenek04bc8762008-06-26 23:59:48 +0000223 return Ctx.getTypeSize(T);
224}
225
226#if 0
227static const char* GetCFNumberTypeStr(uint64_t i) {
228 static const char* Names[] = {
229 "kCFNumberSInt8Type",
230 "kCFNumberSInt16Type",
231 "kCFNumberSInt32Type",
232 "kCFNumberSInt64Type",
233 "kCFNumberFloat32Type",
234 "kCFNumberFloat64Type",
235 "kCFNumberCharType",
236 "kCFNumberShortType",
237 "kCFNumberIntType",
238 "kCFNumberLongType",
239 "kCFNumberLongLongType",
240 "kCFNumberFloatType",
241 "kCFNumberDoubleType",
242 "kCFNumberCFIndexType",
243 "kCFNumberNSIntegerType",
244 "kCFNumberCGFloatType"
245 };
Mike Stump1eb44332009-09-09 15:08:12 +0000246
Ted Kremenek04bc8762008-06-26 23:59:48 +0000247 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
248}
249#endif
250
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000251void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
252 CheckerContext &C) const {
Ted Kremenek8bef8232012-01-26 21:29:00 +0000253 ProgramStateRef state = C.getState();
Anna Zaksb805c8f2011-12-01 05:57:37 +0000254 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000255 if (!FD)
256 return;
257
258 ASTContext &Ctx = C.getASTContext();
259 if (!II)
260 II = &Ctx.Idents.get("CFNumberCreate");
261
262 if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
263 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000264
Ted Kremenek04bc8762008-06-26 23:59:48 +0000265 // Get the value of the "theType" argument.
Ted Kremenek5eca4822012-01-06 22:09:28 +0000266 const LocationContext *LCtx = C.getLocationContext();
267 SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx);
Mike Stump1eb44332009-09-09 15:08:12 +0000268
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000269 // FIXME: We really should allow ranges of valid theType values, and
270 // bifurcate the state appropriately.
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000271 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000272 if (!V)
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000273 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000274
Ted Kremenek04bc8762008-06-26 23:59:48 +0000275 uint64_t NumberKind = V->getValue().getLimitedValue();
276 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000277
Ted Kremenek04bc8762008-06-26 23:59:48 +0000278 // FIXME: In some cases we can emit an error.
279 if (!TargetSize.isKnown())
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000280 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000281
Ted Kremenek04bc8762008-06-26 23:59:48 +0000282 // Look at the value of the integer being passed by reference. Essentially
283 // we want to catch cases where the value passed in is not equal to the
284 // size of the type being created.
Ted Kremenek5eca4822012-01-06 22:09:28 +0000285 SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx);
Mike Stump1eb44332009-09-09 15:08:12 +0000286
Ted Kremenek04bc8762008-06-26 23:59:48 +0000287 // FIXME: Eventually we should handle arbitrary locations. We can do this
288 // by having an enhanced memory model that does low-level typing.
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000289 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenek04bc8762008-06-26 23:59:48 +0000290 if (!LV)
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000291 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000292
Ted Kremenek96979342011-08-12 20:02:48 +0000293 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
Ted Kremenek5e77eba2009-07-29 18:17:40 +0000294 if (!R)
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000295 return;
Ted Kremenek5e77eba2009-07-29 18:17:40 +0000296
Zhongxing Xu018220c2010-08-11 06:10:55 +0000297 QualType T = Ctx.getCanonicalType(R->getValueType());
Mike Stump1eb44332009-09-09 15:08:12 +0000298
Ted Kremenek04bc8762008-06-26 23:59:48 +0000299 // FIXME: If the pointee isn't an integer type, should we flag a warning?
300 // People can do weird stuff with pointers.
Mike Stump1eb44332009-09-09 15:08:12 +0000301
302 if (!T->isIntegerType())
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000303 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000304
Ted Kremenek04bc8762008-06-26 23:59:48 +0000305 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump1eb44332009-09-09 15:08:12 +0000306
Ted Kremenek04bc8762008-06-26 23:59:48 +0000307 // CHECK: is SourceSize == TargetSize
Ted Kremenek04bc8762008-06-26 23:59:48 +0000308 if (SourceSize == TargetSize)
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000309 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000310
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000311 // Generate an error. Only generate a sink if 'SourceSize < TargetSize';
312 // otherwise generate a regular node.
313 //
Ted Kremenek04bc8762008-06-26 23:59:48 +0000314 // FIXME: We can actually create an abstract "CFNumber" object that has
315 // the bits initialized to the provided values.
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000316 //
Ted Kremenekd048c6e2010-12-20 21:19:09 +0000317 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
Anna Zaks0bd6b112011-10-26 21:06:34 +0000318 : C.addTransition()) {
Dylan Noblesmithf7ccbad2012-02-05 02:13:05 +0000319 SmallString<128> sbuf;
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000320 llvm::raw_svector_ostream os(sbuf);
321
322 os << (SourceSize == 8 ? "An " : "A ")
323 << SourceSize << " bit integer is used to initialize a CFNumber "
324 "object that represents "
325 << (TargetSize == 8 ? "an " : "a ")
326 << TargetSize << " bit integer. ";
327
328 if (SourceSize < TargetSize)
329 os << (TargetSize - SourceSize)
330 << " bits of the CFNumber value will be garbage." ;
331 else
332 os << (SourceSize - TargetSize)
333 << " bits of the input integer will be lost.";
Ted Kremenek04bc8762008-06-26 23:59:48 +0000334
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000335 if (!BT)
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000336 BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000337
Anna Zakse172e8b2011-08-17 23:00:25 +0000338 BugReport *report = new BugReport(*BT, os.str(), N);
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000339 report->addRange(CE->getArg(2)->getSourceRange());
340 C.EmitReport(report);
341 }
Ted Kremenek04bc8762008-06-26 23:59:48 +0000342}
343
Ted Kremenek78d46242008-07-22 16:21:24 +0000344//===----------------------------------------------------------------------===//
Jordy Rose61fb55c2010-07-06 02:34:42 +0000345// CFRetain/CFRelease checking for null arguments.
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000346//===----------------------------------------------------------------------===//
347
348namespace {
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +0000349class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
Dylan Noblesmith6f42b622012-02-05 02:12:40 +0000350 mutable OwningPtr<APIMisuse> BT;
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000351 mutable IdentifierInfo *Retain, *Release;
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000352public:
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000353 CFRetainReleaseChecker(): Retain(0), Release(0) {}
Ted Kremenek9c378f72011-08-12 23:37:29 +0000354 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000355};
356} // end anonymous namespace
357
358
Ted Kremenek9c378f72011-08-12 23:37:29 +0000359void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
360 CheckerContext &C) const {
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000361 // If the CallExpr doesn't have exactly 1 argument just give up checking.
362 if (CE->getNumArgs() != 1)
Jordy Rose61fb55c2010-07-06 02:34:42 +0000363 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000364
Ted Kremenek8bef8232012-01-26 21:29:00 +0000365 ProgramStateRef state = C.getState();
Anna Zaksb805c8f2011-12-01 05:57:37 +0000366 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000367 if (!FD)
Jordy Rose61fb55c2010-07-06 02:34:42 +0000368 return;
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000369
370 if (!BT) {
371 ASTContext &Ctx = C.getASTContext();
372 Retain = &Ctx.Idents.get("CFRetain");
373 Release = &Ctx.Idents.get("CFRelease");
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000374 BT.reset(new APIMisuse("null passed to CFRetain/CFRelease"));
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000375 }
Mike Stump1eb44332009-09-09 15:08:12 +0000376
Jordy Rose61fb55c2010-07-06 02:34:42 +0000377 // Check if we called CFRetain/CFRelease.
Mike Stump1eb44332009-09-09 15:08:12 +0000378 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000379 if (!(FuncII == Retain || FuncII == Release))
Jordy Rose61fb55c2010-07-06 02:34:42 +0000380 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000381
Jordy Rose61fb55c2010-07-06 02:34:42 +0000382 // FIXME: The rest of this just checks that the argument is non-null.
383 // It should probably be refactored and combined with AttrNonNullChecker.
384
385 // Get the argument's value.
386 const Expr *Arg = CE->getArg(0);
Ted Kremenek5eca4822012-01-06 22:09:28 +0000387 SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
Jordy Rose61fb55c2010-07-06 02:34:42 +0000388 DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
389 if (!DefArgVal)
390 return;
391
392 // Get a NULL value.
Ted Kremenekc8413fd2010-12-02 07:49:45 +0000393 SValBuilder &svalBuilder = C.getSValBuilder();
394 DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
Jordy Rose61fb55c2010-07-06 02:34:42 +0000395
396 // Make an expression asserting that they're equal.
Ted Kremenekc8413fd2010-12-02 07:49:45 +0000397 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
Jordy Rose61fb55c2010-07-06 02:34:42 +0000398
399 // Are they equal?
Ted Kremenek8bef8232012-01-26 21:29:00 +0000400 ProgramStateRef stateTrue, stateFalse;
Ted Kremenek28f47b92010-12-01 22:16:56 +0000401 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
Jordy Rose61fb55c2010-07-06 02:34:42 +0000402
403 if (stateTrue && !stateFalse) {
Ted Kremenekd048c6e2010-12-20 21:19:09 +0000404 ExplodedNode *N = C.generateSink(stateTrue);
Jordy Rose61fb55c2010-07-06 02:34:42 +0000405 if (!N)
406 return;
407
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000408 const char *description = (FuncII == Retain)
409 ? "Null pointer argument in call to CFRetain"
410 : "Null pointer argument in call to CFRelease";
411
Anna Zakse172e8b2011-08-17 23:00:25 +0000412 BugReport *report = new BugReport(*BT, description, N);
Jordy Rose61fb55c2010-07-06 02:34:42 +0000413 report->addRange(Arg->getSourceRange());
Ted Kremenek76aadc32012-03-09 01:13:14 +0000414 report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg,
415 report));
Jordy Rose61fb55c2010-07-06 02:34:42 +0000416 C.EmitReport(report);
417 return;
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000418 }
419
Jordy Rose61fb55c2010-07-06 02:34:42 +0000420 // From here on, we know the argument is non-null.
Anna Zaks0bd6b112011-10-26 21:06:34 +0000421 C.addTransition(stateFalse);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000422}
423
424//===----------------------------------------------------------------------===//
Ted Kremenek50e837b2009-11-20 05:27:05 +0000425// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
426//===----------------------------------------------------------------------===//
427
428namespace {
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +0000429class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000430 mutable Selector releaseS;
431 mutable Selector retainS;
432 mutable Selector autoreleaseS;
433 mutable Selector drainS;
Dylan Noblesmith6f42b622012-02-05 02:12:40 +0000434 mutable OwningPtr<BugType> BT;
Ted Kremenek50e837b2009-11-20 05:27:05 +0000435
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000436public:
437 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
Ted Kremenek50e837b2009-11-20 05:27:05 +0000438};
439}
440
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000441void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
442 CheckerContext &C) const {
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000443
444 if (!BT) {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000445 BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
446 "instance"));
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000447
448 ASTContext &Ctx = C.getASTContext();
449 releaseS = GetNullarySelector("release", Ctx);
450 retainS = GetNullarySelector("retain", Ctx);
451 autoreleaseS = GetNullarySelector("autorelease", Ctx);
452 drainS = GetNullarySelector("drain", Ctx);
453 }
454
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000455 if (msg.isInstanceMessage())
Ted Kremenek50e837b2009-11-20 05:27:05 +0000456 return;
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000457 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
458 assert(Class);
Douglas Gregor04badcf2010-04-21 00:45:42 +0000459
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000460 Selector S = msg.getSelector();
Benjamin Kramer921ddc42009-11-20 10:03:00 +0000461 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremenek50e837b2009-11-20 05:27:05 +0000462 return;
463
Anna Zaks0bd6b112011-10-26 21:06:34 +0000464 if (ExplodedNode *N = C.addTransition()) {
Dylan Noblesmithf7ccbad2012-02-05 02:13:05 +0000465 SmallString<200> buf;
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000466 llvm::raw_svector_ostream os(buf);
Ted Kremenek19d67b52009-11-23 22:22:01 +0000467
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000468 os << "The '" << S.getAsString() << "' message should be sent to instances "
469 "of class '" << Class->getName()
470 << "' and not the class directly";
Ted Kremenek50e837b2009-11-20 05:27:05 +0000471
Anna Zakse172e8b2011-08-17 23:00:25 +0000472 BugReport *report = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000473 report->addRange(msg.getSourceRange());
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000474 C.EmitReport(report);
475 }
Ted Kremenek50e837b2009-11-20 05:27:05 +0000476}
477
478//===----------------------------------------------------------------------===//
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000479// Check for passing non-Objective-C types to variadic methods that expect
480// only Objective-C types.
481//===----------------------------------------------------------------------===//
482
483namespace {
484class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
485 mutable Selector arrayWithObjectsS;
486 mutable Selector dictionaryWithObjectsAndKeysS;
487 mutable Selector setWithObjectsS;
Jordy Rosef439e002012-04-06 19:06:01 +0000488 mutable Selector orderedSetWithObjectsS;
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000489 mutable Selector initWithObjectsS;
490 mutable Selector initWithObjectsAndKeysS;
Dylan Noblesmith6f42b622012-02-05 02:12:40 +0000491 mutable OwningPtr<BugType> BT;
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000492
493 bool isVariadicMessage(const ObjCMessage &msg) const;
494
495public:
496 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
497};
498}
499
500/// isVariadicMessage - Returns whether the given message is a variadic message,
501/// where all arguments must be Objective-C types.
502bool
503VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const {
504 const ObjCMethodDecl *MD = msg.getMethodDecl();
Ted Kremenek9281efe2011-04-12 21:47:05 +0000505
506 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000507 return false;
508
509 Selector S = msg.getSelector();
510
511 if (msg.isInstanceMessage()) {
512 // FIXME: Ideally we'd look at the receiver interface here, but that's not
513 // useful for init, because alloc returns 'id'. In theory, this could lead
514 // to false positives, for example if there existed a class that had an
515 // initWithObjects: implementation that does accept non-Objective-C pointer
516 // types, but the chance of that happening is pretty small compared to the
517 // gains that this analysis gives.
518 const ObjCInterfaceDecl *Class = MD->getClassInterface();
519
520 // -[NSArray initWithObjects:]
521 if (isReceiverClassOrSuperclass(Class, "NSArray") &&
522 S == initWithObjectsS)
523 return true;
524
525 // -[NSDictionary initWithObjectsAndKeys:]
526 if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
527 S == initWithObjectsAndKeysS)
528 return true;
529
530 // -[NSSet initWithObjects:]
531 if (isReceiverClassOrSuperclass(Class, "NSSet") &&
532 S == initWithObjectsS)
533 return true;
Jordy Rosef439e002012-04-06 19:06:01 +0000534
535 // -[NSOrderedSet initWithObjects:]
536 if (isReceiverClassOrSuperclass(Class, "NSOrderedSet") &&
537 S == initWithObjectsS)
538 return true;
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000539 } else {
540 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
541
542 // -[NSArray arrayWithObjects:]
543 if (isReceiverClassOrSuperclass(Class, "NSArray") &&
544 S == arrayWithObjectsS)
545 return true;
546
547 // -[NSDictionary dictionaryWithObjectsAndKeys:]
548 if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
549 S == dictionaryWithObjectsAndKeysS)
550 return true;
551
552 // -[NSSet setWithObjects:]
553 if (isReceiverClassOrSuperclass(Class, "NSSet") &&
554 S == setWithObjectsS)
555 return true;
Jordy Rosef439e002012-04-06 19:06:01 +0000556
557 // -[NSOrderedSet orderedSetWithObjects:]
558 if (isReceiverClassOrSuperclass(Class, "NSOrderedSet") &&
559 S == orderedSetWithObjectsS)
560 return true;
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000561 }
562
563 return false;
564}
565
566void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
567 CheckerContext &C) const {
568 if (!BT) {
569 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
570 "Objective-C pointer types"));
571
572 ASTContext &Ctx = C.getASTContext();
573 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
574 dictionaryWithObjectsAndKeysS =
575 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
576 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
Jordy Rosef439e002012-04-06 19:06:01 +0000577 orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000578
579 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
580 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
581 }
582
583 if (!isVariadicMessage(msg))
584 return;
585
586 // We are not interested in the selector arguments since they have
587 // well-defined types, so the compiler will issue a warning for them.
588 unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
589
590 // We're not interested in the last argument since it has to be nil or the
591 // compiler would have issued a warning for it elsewhere.
592 unsigned variadicArgsEnd = msg.getNumArgs() - 1;
593
594 if (variadicArgsEnd <= variadicArgsBegin)
595 return;
596
597 // Verify that all arguments have Objective-C types.
Ted Kremenek6fb5c1f2011-03-14 19:50:37 +0000598 llvm::Optional<ExplodedNode*> errorNode;
Ted Kremenek8bef8232012-01-26 21:29:00 +0000599 ProgramStateRef state = C.getState();
Ted Kremenek6fb5c1f2011-03-14 19:50:37 +0000600
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000601 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
602 QualType ArgTy = msg.getArgType(I);
603 if (ArgTy->isObjCObjectPointerType())
604 continue;
605
Anders Carlssonf05982b2011-04-19 01:16:46 +0000606 // Block pointers are treaded as Objective-C pointers.
607 if (ArgTy->isBlockPointerType())
608 continue;
609
Ted Kremenekd5fde212011-03-16 00:22:51 +0000610 // Ignore pointer constants.
Ted Kremenek5eca4822012-01-06 22:09:28 +0000611 if (isa<loc::ConcreteInt>(msg.getArgSVal(I, C.getLocationContext(),
612 state)))
Ted Kremenekd5fde212011-03-16 00:22:51 +0000613 continue;
Ted Kremenek928c4152011-03-17 04:01:35 +0000614
Ted Kremenekf3f92932011-03-17 04:10:25 +0000615 // Ignore pointer types annotated with 'NSObject' attribute.
616 if (C.getASTContext().isObjCNSObjectType(ArgTy))
617 continue;
618
Ted Kremenek928c4152011-03-17 04:01:35 +0000619 // Ignore CF references, which can be toll-free bridged.
Ted Kremenek05560482011-07-16 19:50:32 +0000620 if (coreFoundation::isCFObjectRef(ArgTy))
Ted Kremenek928c4152011-03-17 04:01:35 +0000621 continue;
Ted Kremenekd5fde212011-03-16 00:22:51 +0000622
Ted Kremenek6fb5c1f2011-03-14 19:50:37 +0000623 // Generate only one error node to use for all bug reports.
624 if (!errorNode.hasValue()) {
Anna Zaks0bd6b112011-10-26 21:06:34 +0000625 errorNode = C.addTransition();
Ted Kremenek6fb5c1f2011-03-14 19:50:37 +0000626 }
627
628 if (!errorNode.getValue())
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000629 continue;
630
Dylan Noblesmithf7ccbad2012-02-05 02:13:05 +0000631 SmallString<128> sbuf;
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000632 llvm::raw_svector_ostream os(sbuf);
633
634 if (const char *TypeName = GetReceiverNameType(msg))
635 os << "Argument to '" << TypeName << "' method '";
636 else
637 os << "Argument to method '";
638
639 os << msg.getSelector().getAsString()
640 << "' should be an Objective-C pointer type, not '"
641 << ArgTy.getAsString() << "'";
642
Anna Zakse172e8b2011-08-17 23:00:25 +0000643 BugReport *R = new BugReport(*BT, os.str(),
Ted Kremenek6fb5c1f2011-03-14 19:50:37 +0000644 errorNode.getValue());
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000645 R->addRange(msg.getArgSourceRange(I));
646 C.EmitReport(R);
647 }
648}
649
650//===----------------------------------------------------------------------===//
Ted Kremenek78d46242008-07-22 16:21:24 +0000651// Check registration.
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000652//===----------------------------------------------------------------------===//
Argyrios Kyrtzidis0b1ba622011-02-16 01:40:52 +0000653
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +0000654void ento::registerNilArgChecker(CheckerManager &mgr) {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000655 mgr.registerChecker<NilArgChecker>();
Argyrios Kyrtzidis0b1ba622011-02-16 01:40:52 +0000656}
657
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +0000658void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000659 mgr.registerChecker<CFNumberCreateChecker>();
Argyrios Kyrtzidis0b1ba622011-02-16 01:40:52 +0000660}
661
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +0000662void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000663 mgr.registerChecker<CFRetainReleaseChecker>();
Ted Kremenek78d46242008-07-22 16:21:24 +0000664}
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +0000665
666void ento::registerClassReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000667 mgr.registerChecker<ClassReleaseChecker>();
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +0000668}
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000669
670void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
671 mgr.registerChecker<VariadicMethodTypeChecker>();
672}