blob: d4632488e98e2938d993ce6d073505e3a7ee3e4b [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
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +000016#include "ClangSACheckers.h"
Ted Kremenek6fa1dae2011-03-17 04:01:35 +000017#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +000018#include "clang/StaticAnalyzer/Core/Checker.h"
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +000019#include "clang/StaticAnalyzer/Core/CheckerManager.h"
Argyrios Kyrtzidisdff865d2011-02-23 01:05:36 +000020#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
Ted Kremenekf8cbac42011-02-10 01:03:03 +000021#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
Ted Kremenekf8cbac42011-02-10 01:03:03 +000022#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
Jordy Rose087611e2011-09-02 08:02:59 +000023#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
Ted Kremenek001fd5b2011-08-15 22:09:50 +000024#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
Ted Kremenekf8cbac42011-02-10 01:03:03 +000025#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
26#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
Daniel Dunbar6e8aa532008-08-11 05:35:13 +000027#include "clang/AST/DeclObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000028#include "clang/AST/Expr.h"
Steve Naroff021ca182008-05-29 21:12:08 +000029#include "clang/AST/ExprObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000030#include "clang/AST/ASTContext.h"
Benjamin Kramer49038022012-02-04 13:45:25 +000031#include "llvm/ADT/SmallString.h"
Jordan Rose3ba8ae32012-06-11 16:40:37 +000032#include "llvm/ADT/StringMap.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000033
Ted Kremenekc0414922008-03-27 07:25:52 +000034using namespace clang;
Ted Kremenek98857c92010-12-23 07:20:52 +000035using namespace ento;
Ted Kremeneka4d60b62008-03-27 17:17:22 +000036
Ted Kremenekbd2c8002010-10-20 23:38:56 +000037namespace {
38class APIMisuse : public BugType {
39public:
40 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
41};
42} // end anonymous namespace
43
44//===----------------------------------------------------------------------===//
45// Utility functions.
46//===----------------------------------------------------------------------===//
47
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +000048static const char* GetReceiverNameType(const ObjCMessage &msg) {
Anders Carlsson3c50aea2011-03-08 20:05:26 +000049 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
50 return ID->getIdentifier()->getNameStart();
51 return 0;
Ted Kremenek276278e2008-03-27 22:05:32 +000052}
Ted Kremeneka4d60b62008-03-27 17:17:22 +000053
Jordan Rose3ba8ae32012-06-11 16:40:37 +000054enum FoundationClass {
55 FC_None,
56 FC_NSArray,
57 FC_NSDictionary,
58 FC_NSEnumerator,
59 FC_NSOrderedSet,
60 FC_NSSet,
61 FC_NSString
62};
Anders Carlsson3c50aea2011-03-08 20:05:26 +000063
Jordan Rose3ba8ae32012-06-11 16:40:37 +000064static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID) {
65 static llvm::StringMap<FoundationClass> Classes;
66 if (Classes.empty()) {
67 Classes["NSArray"] = FC_NSArray;
68 Classes["NSDictionary"] = FC_NSDictionary;
69 Classes["NSEnumerator"] = FC_NSEnumerator;
70 Classes["NSOrderedSet"] = FC_NSOrderedSet;
71 Classes["NSSet"] = FC_NSSet;
72 Classes["NSString"] = FC_NSString;
73 }
Anders Carlsson3c50aea2011-03-08 20:05:26 +000074
Jordan Rose3ba8ae32012-06-11 16:40:37 +000075 // FIXME: Should we cache this at all?
76 FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());
77 if (result == FC_None)
78 if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
79 return findKnownClass(Super);
80
81 return result;
Ted Kremenekc0414922008-03-27 07:25:52 +000082}
83
Zhongxing Xu27f17422008-10-17 05:57:07 +000084static inline bool isNil(SVal X) {
Mike Stump11289f42009-09-09 15:08:12 +000085 return isa<loc::ConcreteInt>(X);
Ted Kremenek27156c82008-03-27 21:15:17 +000086}
87
Ted Kremenekc0414922008-03-27 07:25:52 +000088//===----------------------------------------------------------------------===//
Ted Kremenekbd2c8002010-10-20 23:38:56 +000089// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
Ted Kremenekc0414922008-03-27 07:25:52 +000090//===----------------------------------------------------------------------===//
91
Benjamin Kramer2fc373e2010-10-22 16:33:16 +000092namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +000093 class NilArgChecker : public Checker<check::PreObjCMessage> {
Dylan Noblesmithe2778992012-02-05 02:12:40 +000094 mutable OwningPtr<APIMisuse> BT;
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +000095
96 void WarnNilArg(CheckerContext &C,
97 const ObjCMessage &msg, unsigned Arg) const;
98
Benjamin Kramer2fc373e2010-10-22 16:33:16 +000099 public:
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000100 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
Benjamin Kramer2fc373e2010-10-22 16:33:16 +0000101 };
102}
Mike Stump11289f42009-09-09 15:08:12 +0000103
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000104void NilArgChecker::WarnNilArg(CheckerContext &C,
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000105 const ObjCMessage &msg,
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000106 unsigned int Arg) const
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000107{
108 if (!BT)
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000109 BT.reset(new APIMisuse("nil argument"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000110
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000111 if (ExplodedNode *N = C.generateSink()) {
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000112 SmallString<128> sbuf;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000113 llvm::raw_svector_ostream os(sbuf);
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000114 os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
115 << msg.getSelector().getAsString() << "' cannot be nil";
Mike Stump11289f42009-09-09 15:08:12 +0000116
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000117 BugReport *R = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000118 R->addRange(msg.getArgSourceRange(Arg));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000119 C.EmitReport(R);
Ted Kremenek276278e2008-03-27 22:05:32 +0000120 }
Ted Kremenek276278e2008-03-27 22:05:32 +0000121}
122
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000123void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
124 CheckerContext &C) const {
Anders Carlsson3c50aea2011-03-08 20:05:26 +0000125 const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
126 if (!ID)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000127 return;
128
Jordan Rose3ba8ae32012-06-11 16:40:37 +0000129 if (findKnownClass(ID) == FC_NSString) {
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000130 Selector S = msg.getSelector();
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000131
132 if (S.isUnarySelector())
133 return;
134
135 // FIXME: This is going to be really slow doing these checks with
136 // lexical comparisons.
137
138 std::string NameStr = S.getAsString();
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000139 StringRef Name(NameStr);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000140 assert(!Name.empty());
141
142 // FIXME: Checking for initWithFormat: will not work in most cases
143 // yet because [NSString alloc] returns id, not NSString*. We will
144 // need support for tracking expected-type information in the analyzer
145 // to find these errors.
146 if (Name == "caseInsensitiveCompare:" ||
147 Name == "compare:" ||
148 Name == "compare:options:" ||
149 Name == "compare:options:range:" ||
150 Name == "compare:options:range:locale:" ||
151 Name == "componentsSeparatedByCharactersInSet:" ||
152 Name == "initWithFormat:") {
Ted Kremenek632e3b72012-01-06 22:09:28 +0000153 if (isNil(msg.getArgSVal(0, C.getLocationContext(), C.getState())))
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000154 WarnNilArg(C, msg, 0);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000155 }
156 }
Ted Kremenekc0414922008-03-27 07:25:52 +0000157}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000158
159//===----------------------------------------------------------------------===//
160// Error reporting.
161//===----------------------------------------------------------------------===//
162
163namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000164class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000165 mutable OwningPtr<APIMisuse> BT;
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000166 mutable IdentifierInfo* II;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000167public:
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000168 CFNumberCreateChecker() : II(0) {}
169
170 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
171
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000172private:
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000173 void EmitError(const TypedRegion* R, const Expr *Ex,
Mike Stump11289f42009-09-09 15:08:12 +0000174 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000175};
176} // end anonymous namespace
177
178enum CFNumberType {
179 kCFNumberSInt8Type = 1,
180 kCFNumberSInt16Type = 2,
181 kCFNumberSInt32Type = 3,
182 kCFNumberSInt64Type = 4,
183 kCFNumberFloat32Type = 5,
184 kCFNumberFloat64Type = 6,
185 kCFNumberCharType = 7,
186 kCFNumberShortType = 8,
187 kCFNumberIntType = 9,
188 kCFNumberLongType = 10,
189 kCFNumberLongLongType = 11,
190 kCFNumberFloatType = 12,
191 kCFNumberDoubleType = 13,
192 kCFNumberCFIndexType = 14,
193 kCFNumberNSIntegerType = 15,
194 kCFNumberCGFloatType = 16
195};
196
197namespace {
198 template<typename T>
199 class Optional {
200 bool IsKnown;
201 T Val;
202 public:
203 Optional() : IsKnown(false), Val(0) {}
204 Optional(const T& val) : IsKnown(true), Val(val) {}
Mike Stump11289f42009-09-09 15:08:12 +0000205
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000206 bool isKnown() const { return IsKnown; }
207
208 const T& getValue() const {
209 assert (isKnown());
210 return Val;
211 }
212
213 operator const T&() const {
214 return getValue();
215 }
216 };
217}
218
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000219static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
Nuno Lopescfca1f02009-12-23 17:49:57 +0000220 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump11289f42009-09-09 15:08:12 +0000221
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000222 if (i < kCFNumberCharType)
223 return FixedSize[i-1];
Mike Stump11289f42009-09-09 15:08:12 +0000224
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000225 QualType T;
Mike Stump11289f42009-09-09 15:08:12 +0000226
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000227 switch (i) {
228 case kCFNumberCharType: T = Ctx.CharTy; break;
229 case kCFNumberShortType: T = Ctx.ShortTy; break;
230 case kCFNumberIntType: T = Ctx.IntTy; break;
231 case kCFNumberLongType: T = Ctx.LongTy; break;
232 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
233 case kCFNumberFloatType: T = Ctx.FloatTy; break;
234 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
235 case kCFNumberCFIndexType:
236 case kCFNumberNSIntegerType:
237 case kCFNumberCGFloatType:
Mike Stump11289f42009-09-09 15:08:12 +0000238 // FIXME: We need a way to map from names to Type*.
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000239 default:
240 return Optional<uint64_t>();
241 }
Mike Stump11289f42009-09-09 15:08:12 +0000242
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000243 return Ctx.getTypeSize(T);
244}
245
246#if 0
247static const char* GetCFNumberTypeStr(uint64_t i) {
248 static const char* Names[] = {
249 "kCFNumberSInt8Type",
250 "kCFNumberSInt16Type",
251 "kCFNumberSInt32Type",
252 "kCFNumberSInt64Type",
253 "kCFNumberFloat32Type",
254 "kCFNumberFloat64Type",
255 "kCFNumberCharType",
256 "kCFNumberShortType",
257 "kCFNumberIntType",
258 "kCFNumberLongType",
259 "kCFNumberLongLongType",
260 "kCFNumberFloatType",
261 "kCFNumberDoubleType",
262 "kCFNumberCFIndexType",
263 "kCFNumberNSIntegerType",
264 "kCFNumberCGFloatType"
265 };
Mike Stump11289f42009-09-09 15:08:12 +0000266
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000267 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
268}
269#endif
270
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000271void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
272 CheckerContext &C) const {
Ted Kremenek49b1e382012-01-26 21:29:00 +0000273 ProgramStateRef state = C.getState();
Anna Zaksc6aa5312011-12-01 05:57:37 +0000274 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000275 if (!FD)
276 return;
277
278 ASTContext &Ctx = C.getASTContext();
279 if (!II)
280 II = &Ctx.Idents.get("CFNumberCreate");
281
282 if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
283 return;
Mike Stump11289f42009-09-09 15:08:12 +0000284
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000285 // Get the value of the "theType" argument.
Ted Kremenek632e3b72012-01-06 22:09:28 +0000286 const LocationContext *LCtx = C.getLocationContext();
287 SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx);
Mike Stump11289f42009-09-09 15:08:12 +0000288
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000289 // FIXME: We really should allow ranges of valid theType values, and
290 // bifurcate the state appropriately.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000291 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000292 if (!V)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000293 return;
Mike Stump11289f42009-09-09 15:08:12 +0000294
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000295 uint64_t NumberKind = V->getValue().getLimitedValue();
296 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000297
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000298 // FIXME: In some cases we can emit an error.
299 if (!TargetSize.isKnown())
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000300 return;
Mike Stump11289f42009-09-09 15:08:12 +0000301
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000302 // Look at the value of the integer being passed by reference. Essentially
303 // we want to catch cases where the value passed in is not equal to the
304 // size of the type being created.
Ted Kremenek632e3b72012-01-06 22:09:28 +0000305 SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx);
Mike Stump11289f42009-09-09 15:08:12 +0000306
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000307 // FIXME: Eventually we should handle arbitrary locations. We can do this
308 // by having an enhanced memory model that does low-level typing.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000309 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000310 if (!LV)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000311 return;
Mike Stump11289f42009-09-09 15:08:12 +0000312
Ted Kremenek8df44b262011-08-12 20:02:48 +0000313 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
Ted Kremenek87a7a452009-07-29 18:17:40 +0000314 if (!R)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000315 return;
Ted Kremenek87a7a452009-07-29 18:17:40 +0000316
Zhongxing Xu8de0a3d2010-08-11 06:10:55 +0000317 QualType T = Ctx.getCanonicalType(R->getValueType());
Mike Stump11289f42009-09-09 15:08:12 +0000318
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000319 // FIXME: If the pointee isn't an integer type, should we flag a warning?
320 // People can do weird stuff with pointers.
Mike Stump11289f42009-09-09 15:08:12 +0000321
322 if (!T->isIntegerType())
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000323 return;
Mike Stump11289f42009-09-09 15:08:12 +0000324
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000325 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump11289f42009-09-09 15:08:12 +0000326
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000327 // CHECK: is SourceSize == TargetSize
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000328 if (SourceSize == TargetSize)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000329 return;
Mike Stump11289f42009-09-09 15:08:12 +0000330
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000331 // Generate an error. Only generate a sink if 'SourceSize < TargetSize';
332 // otherwise generate a regular node.
333 //
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000334 // FIXME: We can actually create an abstract "CFNumber" object that has
335 // the bits initialized to the provided values.
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000336 //
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000337 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
Anna Zaksda4c8d62011-10-26 21:06:34 +0000338 : C.addTransition()) {
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000339 SmallString<128> sbuf;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000340 llvm::raw_svector_ostream os(sbuf);
341
342 os << (SourceSize == 8 ? "An " : "A ")
343 << SourceSize << " bit integer is used to initialize a CFNumber "
344 "object that represents "
345 << (TargetSize == 8 ? "an " : "a ")
346 << TargetSize << " bit integer. ";
347
348 if (SourceSize < TargetSize)
349 os << (TargetSize - SourceSize)
350 << " bits of the CFNumber value will be garbage." ;
351 else
352 os << (SourceSize - TargetSize)
353 << " bits of the input integer will be lost.";
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000354
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000355 if (!BT)
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000356 BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000357
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000358 BugReport *report = new BugReport(*BT, os.str(), N);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000359 report->addRange(CE->getArg(2)->getSourceRange());
360 C.EmitReport(report);
361 }
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000362}
363
Ted Kremenek1f352db2008-07-22 16:21:24 +0000364//===----------------------------------------------------------------------===//
Jordy Rose40c5c242010-07-06 02:34:42 +0000365// CFRetain/CFRelease checking for null arguments.
Ted Kremenekc057f412009-07-14 00:43:42 +0000366//===----------------------------------------------------------------------===//
367
368namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000369class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000370 mutable OwningPtr<APIMisuse> BT;
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000371 mutable IdentifierInfo *Retain, *Release;
Ted Kremenekc057f412009-07-14 00:43:42 +0000372public:
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000373 CFRetainReleaseChecker(): Retain(0), Release(0) {}
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000374 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
Ted Kremenekc057f412009-07-14 00:43:42 +0000375};
376} // end anonymous namespace
377
378
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000379void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
380 CheckerContext &C) const {
Ted Kremenekc057f412009-07-14 00:43:42 +0000381 // If the CallExpr doesn't have exactly 1 argument just give up checking.
382 if (CE->getNumArgs() != 1)
Jordy Rose40c5c242010-07-06 02:34:42 +0000383 return;
Mike Stump11289f42009-09-09 15:08:12 +0000384
Ted Kremenek49b1e382012-01-26 21:29:00 +0000385 ProgramStateRef state = C.getState();
Anna Zaksc6aa5312011-12-01 05:57:37 +0000386 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenekc057f412009-07-14 00:43:42 +0000387 if (!FD)
Jordy Rose40c5c242010-07-06 02:34:42 +0000388 return;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000389
390 if (!BT) {
391 ASTContext &Ctx = C.getASTContext();
392 Retain = &Ctx.Idents.get("CFRetain");
393 Release = &Ctx.Idents.get("CFRelease");
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000394 BT.reset(new APIMisuse("null passed to CFRetain/CFRelease"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000395 }
Mike Stump11289f42009-09-09 15:08:12 +0000396
Jordy Rose40c5c242010-07-06 02:34:42 +0000397 // Check if we called CFRetain/CFRelease.
Mike Stump11289f42009-09-09 15:08:12 +0000398 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenekc057f412009-07-14 00:43:42 +0000399 if (!(FuncII == Retain || FuncII == Release))
Jordy Rose40c5c242010-07-06 02:34:42 +0000400 return;
Mike Stump11289f42009-09-09 15:08:12 +0000401
Jordy Rose40c5c242010-07-06 02:34:42 +0000402 // FIXME: The rest of this just checks that the argument is non-null.
403 // It should probably be refactored and combined with AttrNonNullChecker.
404
405 // Get the argument's value.
406 const Expr *Arg = CE->getArg(0);
Ted Kremenek632e3b72012-01-06 22:09:28 +0000407 SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
Jordy Rose40c5c242010-07-06 02:34:42 +0000408 DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
409 if (!DefArgVal)
410 return;
411
412 // Get a NULL value.
Ted Kremenek90af9092010-12-02 07:49:45 +0000413 SValBuilder &svalBuilder = C.getSValBuilder();
414 DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
Jordy Rose40c5c242010-07-06 02:34:42 +0000415
416 // Make an expression asserting that they're equal.
Ted Kremenek90af9092010-12-02 07:49:45 +0000417 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
Jordy Rose40c5c242010-07-06 02:34:42 +0000418
419 // Are they equal?
Ted Kremenek49b1e382012-01-26 21:29:00 +0000420 ProgramStateRef stateTrue, stateFalse;
Ted Kremenekc5bea1e2010-12-01 22:16:56 +0000421 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
Jordy Rose40c5c242010-07-06 02:34:42 +0000422
423 if (stateTrue && !stateFalse) {
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000424 ExplodedNode *N = C.generateSink(stateTrue);
Jordy Rose40c5c242010-07-06 02:34:42 +0000425 if (!N)
426 return;
427
Ted Kremenekc057f412009-07-14 00:43:42 +0000428 const char *description = (FuncII == Retain)
429 ? "Null pointer argument in call to CFRetain"
430 : "Null pointer argument in call to CFRelease";
431
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000432 BugReport *report = new BugReport(*BT, description, N);
Jordy Rose40c5c242010-07-06 02:34:42 +0000433 report->addRange(Arg->getSourceRange());
Ted Kremenek1e809b42012-03-09 01:13:14 +0000434 report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg,
435 report));
Jordy Rose40c5c242010-07-06 02:34:42 +0000436 C.EmitReport(report);
437 return;
Ted Kremenekc057f412009-07-14 00:43:42 +0000438 }
439
Jordy Rose40c5c242010-07-06 02:34:42 +0000440 // From here on, we know the argument is non-null.
Anna Zaksda4c8d62011-10-26 21:06:34 +0000441 C.addTransition(stateFalse);
Ted Kremenekc057f412009-07-14 00:43:42 +0000442}
443
444//===----------------------------------------------------------------------===//
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000445// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
446//===----------------------------------------------------------------------===//
447
448namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000449class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000450 mutable Selector releaseS;
451 mutable Selector retainS;
452 mutable Selector autoreleaseS;
453 mutable Selector drainS;
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000454 mutable OwningPtr<BugType> BT;
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000455
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000456public:
457 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000458};
459}
460
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000461void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
462 CheckerContext &C) const {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000463
464 if (!BT) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000465 BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
466 "instance"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000467
468 ASTContext &Ctx = C.getASTContext();
469 releaseS = GetNullarySelector("release", Ctx);
470 retainS = GetNullarySelector("retain", Ctx);
471 autoreleaseS = GetNullarySelector("autorelease", Ctx);
472 drainS = GetNullarySelector("drain", Ctx);
473 }
474
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000475 if (msg.isInstanceMessage())
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000476 return;
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000477 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
478 assert(Class);
Douglas Gregor9a129192010-04-21 00:45:42 +0000479
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000480 Selector S = msg.getSelector();
Benjamin Kramer7d875c72009-11-20 10:03:00 +0000481 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000482 return;
483
Anna Zaksda4c8d62011-10-26 21:06:34 +0000484 if (ExplodedNode *N = C.addTransition()) {
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000485 SmallString<200> buf;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000486 llvm::raw_svector_ostream os(buf);
Ted Kremenekf5735152009-11-23 22:22:01 +0000487
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000488 os << "The '" << S.getAsString() << "' message should be sent to instances "
489 "of class '" << Class->getName()
490 << "' and not the class directly";
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000491
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000492 BugReport *report = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000493 report->addRange(msg.getSourceRange());
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000494 C.EmitReport(report);
495 }
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000496}
497
498//===----------------------------------------------------------------------===//
Anders Carlssond91d5f12011-03-13 20:35:21 +0000499// Check for passing non-Objective-C types to variadic methods that expect
500// only Objective-C types.
501//===----------------------------------------------------------------------===//
502
503namespace {
504class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
505 mutable Selector arrayWithObjectsS;
506 mutable Selector dictionaryWithObjectsAndKeysS;
507 mutable Selector setWithObjectsS;
Jordy Rosec0230d72012-04-06 19:06:01 +0000508 mutable Selector orderedSetWithObjectsS;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000509 mutable Selector initWithObjectsS;
510 mutable Selector initWithObjectsAndKeysS;
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000511 mutable OwningPtr<BugType> BT;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000512
513 bool isVariadicMessage(const ObjCMessage &msg) const;
514
515public:
516 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
517};
518}
519
520/// isVariadicMessage - Returns whether the given message is a variadic message,
521/// where all arguments must be Objective-C types.
522bool
523VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const {
524 const ObjCMethodDecl *MD = msg.getMethodDecl();
Ted Kremenekced5fea2011-04-12 21:47:05 +0000525
526 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
Anders Carlssond91d5f12011-03-13 20:35:21 +0000527 return false;
528
529 Selector S = msg.getSelector();
530
531 if (msg.isInstanceMessage()) {
532 // FIXME: Ideally we'd look at the receiver interface here, but that's not
533 // useful for init, because alloc returns 'id'. In theory, this could lead
534 // to false positives, for example if there existed a class that had an
535 // initWithObjects: implementation that does accept non-Objective-C pointer
536 // types, but the chance of that happening is pretty small compared to the
537 // gains that this analysis gives.
538 const ObjCInterfaceDecl *Class = MD->getClassInterface();
539
Jordan Rose3ba8ae32012-06-11 16:40:37 +0000540 switch (findKnownClass(Class)) {
541 case FC_NSArray:
542 case FC_NSOrderedSet:
543 case FC_NSSet:
544 return S == initWithObjectsS;
545 case FC_NSDictionary:
546 return S == initWithObjectsAndKeysS;
547 default:
548 return false;
549 }
Anders Carlssond91d5f12011-03-13 20:35:21 +0000550 } else {
551 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
552
Jordan Rose3ba8ae32012-06-11 16:40:37 +0000553 switch (findKnownClass(Class)) {
554 case FC_NSArray:
555 return S == arrayWithObjectsS;
556 case FC_NSOrderedSet:
557 return S == orderedSetWithObjectsS;
558 case FC_NSSet:
559 return S == setWithObjectsS;
560 case FC_NSDictionary:
561 return S == dictionaryWithObjectsAndKeysS;
562 default:
563 return false;
564 }
Anders Carlssond91d5f12011-03-13 20:35:21 +0000565 }
Anders Carlssond91d5f12011-03-13 20:35:21 +0000566}
567
568void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
569 CheckerContext &C) const {
570 if (!BT) {
571 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
572 "Objective-C pointer types"));
573
574 ASTContext &Ctx = C.getASTContext();
575 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
576 dictionaryWithObjectsAndKeysS =
577 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
578 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
Jordy Rosec0230d72012-04-06 19:06:01 +0000579 orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
Anders Carlssond91d5f12011-03-13 20:35:21 +0000580
581 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
582 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
583 }
584
585 if (!isVariadicMessage(msg))
586 return;
587
588 // We are not interested in the selector arguments since they have
589 // well-defined types, so the compiler will issue a warning for them.
590 unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
591
592 // We're not interested in the last argument since it has to be nil or the
593 // compiler would have issued a warning for it elsewhere.
594 unsigned variadicArgsEnd = msg.getNumArgs() - 1;
595
596 if (variadicArgsEnd <= variadicArgsBegin)
597 return;
598
599 // Verify that all arguments have Objective-C types.
Ted Kremenek066b2262011-03-14 19:50:37 +0000600 llvm::Optional<ExplodedNode*> errorNode;
Ted Kremenek49b1e382012-01-26 21:29:00 +0000601 ProgramStateRef state = C.getState();
Ted Kremenek066b2262011-03-14 19:50:37 +0000602
Anders Carlssond91d5f12011-03-13 20:35:21 +0000603 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
604 QualType ArgTy = msg.getArgType(I);
605 if (ArgTy->isObjCObjectPointerType())
606 continue;
607
Anders Carlssond1f65f62011-04-19 01:16:46 +0000608 // Block pointers are treaded as Objective-C pointers.
609 if (ArgTy->isBlockPointerType())
610 continue;
611
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000612 // Ignore pointer constants.
Ted Kremenek632e3b72012-01-06 22:09:28 +0000613 if (isa<loc::ConcreteInt>(msg.getArgSVal(I, C.getLocationContext(),
614 state)))
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000615 continue;
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000616
Ted Kremenek70727342011-03-17 04:10:25 +0000617 // Ignore pointer types annotated with 'NSObject' attribute.
618 if (C.getASTContext().isObjCNSObjectType(ArgTy))
619 continue;
620
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000621 // Ignore CF references, which can be toll-free bridged.
Ted Kremenekc85964e2011-07-16 19:50:32 +0000622 if (coreFoundation::isCFObjectRef(ArgTy))
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000623 continue;
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000624
Ted Kremenek066b2262011-03-14 19:50:37 +0000625 // Generate only one error node to use for all bug reports.
626 if (!errorNode.hasValue()) {
Anna Zaksda4c8d62011-10-26 21:06:34 +0000627 errorNode = C.addTransition();
Ted Kremenek066b2262011-03-14 19:50:37 +0000628 }
629
630 if (!errorNode.getValue())
Anders Carlssond91d5f12011-03-13 20:35:21 +0000631 continue;
632
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000633 SmallString<128> sbuf;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000634 llvm::raw_svector_ostream os(sbuf);
635
636 if (const char *TypeName = GetReceiverNameType(msg))
637 os << "Argument to '" << TypeName << "' method '";
638 else
639 os << "Argument to method '";
640
641 os << msg.getSelector().getAsString()
642 << "' should be an Objective-C pointer type, not '"
643 << ArgTy.getAsString() << "'";
644
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000645 BugReport *R = new BugReport(*BT, os.str(),
Ted Kremenek066b2262011-03-14 19:50:37 +0000646 errorNode.getValue());
Anders Carlssond91d5f12011-03-13 20:35:21 +0000647 R->addRange(msg.getArgSourceRange(I));
648 C.EmitReport(R);
649 }
650}
651
652//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000653// Check registration.
Ted Kremenekc057f412009-07-14 00:43:42 +0000654//===----------------------------------------------------------------------===//
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000655
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000656void ento::registerNilArgChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000657 mgr.registerChecker<NilArgChecker>();
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000658}
659
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000660void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000661 mgr.registerChecker<CFNumberCreateChecker>();
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000662}
663
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000664void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000665 mgr.registerChecker<CFRetainReleaseChecker>();
Ted Kremenek1f352db2008-07-22 16:21:24 +0000666}
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000667
668void ento::registerClassReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000669 mgr.registerChecker<ClassReleaseChecker>();
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000670}
Anders Carlssond91d5f12011-03-13 20:35:21 +0000671
672void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
673 mgr.registerChecker<VariadicMethodTypeChecker>();
674}