1e5dd7070Spatrick //== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This file defines BasicObjCFoundationChecks, a class that encapsulates
10e5dd7070Spatrick // a set of simple checks to run on Objective-C code using Apple's Foundation
11e5dd7070Spatrick // classes.
12e5dd7070Spatrick //
13e5dd7070Spatrick //===----------------------------------------------------------------------===//
14e5dd7070Spatrick
15e5dd7070Spatrick #include "clang/AST/ASTContext.h"
16e5dd7070Spatrick #include "clang/AST/DeclObjC.h"
17e5dd7070Spatrick #include "clang/AST/Expr.h"
18e5dd7070Spatrick #include "clang/AST/ExprObjC.h"
19e5dd7070Spatrick #include "clang/AST/StmtObjC.h"
20e5dd7070Spatrick #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
21e5dd7070Spatrick #include "clang/Analysis/SelectorExtras.h"
22*12c85518Srobert #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
23e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
24e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
25e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/CheckerManager.h"
26*12c85518Srobert #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
27e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
28e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
29e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
30e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
31e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
32e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
33e5dd7070Spatrick #include "llvm/ADT/SmallString.h"
34e5dd7070Spatrick #include "llvm/ADT/StringMap.h"
35e5dd7070Spatrick #include "llvm/Support/raw_ostream.h"
36*12c85518Srobert #include <optional>
37e5dd7070Spatrick
38e5dd7070Spatrick using namespace clang;
39e5dd7070Spatrick using namespace ento;
40e5dd7070Spatrick using namespace llvm;
41e5dd7070Spatrick
42e5dd7070Spatrick namespace {
43e5dd7070Spatrick class APIMisuse : public BugType {
44e5dd7070Spatrick public:
APIMisuse(const CheckerBase * checker,const char * name)45e5dd7070Spatrick APIMisuse(const CheckerBase *checker, const char *name)
46e5dd7070Spatrick : BugType(checker, name, "API Misuse (Apple)") {}
47e5dd7070Spatrick };
48e5dd7070Spatrick } // end anonymous namespace
49e5dd7070Spatrick
50e5dd7070Spatrick //===----------------------------------------------------------------------===//
51e5dd7070Spatrick // Utility functions.
52e5dd7070Spatrick //===----------------------------------------------------------------------===//
53e5dd7070Spatrick
GetReceiverInterfaceName(const ObjCMethodCall & msg)54e5dd7070Spatrick static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) {
55e5dd7070Spatrick if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
56e5dd7070Spatrick return ID->getIdentifier()->getName();
57e5dd7070Spatrick return StringRef();
58e5dd7070Spatrick }
59e5dd7070Spatrick
60e5dd7070Spatrick enum FoundationClass {
61e5dd7070Spatrick FC_None,
62e5dd7070Spatrick FC_NSArray,
63e5dd7070Spatrick FC_NSDictionary,
64e5dd7070Spatrick FC_NSEnumerator,
65e5dd7070Spatrick FC_NSNull,
66e5dd7070Spatrick FC_NSOrderedSet,
67e5dd7070Spatrick FC_NSSet,
68e5dd7070Spatrick FC_NSString
69e5dd7070Spatrick };
70e5dd7070Spatrick
findKnownClass(const ObjCInterfaceDecl * ID,bool IncludeSuperclasses=true)71e5dd7070Spatrick static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID,
72e5dd7070Spatrick bool IncludeSuperclasses = true) {
73e5dd7070Spatrick static llvm::StringMap<FoundationClass> Classes;
74e5dd7070Spatrick if (Classes.empty()) {
75e5dd7070Spatrick Classes["NSArray"] = FC_NSArray;
76e5dd7070Spatrick Classes["NSDictionary"] = FC_NSDictionary;
77e5dd7070Spatrick Classes["NSEnumerator"] = FC_NSEnumerator;
78e5dd7070Spatrick Classes["NSNull"] = FC_NSNull;
79e5dd7070Spatrick Classes["NSOrderedSet"] = FC_NSOrderedSet;
80e5dd7070Spatrick Classes["NSSet"] = FC_NSSet;
81e5dd7070Spatrick Classes["NSString"] = FC_NSString;
82e5dd7070Spatrick }
83e5dd7070Spatrick
84e5dd7070Spatrick // FIXME: Should we cache this at all?
85e5dd7070Spatrick FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());
86e5dd7070Spatrick if (result == FC_None && IncludeSuperclasses)
87e5dd7070Spatrick if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
88e5dd7070Spatrick return findKnownClass(Super);
89e5dd7070Spatrick
90e5dd7070Spatrick return result;
91e5dd7070Spatrick }
92e5dd7070Spatrick
93e5dd7070Spatrick //===----------------------------------------------------------------------===//
94e5dd7070Spatrick // NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
95e5dd7070Spatrick //===----------------------------------------------------------------------===//
96e5dd7070Spatrick
97e5dd7070Spatrick namespace {
98e5dd7070Spatrick class NilArgChecker : public Checker<check::PreObjCMessage,
99e5dd7070Spatrick check::PostStmt<ObjCDictionaryLiteral>,
100e5dd7070Spatrick check::PostStmt<ObjCArrayLiteral> > {
101e5dd7070Spatrick mutable std::unique_ptr<APIMisuse> BT;
102e5dd7070Spatrick
103e5dd7070Spatrick mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors;
104e5dd7070Spatrick mutable Selector ArrayWithObjectSel;
105e5dd7070Spatrick mutable Selector AddObjectSel;
106e5dd7070Spatrick mutable Selector InsertObjectAtIndexSel;
107e5dd7070Spatrick mutable Selector ReplaceObjectAtIndexWithObjectSel;
108e5dd7070Spatrick mutable Selector SetObjectAtIndexedSubscriptSel;
109e5dd7070Spatrick mutable Selector ArrayByAddingObjectSel;
110e5dd7070Spatrick mutable Selector DictionaryWithObjectForKeySel;
111e5dd7070Spatrick mutable Selector SetObjectForKeySel;
112e5dd7070Spatrick mutable Selector SetObjectForKeyedSubscriptSel;
113e5dd7070Spatrick mutable Selector RemoveObjectForKeySel;
114e5dd7070Spatrick
115e5dd7070Spatrick void warnIfNilExpr(const Expr *E,
116e5dd7070Spatrick const char *Msg,
117e5dd7070Spatrick CheckerContext &C) const;
118e5dd7070Spatrick
119e5dd7070Spatrick void warnIfNilArg(CheckerContext &C,
120e5dd7070Spatrick const ObjCMethodCall &msg, unsigned Arg,
121e5dd7070Spatrick FoundationClass Class,
122e5dd7070Spatrick bool CanBeSubscript = false) const;
123e5dd7070Spatrick
124e5dd7070Spatrick void generateBugReport(ExplodedNode *N,
125e5dd7070Spatrick StringRef Msg,
126e5dd7070Spatrick SourceRange Range,
127e5dd7070Spatrick const Expr *Expr,
128e5dd7070Spatrick CheckerContext &C) const;
129e5dd7070Spatrick
130e5dd7070Spatrick public:
131e5dd7070Spatrick void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
132e5dd7070Spatrick void checkPostStmt(const ObjCDictionaryLiteral *DL,
133e5dd7070Spatrick CheckerContext &C) const;
134e5dd7070Spatrick void checkPostStmt(const ObjCArrayLiteral *AL,
135e5dd7070Spatrick CheckerContext &C) const;
136e5dd7070Spatrick };
137e5dd7070Spatrick } // end anonymous namespace
138e5dd7070Spatrick
warnIfNilExpr(const Expr * E,const char * Msg,CheckerContext & C) const139e5dd7070Spatrick void NilArgChecker::warnIfNilExpr(const Expr *E,
140e5dd7070Spatrick const char *Msg,
141e5dd7070Spatrick CheckerContext &C) const {
142e5dd7070Spatrick ProgramStateRef State = C.getState();
143e5dd7070Spatrick if (State->isNull(C.getSVal(E)).isConstrainedTrue()) {
144e5dd7070Spatrick
145e5dd7070Spatrick if (ExplodedNode *N = C.generateErrorNode()) {
146e5dd7070Spatrick generateBugReport(N, Msg, E->getSourceRange(), E, C);
147e5dd7070Spatrick }
148e5dd7070Spatrick }
149e5dd7070Spatrick }
150e5dd7070Spatrick
warnIfNilArg(CheckerContext & C,const ObjCMethodCall & msg,unsigned int Arg,FoundationClass Class,bool CanBeSubscript) const151e5dd7070Spatrick void NilArgChecker::warnIfNilArg(CheckerContext &C,
152e5dd7070Spatrick const ObjCMethodCall &msg,
153e5dd7070Spatrick unsigned int Arg,
154e5dd7070Spatrick FoundationClass Class,
155e5dd7070Spatrick bool CanBeSubscript) const {
156e5dd7070Spatrick // Check if the argument is nil.
157e5dd7070Spatrick ProgramStateRef State = C.getState();
158e5dd7070Spatrick if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
159e5dd7070Spatrick return;
160e5dd7070Spatrick
161e5dd7070Spatrick // NOTE: We cannot throw non-fatal errors from warnIfNilExpr,
162e5dd7070Spatrick // because it's called multiple times from some callers, so it'd cause
163e5dd7070Spatrick // an unwanted state split if two or more non-fatal errors are thrown
164e5dd7070Spatrick // within the same checker callback. For now we don't want to, but
165e5dd7070Spatrick // it'll need to be fixed if we ever want to.
166e5dd7070Spatrick if (ExplodedNode *N = C.generateErrorNode()) {
167e5dd7070Spatrick SmallString<128> sbuf;
168e5dd7070Spatrick llvm::raw_svector_ostream os(sbuf);
169e5dd7070Spatrick
170e5dd7070Spatrick if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) {
171e5dd7070Spatrick
172e5dd7070Spatrick if (Class == FC_NSArray) {
173e5dd7070Spatrick os << "Array element cannot be nil";
174e5dd7070Spatrick } else if (Class == FC_NSDictionary) {
175e5dd7070Spatrick if (Arg == 0) {
176e5dd7070Spatrick os << "Value stored into '";
177e5dd7070Spatrick os << GetReceiverInterfaceName(msg) << "' cannot be nil";
178e5dd7070Spatrick } else {
179e5dd7070Spatrick assert(Arg == 1);
180e5dd7070Spatrick os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil";
181e5dd7070Spatrick }
182e5dd7070Spatrick } else
183e5dd7070Spatrick llvm_unreachable("Missing foundation class for the subscript expr");
184e5dd7070Spatrick
185e5dd7070Spatrick } else {
186e5dd7070Spatrick if (Class == FC_NSDictionary) {
187e5dd7070Spatrick if (Arg == 0)
188e5dd7070Spatrick os << "Value argument ";
189e5dd7070Spatrick else {
190e5dd7070Spatrick assert(Arg == 1);
191e5dd7070Spatrick os << "Key argument ";
192e5dd7070Spatrick }
193e5dd7070Spatrick os << "to '";
194e5dd7070Spatrick msg.getSelector().print(os);
195e5dd7070Spatrick os << "' cannot be nil";
196e5dd7070Spatrick } else {
197e5dd7070Spatrick os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '";
198e5dd7070Spatrick msg.getSelector().print(os);
199e5dd7070Spatrick os << "' cannot be nil";
200e5dd7070Spatrick }
201e5dd7070Spatrick }
202e5dd7070Spatrick
203e5dd7070Spatrick generateBugReport(N, os.str(), msg.getArgSourceRange(Arg),
204e5dd7070Spatrick msg.getArgExpr(Arg), C);
205e5dd7070Spatrick }
206e5dd7070Spatrick }
207e5dd7070Spatrick
generateBugReport(ExplodedNode * N,StringRef Msg,SourceRange Range,const Expr * E,CheckerContext & C) const208e5dd7070Spatrick void NilArgChecker::generateBugReport(ExplodedNode *N,
209e5dd7070Spatrick StringRef Msg,
210e5dd7070Spatrick SourceRange Range,
211e5dd7070Spatrick const Expr *E,
212e5dd7070Spatrick CheckerContext &C) const {
213e5dd7070Spatrick if (!BT)
214e5dd7070Spatrick BT.reset(new APIMisuse(this, "nil argument"));
215e5dd7070Spatrick
216e5dd7070Spatrick auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
217e5dd7070Spatrick R->addRange(Range);
218e5dd7070Spatrick bugreporter::trackExpressionValue(N, E, *R);
219e5dd7070Spatrick C.emitReport(std::move(R));
220e5dd7070Spatrick }
221e5dd7070Spatrick
checkPreObjCMessage(const ObjCMethodCall & msg,CheckerContext & C) const222e5dd7070Spatrick void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
223e5dd7070Spatrick CheckerContext &C) const {
224e5dd7070Spatrick const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
225e5dd7070Spatrick if (!ID)
226e5dd7070Spatrick return;
227e5dd7070Spatrick
228e5dd7070Spatrick FoundationClass Class = findKnownClass(ID);
229e5dd7070Spatrick
230e5dd7070Spatrick static const unsigned InvalidArgIndex = UINT_MAX;
231e5dd7070Spatrick unsigned Arg = InvalidArgIndex;
232e5dd7070Spatrick bool CanBeSubscript = false;
233e5dd7070Spatrick
234e5dd7070Spatrick if (Class == FC_NSString) {
235e5dd7070Spatrick Selector S = msg.getSelector();
236e5dd7070Spatrick
237e5dd7070Spatrick if (S.isUnarySelector())
238e5dd7070Spatrick return;
239e5dd7070Spatrick
240e5dd7070Spatrick if (StringSelectors.empty()) {
241e5dd7070Spatrick ASTContext &Ctx = C.getASTContext();
242e5dd7070Spatrick Selector Sels[] = {
243e5dd7070Spatrick getKeywordSelector(Ctx, "caseInsensitiveCompare"),
244e5dd7070Spatrick getKeywordSelector(Ctx, "compare"),
245e5dd7070Spatrick getKeywordSelector(Ctx, "compare", "options"),
246e5dd7070Spatrick getKeywordSelector(Ctx, "compare", "options", "range"),
247e5dd7070Spatrick getKeywordSelector(Ctx, "compare", "options", "range", "locale"),
248e5dd7070Spatrick getKeywordSelector(Ctx, "componentsSeparatedByCharactersInSet"),
249e5dd7070Spatrick getKeywordSelector(Ctx, "initWithFormat"),
250e5dd7070Spatrick getKeywordSelector(Ctx, "localizedCaseInsensitiveCompare"),
251e5dd7070Spatrick getKeywordSelector(Ctx, "localizedCompare"),
252e5dd7070Spatrick getKeywordSelector(Ctx, "localizedStandardCompare"),
253e5dd7070Spatrick };
254e5dd7070Spatrick for (Selector KnownSel : Sels)
255e5dd7070Spatrick StringSelectors[KnownSel] = 0;
256e5dd7070Spatrick }
257e5dd7070Spatrick auto I = StringSelectors.find(S);
258e5dd7070Spatrick if (I == StringSelectors.end())
259e5dd7070Spatrick return;
260e5dd7070Spatrick Arg = I->second;
261e5dd7070Spatrick } else if (Class == FC_NSArray) {
262e5dd7070Spatrick Selector S = msg.getSelector();
263e5dd7070Spatrick
264e5dd7070Spatrick if (S.isUnarySelector())
265e5dd7070Spatrick return;
266e5dd7070Spatrick
267e5dd7070Spatrick if (ArrayWithObjectSel.isNull()) {
268e5dd7070Spatrick ASTContext &Ctx = C.getASTContext();
269e5dd7070Spatrick ArrayWithObjectSel = getKeywordSelector(Ctx, "arrayWithObject");
270e5dd7070Spatrick AddObjectSel = getKeywordSelector(Ctx, "addObject");
271e5dd7070Spatrick InsertObjectAtIndexSel =
272e5dd7070Spatrick getKeywordSelector(Ctx, "insertObject", "atIndex");
273e5dd7070Spatrick ReplaceObjectAtIndexWithObjectSel =
274e5dd7070Spatrick getKeywordSelector(Ctx, "replaceObjectAtIndex", "withObject");
275e5dd7070Spatrick SetObjectAtIndexedSubscriptSel =
276e5dd7070Spatrick getKeywordSelector(Ctx, "setObject", "atIndexedSubscript");
277e5dd7070Spatrick ArrayByAddingObjectSel = getKeywordSelector(Ctx, "arrayByAddingObject");
278e5dd7070Spatrick }
279e5dd7070Spatrick
280e5dd7070Spatrick if (S == ArrayWithObjectSel || S == AddObjectSel ||
281e5dd7070Spatrick S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) {
282e5dd7070Spatrick Arg = 0;
283e5dd7070Spatrick } else if (S == SetObjectAtIndexedSubscriptSel) {
284e5dd7070Spatrick Arg = 0;
285e5dd7070Spatrick CanBeSubscript = true;
286e5dd7070Spatrick } else if (S == ReplaceObjectAtIndexWithObjectSel) {
287e5dd7070Spatrick Arg = 1;
288e5dd7070Spatrick }
289e5dd7070Spatrick } else if (Class == FC_NSDictionary) {
290e5dd7070Spatrick Selector S = msg.getSelector();
291e5dd7070Spatrick
292e5dd7070Spatrick if (S.isUnarySelector())
293e5dd7070Spatrick return;
294e5dd7070Spatrick
295e5dd7070Spatrick if (DictionaryWithObjectForKeySel.isNull()) {
296e5dd7070Spatrick ASTContext &Ctx = C.getASTContext();
297e5dd7070Spatrick DictionaryWithObjectForKeySel =
298e5dd7070Spatrick getKeywordSelector(Ctx, "dictionaryWithObject", "forKey");
299e5dd7070Spatrick SetObjectForKeySel = getKeywordSelector(Ctx, "setObject", "forKey");
300e5dd7070Spatrick SetObjectForKeyedSubscriptSel =
301e5dd7070Spatrick getKeywordSelector(Ctx, "setObject", "forKeyedSubscript");
302e5dd7070Spatrick RemoveObjectForKeySel = getKeywordSelector(Ctx, "removeObjectForKey");
303e5dd7070Spatrick }
304e5dd7070Spatrick
305e5dd7070Spatrick if (S == DictionaryWithObjectForKeySel || S == SetObjectForKeySel) {
306e5dd7070Spatrick Arg = 0;
307e5dd7070Spatrick warnIfNilArg(C, msg, /* Arg */1, Class);
308e5dd7070Spatrick } else if (S == SetObjectForKeyedSubscriptSel) {
309e5dd7070Spatrick CanBeSubscript = true;
310e5dd7070Spatrick Arg = 1;
311e5dd7070Spatrick } else if (S == RemoveObjectForKeySel) {
312e5dd7070Spatrick Arg = 0;
313e5dd7070Spatrick }
314e5dd7070Spatrick }
315e5dd7070Spatrick
316e5dd7070Spatrick // If argument is '0', report a warning.
317e5dd7070Spatrick if ((Arg != InvalidArgIndex))
318e5dd7070Spatrick warnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
319e5dd7070Spatrick }
320e5dd7070Spatrick
checkPostStmt(const ObjCArrayLiteral * AL,CheckerContext & C) const321e5dd7070Spatrick void NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL,
322e5dd7070Spatrick CheckerContext &C) const {
323e5dd7070Spatrick unsigned NumOfElements = AL->getNumElements();
324e5dd7070Spatrick for (unsigned i = 0; i < NumOfElements; ++i) {
325e5dd7070Spatrick warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C);
326e5dd7070Spatrick }
327e5dd7070Spatrick }
328e5dd7070Spatrick
checkPostStmt(const ObjCDictionaryLiteral * DL,CheckerContext & C) const329e5dd7070Spatrick void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
330e5dd7070Spatrick CheckerContext &C) const {
331e5dd7070Spatrick unsigned NumOfElements = DL->getNumElements();
332e5dd7070Spatrick for (unsigned i = 0; i < NumOfElements; ++i) {
333e5dd7070Spatrick ObjCDictionaryElement Element = DL->getKeyValueElement(i);
334e5dd7070Spatrick warnIfNilExpr(Element.Key, "Dictionary key cannot be nil", C);
335e5dd7070Spatrick warnIfNilExpr(Element.Value, "Dictionary value cannot be nil", C);
336e5dd7070Spatrick }
337e5dd7070Spatrick }
338e5dd7070Spatrick
339e5dd7070Spatrick //===----------------------------------------------------------------------===//
340e5dd7070Spatrick // Checking for mismatched types passed to CFNumberCreate/CFNumberGetValue.
341e5dd7070Spatrick //===----------------------------------------------------------------------===//
342e5dd7070Spatrick
343e5dd7070Spatrick namespace {
344e5dd7070Spatrick class CFNumberChecker : public Checker< check::PreStmt<CallExpr> > {
345e5dd7070Spatrick mutable std::unique_ptr<APIMisuse> BT;
346e5dd7070Spatrick mutable IdentifierInfo *ICreate, *IGetValue;
347e5dd7070Spatrick public:
CFNumberChecker()348e5dd7070Spatrick CFNumberChecker() : ICreate(nullptr), IGetValue(nullptr) {}
349e5dd7070Spatrick
350e5dd7070Spatrick void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
351e5dd7070Spatrick };
352e5dd7070Spatrick } // end anonymous namespace
353e5dd7070Spatrick
354e5dd7070Spatrick enum CFNumberType {
355e5dd7070Spatrick kCFNumberSInt8Type = 1,
356e5dd7070Spatrick kCFNumberSInt16Type = 2,
357e5dd7070Spatrick kCFNumberSInt32Type = 3,
358e5dd7070Spatrick kCFNumberSInt64Type = 4,
359e5dd7070Spatrick kCFNumberFloat32Type = 5,
360e5dd7070Spatrick kCFNumberFloat64Type = 6,
361e5dd7070Spatrick kCFNumberCharType = 7,
362e5dd7070Spatrick kCFNumberShortType = 8,
363e5dd7070Spatrick kCFNumberIntType = 9,
364e5dd7070Spatrick kCFNumberLongType = 10,
365e5dd7070Spatrick kCFNumberLongLongType = 11,
366e5dd7070Spatrick kCFNumberFloatType = 12,
367e5dd7070Spatrick kCFNumberDoubleType = 13,
368e5dd7070Spatrick kCFNumberCFIndexType = 14,
369e5dd7070Spatrick kCFNumberNSIntegerType = 15,
370e5dd7070Spatrick kCFNumberCGFloatType = 16
371e5dd7070Spatrick };
372e5dd7070Spatrick
GetCFNumberSize(ASTContext & Ctx,uint64_t i)373*12c85518Srobert static std::optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
374e5dd7070Spatrick static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
375e5dd7070Spatrick
376e5dd7070Spatrick if (i < kCFNumberCharType)
377e5dd7070Spatrick return FixedSize[i-1];
378e5dd7070Spatrick
379e5dd7070Spatrick QualType T;
380e5dd7070Spatrick
381e5dd7070Spatrick switch (i) {
382e5dd7070Spatrick case kCFNumberCharType: T = Ctx.CharTy; break;
383e5dd7070Spatrick case kCFNumberShortType: T = Ctx.ShortTy; break;
384e5dd7070Spatrick case kCFNumberIntType: T = Ctx.IntTy; break;
385e5dd7070Spatrick case kCFNumberLongType: T = Ctx.LongTy; break;
386e5dd7070Spatrick case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
387e5dd7070Spatrick case kCFNumberFloatType: T = Ctx.FloatTy; break;
388e5dd7070Spatrick case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
389e5dd7070Spatrick case kCFNumberCFIndexType:
390e5dd7070Spatrick case kCFNumberNSIntegerType:
391e5dd7070Spatrick case kCFNumberCGFloatType:
392e5dd7070Spatrick // FIXME: We need a way to map from names to Type*.
393e5dd7070Spatrick default:
394*12c85518Srobert return std::nullopt;
395e5dd7070Spatrick }
396e5dd7070Spatrick
397e5dd7070Spatrick return Ctx.getTypeSize(T);
398e5dd7070Spatrick }
399e5dd7070Spatrick
400e5dd7070Spatrick #if 0
401e5dd7070Spatrick static const char* GetCFNumberTypeStr(uint64_t i) {
402e5dd7070Spatrick static const char* Names[] = {
403e5dd7070Spatrick "kCFNumberSInt8Type",
404e5dd7070Spatrick "kCFNumberSInt16Type",
405e5dd7070Spatrick "kCFNumberSInt32Type",
406e5dd7070Spatrick "kCFNumberSInt64Type",
407e5dd7070Spatrick "kCFNumberFloat32Type",
408e5dd7070Spatrick "kCFNumberFloat64Type",
409e5dd7070Spatrick "kCFNumberCharType",
410e5dd7070Spatrick "kCFNumberShortType",
411e5dd7070Spatrick "kCFNumberIntType",
412e5dd7070Spatrick "kCFNumberLongType",
413e5dd7070Spatrick "kCFNumberLongLongType",
414e5dd7070Spatrick "kCFNumberFloatType",
415e5dd7070Spatrick "kCFNumberDoubleType",
416e5dd7070Spatrick "kCFNumberCFIndexType",
417e5dd7070Spatrick "kCFNumberNSIntegerType",
418e5dd7070Spatrick "kCFNumberCGFloatType"
419e5dd7070Spatrick };
420e5dd7070Spatrick
421e5dd7070Spatrick return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
422e5dd7070Spatrick }
423e5dd7070Spatrick #endif
424e5dd7070Spatrick
checkPreStmt(const CallExpr * CE,CheckerContext & C) const425e5dd7070Spatrick void CFNumberChecker::checkPreStmt(const CallExpr *CE,
426e5dd7070Spatrick CheckerContext &C) const {
427e5dd7070Spatrick ProgramStateRef state = C.getState();
428e5dd7070Spatrick const FunctionDecl *FD = C.getCalleeDecl(CE);
429e5dd7070Spatrick if (!FD)
430e5dd7070Spatrick return;
431e5dd7070Spatrick
432e5dd7070Spatrick ASTContext &Ctx = C.getASTContext();
433e5dd7070Spatrick if (!ICreate) {
434e5dd7070Spatrick ICreate = &Ctx.Idents.get("CFNumberCreate");
435e5dd7070Spatrick IGetValue = &Ctx.Idents.get("CFNumberGetValue");
436e5dd7070Spatrick }
437e5dd7070Spatrick if (!(FD->getIdentifier() == ICreate || FD->getIdentifier() == IGetValue) ||
438e5dd7070Spatrick CE->getNumArgs() != 3)
439e5dd7070Spatrick return;
440e5dd7070Spatrick
441e5dd7070Spatrick // Get the value of the "theType" argument.
442e5dd7070Spatrick SVal TheTypeVal = C.getSVal(CE->getArg(1));
443e5dd7070Spatrick
444e5dd7070Spatrick // FIXME: We really should allow ranges of valid theType values, and
445e5dd7070Spatrick // bifurcate the state appropriately.
446*12c85518Srobert std::optional<nonloc::ConcreteInt> V =
447*12c85518Srobert dyn_cast<nonloc::ConcreteInt>(TheTypeVal);
448e5dd7070Spatrick if (!V)
449e5dd7070Spatrick return;
450e5dd7070Spatrick
451e5dd7070Spatrick uint64_t NumberKind = V->getValue().getLimitedValue();
452*12c85518Srobert std::optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind);
453e5dd7070Spatrick
454e5dd7070Spatrick // FIXME: In some cases we can emit an error.
455e5dd7070Spatrick if (!OptCFNumberSize)
456e5dd7070Spatrick return;
457e5dd7070Spatrick
458e5dd7070Spatrick uint64_t CFNumberSize = *OptCFNumberSize;
459e5dd7070Spatrick
460e5dd7070Spatrick // Look at the value of the integer being passed by reference. Essentially
461e5dd7070Spatrick // we want to catch cases where the value passed in is not equal to the
462e5dd7070Spatrick // size of the type being created.
463e5dd7070Spatrick SVal TheValueExpr = C.getSVal(CE->getArg(2));
464e5dd7070Spatrick
465e5dd7070Spatrick // FIXME: Eventually we should handle arbitrary locations. We can do this
466e5dd7070Spatrick // by having an enhanced memory model that does low-level typing.
467*12c85518Srobert std::optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
468e5dd7070Spatrick if (!LV)
469e5dd7070Spatrick return;
470e5dd7070Spatrick
471e5dd7070Spatrick const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
472e5dd7070Spatrick if (!R)
473e5dd7070Spatrick return;
474e5dd7070Spatrick
475e5dd7070Spatrick QualType T = Ctx.getCanonicalType(R->getValueType());
476e5dd7070Spatrick
477e5dd7070Spatrick // FIXME: If the pointee isn't an integer type, should we flag a warning?
478e5dd7070Spatrick // People can do weird stuff with pointers.
479e5dd7070Spatrick
480e5dd7070Spatrick if (!T->isIntegralOrEnumerationType())
481e5dd7070Spatrick return;
482e5dd7070Spatrick
483e5dd7070Spatrick uint64_t PrimitiveTypeSize = Ctx.getTypeSize(T);
484e5dd7070Spatrick
485e5dd7070Spatrick if (PrimitiveTypeSize == CFNumberSize)
486e5dd7070Spatrick return;
487e5dd7070Spatrick
488e5dd7070Spatrick // FIXME: We can actually create an abstract "CFNumber" object that has
489e5dd7070Spatrick // the bits initialized to the provided values.
490e5dd7070Spatrick ExplodedNode *N = C.generateNonFatalErrorNode();
491e5dd7070Spatrick if (N) {
492e5dd7070Spatrick SmallString<128> sbuf;
493e5dd7070Spatrick llvm::raw_svector_ostream os(sbuf);
494e5dd7070Spatrick bool isCreate = (FD->getIdentifier() == ICreate);
495e5dd7070Spatrick
496e5dd7070Spatrick if (isCreate) {
497e5dd7070Spatrick os << (PrimitiveTypeSize == 8 ? "An " : "A ")
498e5dd7070Spatrick << PrimitiveTypeSize << "-bit integer is used to initialize a "
499e5dd7070Spatrick << "CFNumber object that represents "
500e5dd7070Spatrick << (CFNumberSize == 8 ? "an " : "a ")
501e5dd7070Spatrick << CFNumberSize << "-bit integer; ";
502e5dd7070Spatrick } else {
503e5dd7070Spatrick os << "A CFNumber object that represents "
504e5dd7070Spatrick << (CFNumberSize == 8 ? "an " : "a ")
505e5dd7070Spatrick << CFNumberSize << "-bit integer is used to initialize "
506e5dd7070Spatrick << (PrimitiveTypeSize == 8 ? "an " : "a ")
507e5dd7070Spatrick << PrimitiveTypeSize << "-bit integer; ";
508e5dd7070Spatrick }
509e5dd7070Spatrick
510e5dd7070Spatrick if (PrimitiveTypeSize < CFNumberSize)
511e5dd7070Spatrick os << (CFNumberSize - PrimitiveTypeSize)
512e5dd7070Spatrick << " bits of the CFNumber value will "
513e5dd7070Spatrick << (isCreate ? "be garbage." : "overwrite adjacent storage.");
514e5dd7070Spatrick else
515e5dd7070Spatrick os << (PrimitiveTypeSize - CFNumberSize)
516e5dd7070Spatrick << " bits of the integer value will be "
517e5dd7070Spatrick << (isCreate ? "lost." : "garbage.");
518e5dd7070Spatrick
519e5dd7070Spatrick if (!BT)
520e5dd7070Spatrick BT.reset(new APIMisuse(this, "Bad use of CFNumber APIs"));
521e5dd7070Spatrick
522e5dd7070Spatrick auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
523e5dd7070Spatrick report->addRange(CE->getArg(2)->getSourceRange());
524e5dd7070Spatrick C.emitReport(std::move(report));
525e5dd7070Spatrick }
526e5dd7070Spatrick }
527e5dd7070Spatrick
528e5dd7070Spatrick //===----------------------------------------------------------------------===//
529e5dd7070Spatrick // CFRetain/CFRelease/CFMakeCollectable/CFAutorelease checking for null arguments.
530e5dd7070Spatrick //===----------------------------------------------------------------------===//
531e5dd7070Spatrick
532e5dd7070Spatrick namespace {
533e5dd7070Spatrick class CFRetainReleaseChecker : public Checker<check::PreCall> {
534e5dd7070Spatrick mutable APIMisuse BT{this, "null passed to CF memory management function"};
535*12c85518Srobert const CallDescriptionSet ModelledCalls = {
536*12c85518Srobert {{"CFRetain"}, 1},
537*12c85518Srobert {{"CFRelease"}, 1},
538*12c85518Srobert {{"CFMakeCollectable"}, 1},
539*12c85518Srobert {{"CFAutorelease"}, 1},
540*12c85518Srobert };
541e5dd7070Spatrick
542e5dd7070Spatrick public:
543e5dd7070Spatrick void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
544e5dd7070Spatrick };
545e5dd7070Spatrick } // end anonymous namespace
546e5dd7070Spatrick
checkPreCall(const CallEvent & Call,CheckerContext & C) const547e5dd7070Spatrick void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call,
548e5dd7070Spatrick CheckerContext &C) const {
549e5dd7070Spatrick // TODO: Make this check part of CallDescription.
550e5dd7070Spatrick if (!Call.isGlobalCFunction())
551e5dd7070Spatrick return;
552e5dd7070Spatrick
553e5dd7070Spatrick // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease.
554*12c85518Srobert if (!ModelledCalls.contains(Call))
555e5dd7070Spatrick return;
556e5dd7070Spatrick
557e5dd7070Spatrick // Get the argument's value.
558e5dd7070Spatrick SVal ArgVal = Call.getArgSVal(0);
559*12c85518Srobert std::optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
560e5dd7070Spatrick if (!DefArgVal)
561e5dd7070Spatrick return;
562e5dd7070Spatrick
563e5dd7070Spatrick // Is it null?
564e5dd7070Spatrick ProgramStateRef state = C.getState();
565e5dd7070Spatrick ProgramStateRef stateNonNull, stateNull;
566e5dd7070Spatrick std::tie(stateNonNull, stateNull) = state->assume(*DefArgVal);
567e5dd7070Spatrick
568e5dd7070Spatrick if (!stateNonNull) {
569e5dd7070Spatrick ExplodedNode *N = C.generateErrorNode(stateNull);
570e5dd7070Spatrick if (!N)
571e5dd7070Spatrick return;
572e5dd7070Spatrick
573e5dd7070Spatrick SmallString<64> Str;
574e5dd7070Spatrick raw_svector_ostream OS(Str);
575e5dd7070Spatrick OS << "Null pointer argument in call to "
576e5dd7070Spatrick << cast<FunctionDecl>(Call.getDecl())->getName();
577e5dd7070Spatrick
578e5dd7070Spatrick auto report = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N);
579e5dd7070Spatrick report->addRange(Call.getArgSourceRange(0));
580e5dd7070Spatrick bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *report);
581e5dd7070Spatrick C.emitReport(std::move(report));
582e5dd7070Spatrick return;
583e5dd7070Spatrick }
584e5dd7070Spatrick
585e5dd7070Spatrick // From here on, we know the argument is non-null.
586e5dd7070Spatrick C.addTransition(stateNonNull);
587e5dd7070Spatrick }
588e5dd7070Spatrick
589e5dd7070Spatrick //===----------------------------------------------------------------------===//
590e5dd7070Spatrick // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
591e5dd7070Spatrick //===----------------------------------------------------------------------===//
592e5dd7070Spatrick
593e5dd7070Spatrick namespace {
594e5dd7070Spatrick class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
595e5dd7070Spatrick mutable Selector releaseS;
596e5dd7070Spatrick mutable Selector retainS;
597e5dd7070Spatrick mutable Selector autoreleaseS;
598e5dd7070Spatrick mutable Selector drainS;
599e5dd7070Spatrick mutable std::unique_ptr<BugType> BT;
600e5dd7070Spatrick
601e5dd7070Spatrick public:
602e5dd7070Spatrick void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
603e5dd7070Spatrick };
604e5dd7070Spatrick } // end anonymous namespace
605e5dd7070Spatrick
checkPreObjCMessage(const ObjCMethodCall & msg,CheckerContext & C) const606e5dd7070Spatrick void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
607e5dd7070Spatrick CheckerContext &C) const {
608e5dd7070Spatrick if (!BT) {
609e5dd7070Spatrick BT.reset(new APIMisuse(
610e5dd7070Spatrick this, "message incorrectly sent to class instead of class instance"));
611e5dd7070Spatrick
612e5dd7070Spatrick ASTContext &Ctx = C.getASTContext();
613e5dd7070Spatrick releaseS = GetNullarySelector("release", Ctx);
614e5dd7070Spatrick retainS = GetNullarySelector("retain", Ctx);
615e5dd7070Spatrick autoreleaseS = GetNullarySelector("autorelease", Ctx);
616e5dd7070Spatrick drainS = GetNullarySelector("drain", Ctx);
617e5dd7070Spatrick }
618e5dd7070Spatrick
619e5dd7070Spatrick if (msg.isInstanceMessage())
620e5dd7070Spatrick return;
621e5dd7070Spatrick const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
622e5dd7070Spatrick assert(Class);
623e5dd7070Spatrick
624e5dd7070Spatrick Selector S = msg.getSelector();
625e5dd7070Spatrick if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
626e5dd7070Spatrick return;
627e5dd7070Spatrick
628e5dd7070Spatrick if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
629e5dd7070Spatrick SmallString<200> buf;
630e5dd7070Spatrick llvm::raw_svector_ostream os(buf);
631e5dd7070Spatrick
632e5dd7070Spatrick os << "The '";
633e5dd7070Spatrick S.print(os);
634e5dd7070Spatrick os << "' message should be sent to instances "
635e5dd7070Spatrick "of class '" << Class->getName()
636e5dd7070Spatrick << "' and not the class directly";
637e5dd7070Spatrick
638e5dd7070Spatrick auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
639e5dd7070Spatrick report->addRange(msg.getSourceRange());
640e5dd7070Spatrick C.emitReport(std::move(report));
641e5dd7070Spatrick }
642e5dd7070Spatrick }
643e5dd7070Spatrick
644e5dd7070Spatrick //===----------------------------------------------------------------------===//
645e5dd7070Spatrick // Check for passing non-Objective-C types to variadic methods that expect
646e5dd7070Spatrick // only Objective-C types.
647e5dd7070Spatrick //===----------------------------------------------------------------------===//
648e5dd7070Spatrick
649e5dd7070Spatrick namespace {
650e5dd7070Spatrick class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
651e5dd7070Spatrick mutable Selector arrayWithObjectsS;
652e5dd7070Spatrick mutable Selector dictionaryWithObjectsAndKeysS;
653e5dd7070Spatrick mutable Selector setWithObjectsS;
654e5dd7070Spatrick mutable Selector orderedSetWithObjectsS;
655e5dd7070Spatrick mutable Selector initWithObjectsS;
656e5dd7070Spatrick mutable Selector initWithObjectsAndKeysS;
657e5dd7070Spatrick mutable std::unique_ptr<BugType> BT;
658e5dd7070Spatrick
659e5dd7070Spatrick bool isVariadicMessage(const ObjCMethodCall &msg) const;
660e5dd7070Spatrick
661e5dd7070Spatrick public:
662e5dd7070Spatrick void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
663e5dd7070Spatrick };
664e5dd7070Spatrick } // end anonymous namespace
665e5dd7070Spatrick
666e5dd7070Spatrick /// isVariadicMessage - Returns whether the given message is a variadic message,
667e5dd7070Spatrick /// where all arguments must be Objective-C types.
668e5dd7070Spatrick bool
isVariadicMessage(const ObjCMethodCall & msg) const669e5dd7070Spatrick VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
670e5dd7070Spatrick const ObjCMethodDecl *MD = msg.getDecl();
671e5dd7070Spatrick
672e5dd7070Spatrick if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
673e5dd7070Spatrick return false;
674e5dd7070Spatrick
675e5dd7070Spatrick Selector S = msg.getSelector();
676e5dd7070Spatrick
677e5dd7070Spatrick if (msg.isInstanceMessage()) {
678e5dd7070Spatrick // FIXME: Ideally we'd look at the receiver interface here, but that's not
679e5dd7070Spatrick // useful for init, because alloc returns 'id'. In theory, this could lead
680e5dd7070Spatrick // to false positives, for example if there existed a class that had an
681e5dd7070Spatrick // initWithObjects: implementation that does accept non-Objective-C pointer
682e5dd7070Spatrick // types, but the chance of that happening is pretty small compared to the
683e5dd7070Spatrick // gains that this analysis gives.
684e5dd7070Spatrick const ObjCInterfaceDecl *Class = MD->getClassInterface();
685e5dd7070Spatrick
686e5dd7070Spatrick switch (findKnownClass(Class)) {
687e5dd7070Spatrick case FC_NSArray:
688e5dd7070Spatrick case FC_NSOrderedSet:
689e5dd7070Spatrick case FC_NSSet:
690e5dd7070Spatrick return S == initWithObjectsS;
691e5dd7070Spatrick case FC_NSDictionary:
692e5dd7070Spatrick return S == initWithObjectsAndKeysS;
693e5dd7070Spatrick default:
694e5dd7070Spatrick return false;
695e5dd7070Spatrick }
696e5dd7070Spatrick } else {
697e5dd7070Spatrick const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
698e5dd7070Spatrick
699e5dd7070Spatrick switch (findKnownClass(Class)) {
700e5dd7070Spatrick case FC_NSArray:
701e5dd7070Spatrick return S == arrayWithObjectsS;
702e5dd7070Spatrick case FC_NSOrderedSet:
703e5dd7070Spatrick return S == orderedSetWithObjectsS;
704e5dd7070Spatrick case FC_NSSet:
705e5dd7070Spatrick return S == setWithObjectsS;
706e5dd7070Spatrick case FC_NSDictionary:
707e5dd7070Spatrick return S == dictionaryWithObjectsAndKeysS;
708e5dd7070Spatrick default:
709e5dd7070Spatrick return false;
710e5dd7070Spatrick }
711e5dd7070Spatrick }
712e5dd7070Spatrick }
713e5dd7070Spatrick
checkPreObjCMessage(const ObjCMethodCall & msg,CheckerContext & C) const714e5dd7070Spatrick void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
715e5dd7070Spatrick CheckerContext &C) const {
716e5dd7070Spatrick if (!BT) {
717e5dd7070Spatrick BT.reset(new APIMisuse(this,
718e5dd7070Spatrick "Arguments passed to variadic method aren't all "
719e5dd7070Spatrick "Objective-C pointer types"));
720e5dd7070Spatrick
721e5dd7070Spatrick ASTContext &Ctx = C.getASTContext();
722e5dd7070Spatrick arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
723e5dd7070Spatrick dictionaryWithObjectsAndKeysS =
724e5dd7070Spatrick GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
725e5dd7070Spatrick setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
726e5dd7070Spatrick orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
727e5dd7070Spatrick
728e5dd7070Spatrick initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
729e5dd7070Spatrick initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
730e5dd7070Spatrick }
731e5dd7070Spatrick
732e5dd7070Spatrick if (!isVariadicMessage(msg))
733e5dd7070Spatrick return;
734e5dd7070Spatrick
735e5dd7070Spatrick // We are not interested in the selector arguments since they have
736e5dd7070Spatrick // well-defined types, so the compiler will issue a warning for them.
737e5dd7070Spatrick unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
738e5dd7070Spatrick
739e5dd7070Spatrick // We're not interested in the last argument since it has to be nil or the
740e5dd7070Spatrick // compiler would have issued a warning for it elsewhere.
741e5dd7070Spatrick unsigned variadicArgsEnd = msg.getNumArgs() - 1;
742e5dd7070Spatrick
743e5dd7070Spatrick if (variadicArgsEnd <= variadicArgsBegin)
744e5dd7070Spatrick return;
745e5dd7070Spatrick
746e5dd7070Spatrick // Verify that all arguments have Objective-C types.
747*12c85518Srobert std::optional<ExplodedNode *> errorNode;
748e5dd7070Spatrick
749e5dd7070Spatrick for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
750e5dd7070Spatrick QualType ArgTy = msg.getArgExpr(I)->getType();
751e5dd7070Spatrick if (ArgTy->isObjCObjectPointerType())
752e5dd7070Spatrick continue;
753e5dd7070Spatrick
754e5dd7070Spatrick // Block pointers are treaded as Objective-C pointers.
755e5dd7070Spatrick if (ArgTy->isBlockPointerType())
756e5dd7070Spatrick continue;
757e5dd7070Spatrick
758e5dd7070Spatrick // Ignore pointer constants.
759*12c85518Srobert if (isa<loc::ConcreteInt>(msg.getArgSVal(I)))
760e5dd7070Spatrick continue;
761e5dd7070Spatrick
762e5dd7070Spatrick // Ignore pointer types annotated with 'NSObject' attribute.
763e5dd7070Spatrick if (C.getASTContext().isObjCNSObjectType(ArgTy))
764e5dd7070Spatrick continue;
765e5dd7070Spatrick
766e5dd7070Spatrick // Ignore CF references, which can be toll-free bridged.
767e5dd7070Spatrick if (coreFoundation::isCFObjectRef(ArgTy))
768e5dd7070Spatrick continue;
769e5dd7070Spatrick
770e5dd7070Spatrick // Generate only one error node to use for all bug reports.
771*12c85518Srobert if (!errorNode)
772e5dd7070Spatrick errorNode = C.generateNonFatalErrorNode();
773e5dd7070Spatrick
774*12c85518Srobert if (!*errorNode)
775e5dd7070Spatrick continue;
776e5dd7070Spatrick
777e5dd7070Spatrick SmallString<128> sbuf;
778e5dd7070Spatrick llvm::raw_svector_ostream os(sbuf);
779e5dd7070Spatrick
780e5dd7070Spatrick StringRef TypeName = GetReceiverInterfaceName(msg);
781e5dd7070Spatrick if (!TypeName.empty())
782e5dd7070Spatrick os << "Argument to '" << TypeName << "' method '";
783e5dd7070Spatrick else
784e5dd7070Spatrick os << "Argument to method '";
785e5dd7070Spatrick
786e5dd7070Spatrick msg.getSelector().print(os);
787e5dd7070Spatrick os << "' should be an Objective-C pointer type, not '";
788e5dd7070Spatrick ArgTy.print(os, C.getLangOpts());
789e5dd7070Spatrick os << "'";
790e5dd7070Spatrick
791*12c85518Srobert auto R =
792*12c85518Srobert std::make_unique<PathSensitiveBugReport>(*BT, os.str(), *errorNode);
793e5dd7070Spatrick R->addRange(msg.getArgSourceRange(I));
794e5dd7070Spatrick C.emitReport(std::move(R));
795e5dd7070Spatrick }
796e5dd7070Spatrick }
797e5dd7070Spatrick
798e5dd7070Spatrick //===----------------------------------------------------------------------===//
799e5dd7070Spatrick // Improves the modeling of loops over Cocoa collections.
800e5dd7070Spatrick //===----------------------------------------------------------------------===//
801e5dd7070Spatrick
802e5dd7070Spatrick // The map from container symbol to the container count symbol.
803e5dd7070Spatrick // We currently will remember the last container count symbol encountered.
804e5dd7070Spatrick REGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap, SymbolRef, SymbolRef)
805e5dd7070Spatrick REGISTER_MAP_WITH_PROGRAMSTATE(ContainerNonEmptyMap, SymbolRef, bool)
806e5dd7070Spatrick
807e5dd7070Spatrick namespace {
808e5dd7070Spatrick class ObjCLoopChecker
809e5dd7070Spatrick : public Checker<check::PostStmt<ObjCForCollectionStmt>,
810e5dd7070Spatrick check::PostObjCMessage,
811e5dd7070Spatrick check::DeadSymbols,
812e5dd7070Spatrick check::PointerEscape > {
813e5dd7070Spatrick mutable IdentifierInfo *CountSelectorII;
814e5dd7070Spatrick
815e5dd7070Spatrick bool isCollectionCountMethod(const ObjCMethodCall &M,
816e5dd7070Spatrick CheckerContext &C) const;
817e5dd7070Spatrick
818e5dd7070Spatrick public:
ObjCLoopChecker()819e5dd7070Spatrick ObjCLoopChecker() : CountSelectorII(nullptr) {}
820e5dd7070Spatrick void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
821e5dd7070Spatrick void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
822e5dd7070Spatrick void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
823e5dd7070Spatrick ProgramStateRef checkPointerEscape(ProgramStateRef State,
824e5dd7070Spatrick const InvalidatedSymbols &Escaped,
825e5dd7070Spatrick const CallEvent *Call,
826e5dd7070Spatrick PointerEscapeKind Kind) const;
827e5dd7070Spatrick };
828e5dd7070Spatrick } // end anonymous namespace
829e5dd7070Spatrick
isKnownNonNilCollectionType(QualType T)830e5dd7070Spatrick static bool isKnownNonNilCollectionType(QualType T) {
831e5dd7070Spatrick const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
832e5dd7070Spatrick if (!PT)
833e5dd7070Spatrick return false;
834e5dd7070Spatrick
835e5dd7070Spatrick const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
836e5dd7070Spatrick if (!ID)
837e5dd7070Spatrick return false;
838e5dd7070Spatrick
839e5dd7070Spatrick switch (findKnownClass(ID)) {
840e5dd7070Spatrick case FC_NSArray:
841e5dd7070Spatrick case FC_NSDictionary:
842e5dd7070Spatrick case FC_NSEnumerator:
843e5dd7070Spatrick case FC_NSOrderedSet:
844e5dd7070Spatrick case FC_NSSet:
845e5dd7070Spatrick return true;
846e5dd7070Spatrick default:
847e5dd7070Spatrick return false;
848e5dd7070Spatrick }
849e5dd7070Spatrick }
850e5dd7070Spatrick
851e5dd7070Spatrick /// Assumes that the collection is non-nil.
852e5dd7070Spatrick ///
853e5dd7070Spatrick /// If the collection is known to be nil, returns NULL to indicate an infeasible
854e5dd7070Spatrick /// path.
checkCollectionNonNil(CheckerContext & C,ProgramStateRef State,const ObjCForCollectionStmt * FCS)855e5dd7070Spatrick static ProgramStateRef checkCollectionNonNil(CheckerContext &C,
856e5dd7070Spatrick ProgramStateRef State,
857e5dd7070Spatrick const ObjCForCollectionStmt *FCS) {
858e5dd7070Spatrick if (!State)
859e5dd7070Spatrick return nullptr;
860e5dd7070Spatrick
861e5dd7070Spatrick SVal CollectionVal = C.getSVal(FCS->getCollection());
862*12c85518Srobert std::optional<DefinedSVal> KnownCollection =
863*12c85518Srobert CollectionVal.getAs<DefinedSVal>();
864e5dd7070Spatrick if (!KnownCollection)
865e5dd7070Spatrick return State;
866e5dd7070Spatrick
867e5dd7070Spatrick ProgramStateRef StNonNil, StNil;
868e5dd7070Spatrick std::tie(StNonNil, StNil) = State->assume(*KnownCollection);
869e5dd7070Spatrick if (StNil && !StNonNil) {
870e5dd7070Spatrick // The collection is nil. This path is infeasible.
871e5dd7070Spatrick return nullptr;
872e5dd7070Spatrick }
873e5dd7070Spatrick
874e5dd7070Spatrick return StNonNil;
875e5dd7070Spatrick }
876e5dd7070Spatrick
877e5dd7070Spatrick /// Assumes that the collection elements are non-nil.
878e5dd7070Spatrick ///
879e5dd7070Spatrick /// This only applies if the collection is one of those known not to contain
880e5dd7070Spatrick /// nil values.
checkElementNonNil(CheckerContext & C,ProgramStateRef State,const ObjCForCollectionStmt * FCS)881e5dd7070Spatrick static ProgramStateRef checkElementNonNil(CheckerContext &C,
882e5dd7070Spatrick ProgramStateRef State,
883e5dd7070Spatrick const ObjCForCollectionStmt *FCS) {
884e5dd7070Spatrick if (!State)
885e5dd7070Spatrick return nullptr;
886e5dd7070Spatrick
887e5dd7070Spatrick // See if the collection is one where we /know/ the elements are non-nil.
888e5dd7070Spatrick if (!isKnownNonNilCollectionType(FCS->getCollection()->getType()))
889e5dd7070Spatrick return State;
890e5dd7070Spatrick
891e5dd7070Spatrick const LocationContext *LCtx = C.getLocationContext();
892e5dd7070Spatrick const Stmt *Element = FCS->getElement();
893e5dd7070Spatrick
894e5dd7070Spatrick // FIXME: Copied from ExprEngineObjC.
895*12c85518Srobert std::optional<Loc> ElementLoc;
896e5dd7070Spatrick if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
897e5dd7070Spatrick const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
898e5dd7070Spatrick assert(ElemDecl->getInit() == nullptr);
899e5dd7070Spatrick ElementLoc = State->getLValue(ElemDecl, LCtx);
900e5dd7070Spatrick } else {
901e5dd7070Spatrick ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>();
902e5dd7070Spatrick }
903e5dd7070Spatrick
904e5dd7070Spatrick if (!ElementLoc)
905e5dd7070Spatrick return State;
906e5dd7070Spatrick
907e5dd7070Spatrick // Go ahead and assume the value is non-nil.
908e5dd7070Spatrick SVal Val = State->getSVal(*ElementLoc);
909*12c85518Srobert return State->assume(cast<DefinedOrUnknownSVal>(Val), true);
910e5dd7070Spatrick }
911e5dd7070Spatrick
912e5dd7070Spatrick /// Returns NULL state if the collection is known to contain elements
913e5dd7070Spatrick /// (or is known not to contain elements if the Assumption parameter is false.)
914e5dd7070Spatrick static ProgramStateRef
assumeCollectionNonEmpty(CheckerContext & C,ProgramStateRef State,SymbolRef CollectionS,bool Assumption)915e5dd7070Spatrick assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
916e5dd7070Spatrick SymbolRef CollectionS, bool Assumption) {
917e5dd7070Spatrick if (!State || !CollectionS)
918e5dd7070Spatrick return State;
919e5dd7070Spatrick
920e5dd7070Spatrick const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS);
921e5dd7070Spatrick if (!CountS) {
922e5dd7070Spatrick const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS);
923e5dd7070Spatrick if (!KnownNonEmpty)
924e5dd7070Spatrick return State->set<ContainerNonEmptyMap>(CollectionS, Assumption);
925e5dd7070Spatrick return (Assumption == *KnownNonEmpty) ? State : nullptr;
926e5dd7070Spatrick }
927e5dd7070Spatrick
928e5dd7070Spatrick SValBuilder &SvalBuilder = C.getSValBuilder();
929e5dd7070Spatrick SVal CountGreaterThanZeroVal =
930e5dd7070Spatrick SvalBuilder.evalBinOp(State, BO_GT,
931e5dd7070Spatrick nonloc::SymbolVal(*CountS),
932e5dd7070Spatrick SvalBuilder.makeIntVal(0, (*CountS)->getType()),
933e5dd7070Spatrick SvalBuilder.getConditionType());
934*12c85518Srobert std::optional<DefinedSVal> CountGreaterThanZero =
935e5dd7070Spatrick CountGreaterThanZeroVal.getAs<DefinedSVal>();
936e5dd7070Spatrick if (!CountGreaterThanZero) {
937e5dd7070Spatrick // The SValBuilder cannot construct a valid SVal for this condition.
938e5dd7070Spatrick // This means we cannot properly reason about it.
939e5dd7070Spatrick return State;
940e5dd7070Spatrick }
941e5dd7070Spatrick
942e5dd7070Spatrick return State->assume(*CountGreaterThanZero, Assumption);
943e5dd7070Spatrick }
944e5dd7070Spatrick
945e5dd7070Spatrick static ProgramStateRef
assumeCollectionNonEmpty(CheckerContext & C,ProgramStateRef State,const ObjCForCollectionStmt * FCS,bool Assumption)946e5dd7070Spatrick assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
947e5dd7070Spatrick const ObjCForCollectionStmt *FCS,
948e5dd7070Spatrick bool Assumption) {
949e5dd7070Spatrick if (!State)
950e5dd7070Spatrick return nullptr;
951e5dd7070Spatrick
952e5dd7070Spatrick SymbolRef CollectionS = C.getSVal(FCS->getCollection()).getAsSymbol();
953e5dd7070Spatrick return assumeCollectionNonEmpty(C, State, CollectionS, Assumption);
954e5dd7070Spatrick }
955e5dd7070Spatrick
956e5dd7070Spatrick /// If the fist block edge is a back edge, we are reentering the loop.
alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode * N,const ObjCForCollectionStmt * FCS)957e5dd7070Spatrick static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N,
958e5dd7070Spatrick const ObjCForCollectionStmt *FCS) {
959e5dd7070Spatrick if (!N)
960e5dd7070Spatrick return false;
961e5dd7070Spatrick
962e5dd7070Spatrick ProgramPoint P = N->getLocation();
963*12c85518Srobert if (std::optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
964e5dd7070Spatrick return BE->getSrc()->getLoopTarget() == FCS;
965e5dd7070Spatrick }
966e5dd7070Spatrick
967e5dd7070Spatrick // Keep looking for a block edge.
968e5dd7070Spatrick for (ExplodedNode::const_pred_iterator I = N->pred_begin(),
969e5dd7070Spatrick E = N->pred_end(); I != E; ++I) {
970e5dd7070Spatrick if (alreadyExecutedAtLeastOneLoopIteration(*I, FCS))
971e5dd7070Spatrick return true;
972e5dd7070Spatrick }
973e5dd7070Spatrick
974e5dd7070Spatrick return false;
975e5dd7070Spatrick }
976e5dd7070Spatrick
checkPostStmt(const ObjCForCollectionStmt * FCS,CheckerContext & C) const977e5dd7070Spatrick void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
978e5dd7070Spatrick CheckerContext &C) const {
979e5dd7070Spatrick ProgramStateRef State = C.getState();
980e5dd7070Spatrick
981e5dd7070Spatrick // Check if this is the branch for the end of the loop.
982a9ac8606Spatrick if (!ExprEngine::hasMoreIteration(State, FCS, C.getLocationContext())) {
983e5dd7070Spatrick if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS))
984e5dd7070Spatrick State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false);
985e5dd7070Spatrick
986e5dd7070Spatrick // Otherwise, this is a branch that goes through the loop body.
987e5dd7070Spatrick } else {
988e5dd7070Spatrick State = checkCollectionNonNil(C, State, FCS);
989e5dd7070Spatrick State = checkElementNonNil(C, State, FCS);
990e5dd7070Spatrick State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true);
991e5dd7070Spatrick }
992e5dd7070Spatrick
993e5dd7070Spatrick if (!State)
994e5dd7070Spatrick C.generateSink(C.getState(), C.getPredecessor());
995e5dd7070Spatrick else if (State != C.getState())
996e5dd7070Spatrick C.addTransition(State);
997e5dd7070Spatrick }
998e5dd7070Spatrick
isCollectionCountMethod(const ObjCMethodCall & M,CheckerContext & C) const999e5dd7070Spatrick bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M,
1000e5dd7070Spatrick CheckerContext &C) const {
1001e5dd7070Spatrick Selector S = M.getSelector();
1002e5dd7070Spatrick // Initialize the identifiers on first use.
1003e5dd7070Spatrick if (!CountSelectorII)
1004e5dd7070Spatrick CountSelectorII = &C.getASTContext().Idents.get("count");
1005e5dd7070Spatrick
1006e5dd7070Spatrick // If the method returns collection count, record the value.
1007e5dd7070Spatrick return S.isUnarySelector() &&
1008e5dd7070Spatrick (S.getIdentifierInfoForSlot(0) == CountSelectorII);
1009e5dd7070Spatrick }
1010e5dd7070Spatrick
checkPostObjCMessage(const ObjCMethodCall & M,CheckerContext & C) const1011e5dd7070Spatrick void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M,
1012e5dd7070Spatrick CheckerContext &C) const {
1013e5dd7070Spatrick if (!M.isInstanceMessage())
1014e5dd7070Spatrick return;
1015e5dd7070Spatrick
1016e5dd7070Spatrick const ObjCInterfaceDecl *ClassID = M.getReceiverInterface();
1017e5dd7070Spatrick if (!ClassID)
1018e5dd7070Spatrick return;
1019e5dd7070Spatrick
1020e5dd7070Spatrick FoundationClass Class = findKnownClass(ClassID);
1021e5dd7070Spatrick if (Class != FC_NSDictionary &&
1022e5dd7070Spatrick Class != FC_NSArray &&
1023e5dd7070Spatrick Class != FC_NSSet &&
1024e5dd7070Spatrick Class != FC_NSOrderedSet)
1025e5dd7070Spatrick return;
1026e5dd7070Spatrick
1027e5dd7070Spatrick SymbolRef ContainerS = M.getReceiverSVal().getAsSymbol();
1028e5dd7070Spatrick if (!ContainerS)
1029e5dd7070Spatrick return;
1030e5dd7070Spatrick
1031e5dd7070Spatrick // If we are processing a call to "count", get the symbolic value returned by
1032e5dd7070Spatrick // a call to "count" and add it to the map.
1033e5dd7070Spatrick if (!isCollectionCountMethod(M, C))
1034e5dd7070Spatrick return;
1035e5dd7070Spatrick
1036e5dd7070Spatrick const Expr *MsgExpr = M.getOriginExpr();
1037e5dd7070Spatrick SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol();
1038e5dd7070Spatrick if (CountS) {
1039e5dd7070Spatrick ProgramStateRef State = C.getState();
1040e5dd7070Spatrick
1041e5dd7070Spatrick C.getSymbolManager().addSymbolDependency(ContainerS, CountS);
1042e5dd7070Spatrick State = State->set<ContainerCountMap>(ContainerS, CountS);
1043e5dd7070Spatrick
1044e5dd7070Spatrick if (const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) {
1045e5dd7070Spatrick State = State->remove<ContainerNonEmptyMap>(ContainerS);
1046e5dd7070Spatrick State = assumeCollectionNonEmpty(C, State, ContainerS, *NonEmpty);
1047e5dd7070Spatrick }
1048e5dd7070Spatrick
1049e5dd7070Spatrick C.addTransition(State);
1050e5dd7070Spatrick }
1051e5dd7070Spatrick }
1052e5dd7070Spatrick
getMethodReceiverIfKnownImmutable(const CallEvent * Call)1053e5dd7070Spatrick static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call) {
1054e5dd7070Spatrick const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call);
1055e5dd7070Spatrick if (!Message)
1056e5dd7070Spatrick return nullptr;
1057e5dd7070Spatrick
1058e5dd7070Spatrick const ObjCMethodDecl *MD = Message->getDecl();
1059e5dd7070Spatrick if (!MD)
1060e5dd7070Spatrick return nullptr;
1061e5dd7070Spatrick
1062e5dd7070Spatrick const ObjCInterfaceDecl *StaticClass;
1063e5dd7070Spatrick if (isa<ObjCProtocolDecl>(MD->getDeclContext())) {
1064e5dd7070Spatrick // We can't find out where the method was declared without doing more work.
1065e5dd7070Spatrick // Instead, see if the receiver is statically typed as a known immutable
1066e5dd7070Spatrick // collection.
1067e5dd7070Spatrick StaticClass = Message->getOriginExpr()->getReceiverInterface();
1068e5dd7070Spatrick } else {
1069e5dd7070Spatrick StaticClass = MD->getClassInterface();
1070e5dd7070Spatrick }
1071e5dd7070Spatrick
1072e5dd7070Spatrick if (!StaticClass)
1073e5dd7070Spatrick return nullptr;
1074e5dd7070Spatrick
1075e5dd7070Spatrick switch (findKnownClass(StaticClass, /*IncludeSuper=*/false)) {
1076e5dd7070Spatrick case FC_None:
1077e5dd7070Spatrick return nullptr;
1078e5dd7070Spatrick case FC_NSArray:
1079e5dd7070Spatrick case FC_NSDictionary:
1080e5dd7070Spatrick case FC_NSEnumerator:
1081e5dd7070Spatrick case FC_NSNull:
1082e5dd7070Spatrick case FC_NSOrderedSet:
1083e5dd7070Spatrick case FC_NSSet:
1084e5dd7070Spatrick case FC_NSString:
1085e5dd7070Spatrick break;
1086e5dd7070Spatrick }
1087e5dd7070Spatrick
1088e5dd7070Spatrick return Message->getReceiverSVal().getAsSymbol();
1089e5dd7070Spatrick }
1090e5dd7070Spatrick
1091e5dd7070Spatrick ProgramStateRef
checkPointerEscape(ProgramStateRef State,const InvalidatedSymbols & Escaped,const CallEvent * Call,PointerEscapeKind Kind) const1092e5dd7070Spatrick ObjCLoopChecker::checkPointerEscape(ProgramStateRef State,
1093e5dd7070Spatrick const InvalidatedSymbols &Escaped,
1094e5dd7070Spatrick const CallEvent *Call,
1095e5dd7070Spatrick PointerEscapeKind Kind) const {
1096e5dd7070Spatrick SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call);
1097e5dd7070Spatrick
1098*12c85518Srobert // Remove the invalidated symbols from the collection count map.
1099e5dd7070Spatrick for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
1100e5dd7070Spatrick E = Escaped.end();
1101e5dd7070Spatrick I != E; ++I) {
1102e5dd7070Spatrick SymbolRef Sym = *I;
1103e5dd7070Spatrick
1104e5dd7070Spatrick // Don't invalidate this symbol's count if we know the method being called
1105e5dd7070Spatrick // is declared on an immutable class. This isn't completely correct if the
1106e5dd7070Spatrick // receiver is also passed as an argument, but in most uses of NSArray,
1107e5dd7070Spatrick // NSDictionary, etc. this isn't likely to happen in a dangerous way.
1108e5dd7070Spatrick if (Sym == ImmutableReceiver)
1109e5dd7070Spatrick continue;
1110e5dd7070Spatrick
1111e5dd7070Spatrick // The symbol escaped. Pessimistically, assume that the count could have
1112e5dd7070Spatrick // changed.
1113e5dd7070Spatrick State = State->remove<ContainerCountMap>(Sym);
1114e5dd7070Spatrick State = State->remove<ContainerNonEmptyMap>(Sym);
1115e5dd7070Spatrick }
1116e5dd7070Spatrick return State;
1117e5dd7070Spatrick }
1118e5dd7070Spatrick
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const1119e5dd7070Spatrick void ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1120e5dd7070Spatrick CheckerContext &C) const {
1121e5dd7070Spatrick ProgramStateRef State = C.getState();
1122e5dd7070Spatrick
1123e5dd7070Spatrick // Remove the dead symbols from the collection count map.
1124e5dd7070Spatrick ContainerCountMapTy Tracked = State->get<ContainerCountMap>();
1125e5dd7070Spatrick for (ContainerCountMapTy::iterator I = Tracked.begin(),
1126e5dd7070Spatrick E = Tracked.end(); I != E; ++I) {
1127e5dd7070Spatrick SymbolRef Sym = I->first;
1128e5dd7070Spatrick if (SymReaper.isDead(Sym)) {
1129e5dd7070Spatrick State = State->remove<ContainerCountMap>(Sym);
1130e5dd7070Spatrick State = State->remove<ContainerNonEmptyMap>(Sym);
1131e5dd7070Spatrick }
1132e5dd7070Spatrick }
1133e5dd7070Spatrick
1134e5dd7070Spatrick C.addTransition(State);
1135e5dd7070Spatrick }
1136e5dd7070Spatrick
1137e5dd7070Spatrick namespace {
1138e5dd7070Spatrick /// \class ObjCNonNilReturnValueChecker
1139e5dd7070Spatrick /// The checker restricts the return values of APIs known to
1140e5dd7070Spatrick /// never (or almost never) return 'nil'.
1141e5dd7070Spatrick class ObjCNonNilReturnValueChecker
1142e5dd7070Spatrick : public Checker<check::PostObjCMessage,
1143e5dd7070Spatrick check::PostStmt<ObjCArrayLiteral>,
1144e5dd7070Spatrick check::PostStmt<ObjCDictionaryLiteral>,
1145e5dd7070Spatrick check::PostStmt<ObjCBoxedExpr> > {
1146e5dd7070Spatrick mutable bool Initialized;
1147e5dd7070Spatrick mutable Selector ObjectAtIndex;
1148e5dd7070Spatrick mutable Selector ObjectAtIndexedSubscript;
1149e5dd7070Spatrick mutable Selector NullSelector;
1150e5dd7070Spatrick
1151e5dd7070Spatrick public:
ObjCNonNilReturnValueChecker()1152e5dd7070Spatrick ObjCNonNilReturnValueChecker() : Initialized(false) {}
1153e5dd7070Spatrick
1154e5dd7070Spatrick ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
1155e5dd7070Spatrick ProgramStateRef State,
1156e5dd7070Spatrick CheckerContext &C) const;
assumeExprIsNonNull(const Expr * E,CheckerContext & C) const1157e5dd7070Spatrick void assumeExprIsNonNull(const Expr *E, CheckerContext &C) const {
1158e5dd7070Spatrick C.addTransition(assumeExprIsNonNull(E, C.getState(), C));
1159e5dd7070Spatrick }
1160e5dd7070Spatrick
checkPostStmt(const ObjCArrayLiteral * E,CheckerContext & C) const1161e5dd7070Spatrick void checkPostStmt(const ObjCArrayLiteral *E, CheckerContext &C) const {
1162e5dd7070Spatrick assumeExprIsNonNull(E, C);
1163e5dd7070Spatrick }
checkPostStmt(const ObjCDictionaryLiteral * E,CheckerContext & C) const1164e5dd7070Spatrick void checkPostStmt(const ObjCDictionaryLiteral *E, CheckerContext &C) const {
1165e5dd7070Spatrick assumeExprIsNonNull(E, C);
1166e5dd7070Spatrick }
checkPostStmt(const ObjCBoxedExpr * E,CheckerContext & C) const1167e5dd7070Spatrick void checkPostStmt(const ObjCBoxedExpr *E, CheckerContext &C) const {
1168e5dd7070Spatrick assumeExprIsNonNull(E, C);
1169e5dd7070Spatrick }
1170e5dd7070Spatrick
1171e5dd7070Spatrick void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
1172e5dd7070Spatrick };
1173e5dd7070Spatrick } // end anonymous namespace
1174e5dd7070Spatrick
1175e5dd7070Spatrick ProgramStateRef
assumeExprIsNonNull(const Expr * NonNullExpr,ProgramStateRef State,CheckerContext & C) const1176e5dd7070Spatrick ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr,
1177e5dd7070Spatrick ProgramStateRef State,
1178e5dd7070Spatrick CheckerContext &C) const {
1179e5dd7070Spatrick SVal Val = C.getSVal(NonNullExpr);
1180*12c85518Srobert if (std::optional<DefinedOrUnknownSVal> DV =
1181*12c85518Srobert Val.getAs<DefinedOrUnknownSVal>())
1182e5dd7070Spatrick return State->assume(*DV, true);
1183e5dd7070Spatrick return State;
1184e5dd7070Spatrick }
1185e5dd7070Spatrick
checkPostObjCMessage(const ObjCMethodCall & M,CheckerContext & C) const1186e5dd7070Spatrick void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
1187e5dd7070Spatrick CheckerContext &C)
1188e5dd7070Spatrick const {
1189e5dd7070Spatrick ProgramStateRef State = C.getState();
1190e5dd7070Spatrick
1191e5dd7070Spatrick if (!Initialized) {
1192e5dd7070Spatrick ASTContext &Ctx = C.getASTContext();
1193e5dd7070Spatrick ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
1194e5dd7070Spatrick ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
1195e5dd7070Spatrick NullSelector = GetNullarySelector("null", Ctx);
1196e5dd7070Spatrick }
1197e5dd7070Spatrick
1198e5dd7070Spatrick // Check the receiver type.
1199e5dd7070Spatrick if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
1200e5dd7070Spatrick
1201e5dd7070Spatrick // Assume that object returned from '[self init]' or '[super init]' is not
1202e5dd7070Spatrick // 'nil' if we are processing an inlined function/method.
1203e5dd7070Spatrick //
1204e5dd7070Spatrick // A defensive callee will (and should) check if the object returned by
1205e5dd7070Spatrick // '[super init]' is 'nil' before doing it's own initialization. However,
1206e5dd7070Spatrick // since 'nil' is rarely returned in practice, we should not warn when the
1207e5dd7070Spatrick // caller to the defensive constructor uses the object in contexts where
1208e5dd7070Spatrick // 'nil' is not accepted.
1209e5dd7070Spatrick if (!C.inTopFrame() && M.getDecl() &&
1210e5dd7070Spatrick M.getDecl()->getMethodFamily() == OMF_init &&
1211e5dd7070Spatrick M.isReceiverSelfOrSuper()) {
1212e5dd7070Spatrick State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1213e5dd7070Spatrick }
1214e5dd7070Spatrick
1215e5dd7070Spatrick FoundationClass Cl = findKnownClass(Interface);
1216e5dd7070Spatrick
1217e5dd7070Spatrick // Objects returned from
1218e5dd7070Spatrick // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
1219e5dd7070Spatrick // are never 'nil'.
1220e5dd7070Spatrick if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) {
1221e5dd7070Spatrick Selector Sel = M.getSelector();
1222e5dd7070Spatrick if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
1223e5dd7070Spatrick // Go ahead and assume the value is non-nil.
1224e5dd7070Spatrick State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1225e5dd7070Spatrick }
1226e5dd7070Spatrick }
1227e5dd7070Spatrick
1228e5dd7070Spatrick // Objects returned from [NSNull null] are not nil.
1229e5dd7070Spatrick if (Cl == FC_NSNull) {
1230e5dd7070Spatrick if (M.getSelector() == NullSelector) {
1231e5dd7070Spatrick // Go ahead and assume the value is non-nil.
1232e5dd7070Spatrick State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1233e5dd7070Spatrick }
1234e5dd7070Spatrick }
1235e5dd7070Spatrick }
1236e5dd7070Spatrick C.addTransition(State);
1237e5dd7070Spatrick }
1238e5dd7070Spatrick
1239e5dd7070Spatrick //===----------------------------------------------------------------------===//
1240e5dd7070Spatrick // Check registration.
1241e5dd7070Spatrick //===----------------------------------------------------------------------===//
1242e5dd7070Spatrick
registerNilArgChecker(CheckerManager & mgr)1243e5dd7070Spatrick void ento::registerNilArgChecker(CheckerManager &mgr) {
1244e5dd7070Spatrick mgr.registerChecker<NilArgChecker>();
1245e5dd7070Spatrick }
1246e5dd7070Spatrick
shouldRegisterNilArgChecker(const CheckerManager & mgr)1247ec727ea7Spatrick bool ento::shouldRegisterNilArgChecker(const CheckerManager &mgr) {
1248e5dd7070Spatrick return true;
1249e5dd7070Spatrick }
1250e5dd7070Spatrick
registerCFNumberChecker(CheckerManager & mgr)1251e5dd7070Spatrick void ento::registerCFNumberChecker(CheckerManager &mgr) {
1252e5dd7070Spatrick mgr.registerChecker<CFNumberChecker>();
1253e5dd7070Spatrick }
1254e5dd7070Spatrick
shouldRegisterCFNumberChecker(const CheckerManager & mgr)1255ec727ea7Spatrick bool ento::shouldRegisterCFNumberChecker(const CheckerManager &mgr) {
1256e5dd7070Spatrick return true;
1257e5dd7070Spatrick }
1258e5dd7070Spatrick
registerCFRetainReleaseChecker(CheckerManager & mgr)1259e5dd7070Spatrick void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
1260e5dd7070Spatrick mgr.registerChecker<CFRetainReleaseChecker>();
1261e5dd7070Spatrick }
1262e5dd7070Spatrick
shouldRegisterCFRetainReleaseChecker(const CheckerManager & mgr)1263ec727ea7Spatrick bool ento::shouldRegisterCFRetainReleaseChecker(const CheckerManager &mgr) {
1264e5dd7070Spatrick return true;
1265e5dd7070Spatrick }
1266e5dd7070Spatrick
registerClassReleaseChecker(CheckerManager & mgr)1267e5dd7070Spatrick void ento::registerClassReleaseChecker(CheckerManager &mgr) {
1268e5dd7070Spatrick mgr.registerChecker<ClassReleaseChecker>();
1269e5dd7070Spatrick }
1270e5dd7070Spatrick
shouldRegisterClassReleaseChecker(const CheckerManager & mgr)1271ec727ea7Spatrick bool ento::shouldRegisterClassReleaseChecker(const CheckerManager &mgr) {
1272e5dd7070Spatrick return true;
1273e5dd7070Spatrick }
1274e5dd7070Spatrick
registerVariadicMethodTypeChecker(CheckerManager & mgr)1275e5dd7070Spatrick void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
1276e5dd7070Spatrick mgr.registerChecker<VariadicMethodTypeChecker>();
1277e5dd7070Spatrick }
1278e5dd7070Spatrick
shouldRegisterVariadicMethodTypeChecker(const CheckerManager & mgr)1279ec727ea7Spatrick bool ento::shouldRegisterVariadicMethodTypeChecker(const CheckerManager &mgr) {
1280e5dd7070Spatrick return true;
1281e5dd7070Spatrick }
1282e5dd7070Spatrick
registerObjCLoopChecker(CheckerManager & mgr)1283e5dd7070Spatrick void ento::registerObjCLoopChecker(CheckerManager &mgr) {
1284e5dd7070Spatrick mgr.registerChecker<ObjCLoopChecker>();
1285e5dd7070Spatrick }
1286e5dd7070Spatrick
shouldRegisterObjCLoopChecker(const CheckerManager & mgr)1287ec727ea7Spatrick bool ento::shouldRegisterObjCLoopChecker(const CheckerManager &mgr) {
1288e5dd7070Spatrick return true;
1289e5dd7070Spatrick }
1290e5dd7070Spatrick
registerObjCNonNilReturnValueChecker(CheckerManager & mgr)1291e5dd7070Spatrick void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
1292e5dd7070Spatrick mgr.registerChecker<ObjCNonNilReturnValueChecker>();
1293e5dd7070Spatrick }
1294e5dd7070Spatrick
shouldRegisterObjCNonNilReturnValueChecker(const CheckerManager & mgr)1295ec727ea7Spatrick bool ento::shouldRegisterObjCNonNilReturnValueChecker(const CheckerManager &mgr) {
1296e5dd7070Spatrick return true;
1297e5dd7070Spatrick }
1298