1 //== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file defines BasicObjCFoundationChecks, a class that encapsulates
10 // a set of simple checks to run on Objective-C code using Apple's Foundation
13 //===----------------------------------------------------------------------===//
15 #include "clang/AST/ASTContext.h"
16 #include "clang/AST/DeclObjC.h"
17 #include "clang/AST/Expr.h"
18 #include "clang/AST/ExprObjC.h"
19 #include "clang/AST/StmtObjC.h"
20 #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
21 #include "clang/Analysis/SelectorExtras.h"
22 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
23 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
24 #include "clang/StaticAnalyzer/Core/Checker.h"
25 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
27 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
28 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
29 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
30 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
31 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
32 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
33 #include "llvm/ADT/STLExtras.h"
34 #include "llvm/ADT/SmallString.h"
35 #include "llvm/ADT/StringMap.h"
36 #include "llvm/Support/raw_ostream.h"
39 using namespace clang
;
44 class APIMisuse
: public BugType
{
46 APIMisuse(const CheckerBase
*checker
, const char *name
)
47 : BugType(checker
, name
, "API Misuse (Apple)") {}
49 } // end anonymous namespace
51 //===----------------------------------------------------------------------===//
53 //===----------------------------------------------------------------------===//
55 static StringRef
GetReceiverInterfaceName(const ObjCMethodCall
&msg
) {
56 if (const ObjCInterfaceDecl
*ID
= msg
.getReceiverInterface())
57 return ID
->getIdentifier()->getName();
61 enum FoundationClass
{
72 static FoundationClass
findKnownClass(const ObjCInterfaceDecl
*ID
,
73 bool IncludeSuperclasses
= true) {
74 static llvm::StringMap
<FoundationClass
> Classes
;
75 if (Classes
.empty()) {
76 Classes
["NSArray"] = FC_NSArray
;
77 Classes
["NSDictionary"] = FC_NSDictionary
;
78 Classes
["NSEnumerator"] = FC_NSEnumerator
;
79 Classes
["NSNull"] = FC_NSNull
;
80 Classes
["NSOrderedSet"] = FC_NSOrderedSet
;
81 Classes
["NSSet"] = FC_NSSet
;
82 Classes
["NSString"] = FC_NSString
;
85 // FIXME: Should we cache this at all?
86 FoundationClass result
= Classes
.lookup(ID
->getIdentifier()->getName());
87 if (result
== FC_None
&& IncludeSuperclasses
)
88 if (const ObjCInterfaceDecl
*Super
= ID
->getSuperClass())
89 return findKnownClass(Super
);
94 //===----------------------------------------------------------------------===//
95 // NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
96 //===----------------------------------------------------------------------===//
99 class NilArgChecker
: public Checker
<check::PreObjCMessage
,
100 check::PostStmt
<ObjCDictionaryLiteral
>,
101 check::PostStmt
<ObjCArrayLiteral
>,
102 EventDispatcher
<ImplicitNullDerefEvent
>> {
103 mutable std::unique_ptr
<APIMisuse
> BT
;
105 mutable llvm::SmallDenseMap
<Selector
, unsigned, 16> StringSelectors
;
106 mutable Selector ArrayWithObjectSel
;
107 mutable Selector AddObjectSel
;
108 mutable Selector InsertObjectAtIndexSel
;
109 mutable Selector ReplaceObjectAtIndexWithObjectSel
;
110 mutable Selector SetObjectAtIndexedSubscriptSel
;
111 mutable Selector ArrayByAddingObjectSel
;
112 mutable Selector DictionaryWithObjectForKeySel
;
113 mutable Selector SetObjectForKeySel
;
114 mutable Selector SetObjectForKeyedSubscriptSel
;
115 mutable Selector RemoveObjectForKeySel
;
117 void warnIfNilExpr(const Expr
*E
, const char *Msg
, CheckerContext
&C
) const;
119 void warnIfNilArg(CheckerContext
&C
, const ObjCMethodCall
&msg
, unsigned Arg
,
120 FoundationClass Class
, bool CanBeSubscript
= false) const;
122 void generateBugReport(ExplodedNode
*N
, StringRef Msg
, SourceRange Range
,
123 const Expr
*Expr
, CheckerContext
&C
) const;
126 void checkPreObjCMessage(const ObjCMethodCall
&M
, CheckerContext
&C
) const;
127 void checkPostStmt(const ObjCDictionaryLiteral
*DL
, CheckerContext
&C
) const;
128 void checkPostStmt(const ObjCArrayLiteral
*AL
, CheckerContext
&C
) const;
130 } // end anonymous namespace
132 void NilArgChecker::warnIfNilExpr(const Expr
*E
,
134 CheckerContext
&C
) const {
135 auto Location
= C
.getSVal(E
).getAs
<Loc
>();
139 auto [NonNull
, Null
] = C
.getState()->assume(*Location
);
141 // If it's known to be null.
142 if (!NonNull
&& Null
) {
143 if (ExplodedNode
*N
= C
.generateErrorNode()) {
144 generateBugReport(N
, Msg
, E
->getSourceRange(), E
, C
);
149 // If it might be null, assume that it cannot after this operation.
151 // One needs to make sure the pointer is non-null to be used here.
152 if (ExplodedNode
*N
= C
.generateSink(Null
, C
.getPredecessor())) {
153 dispatchEvent({*Location
, /*IsLoad=*/false, N
, &C
.getBugReporter(),
154 /*IsDirectDereference=*/false});
156 C
.addTransition(NonNull
);
160 void NilArgChecker::warnIfNilArg(CheckerContext
&C
,
161 const ObjCMethodCall
&msg
,
163 FoundationClass Class
,
164 bool CanBeSubscript
) const {
165 // Check if the argument is nil.
166 ProgramStateRef State
= C
.getState();
167 if (!State
->isNull(msg
.getArgSVal(Arg
)).isConstrainedTrue())
170 // NOTE: We cannot throw non-fatal errors from warnIfNilExpr,
171 // because it's called multiple times from some callers, so it'd cause
172 // an unwanted state split if two or more non-fatal errors are thrown
173 // within the same checker callback. For now we don't want to, but
174 // it'll need to be fixed if we ever want to.
175 if (ExplodedNode
*N
= C
.generateErrorNode()) {
176 SmallString
<128> sbuf
;
177 llvm::raw_svector_ostream
os(sbuf
);
179 if (CanBeSubscript
&& msg
.getMessageKind() == OCM_Subscript
) {
181 if (Class
== FC_NSArray
) {
182 os
<< "Array element cannot be nil";
183 } else if (Class
== FC_NSDictionary
) {
185 os
<< "Value stored into '";
186 os
<< GetReceiverInterfaceName(msg
) << "' cannot be nil";
189 os
<< "'"<< GetReceiverInterfaceName(msg
) << "' key cannot be nil";
192 llvm_unreachable("Missing foundation class for the subscript expr");
195 if (Class
== FC_NSDictionary
) {
197 os
<< "Value argument ";
200 os
<< "Key argument ";
203 msg
.getSelector().print(os
);
204 os
<< "' cannot be nil";
206 os
<< "Argument to '" << GetReceiverInterfaceName(msg
) << "' method '";
207 msg
.getSelector().print(os
);
208 os
<< "' cannot be nil";
212 generateBugReport(N
, os
.str(), msg
.getArgSourceRange(Arg
),
213 msg
.getArgExpr(Arg
), C
);
217 void NilArgChecker::generateBugReport(ExplodedNode
*N
,
221 CheckerContext
&C
) const {
223 BT
.reset(new APIMisuse(this, "nil argument"));
225 auto R
= std::make_unique
<PathSensitiveBugReport
>(*BT
, Msg
, N
);
227 bugreporter::trackExpressionValue(N
, E
, *R
);
228 C
.emitReport(std::move(R
));
231 void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall
&msg
,
232 CheckerContext
&C
) const {
233 const ObjCInterfaceDecl
*ID
= msg
.getReceiverInterface();
237 FoundationClass Class
= findKnownClass(ID
);
239 static const unsigned InvalidArgIndex
= UINT_MAX
;
240 unsigned Arg
= InvalidArgIndex
;
241 bool CanBeSubscript
= false;
243 if (Class
== FC_NSString
) {
244 Selector S
= msg
.getSelector();
246 if (S
.isUnarySelector())
249 if (StringSelectors
.empty()) {
250 ASTContext
&Ctx
= C
.getASTContext();
252 getKeywordSelector(Ctx
, "caseInsensitiveCompare"),
253 getKeywordSelector(Ctx
, "compare"),
254 getKeywordSelector(Ctx
, "compare", "options"),
255 getKeywordSelector(Ctx
, "compare", "options", "range"),
256 getKeywordSelector(Ctx
, "compare", "options", "range", "locale"),
257 getKeywordSelector(Ctx
, "componentsSeparatedByCharactersInSet"),
258 getKeywordSelector(Ctx
, "initWithFormat"),
259 getKeywordSelector(Ctx
, "localizedCaseInsensitiveCompare"),
260 getKeywordSelector(Ctx
, "localizedCompare"),
261 getKeywordSelector(Ctx
, "localizedStandardCompare"),
263 for (Selector KnownSel
: Sels
)
264 StringSelectors
[KnownSel
] = 0;
266 auto I
= StringSelectors
.find(S
);
267 if (I
== StringSelectors
.end())
270 } else if (Class
== FC_NSArray
) {
271 Selector S
= msg
.getSelector();
273 if (S
.isUnarySelector())
276 if (ArrayWithObjectSel
.isNull()) {
277 ASTContext
&Ctx
= C
.getASTContext();
278 ArrayWithObjectSel
= getKeywordSelector(Ctx
, "arrayWithObject");
279 AddObjectSel
= getKeywordSelector(Ctx
, "addObject");
280 InsertObjectAtIndexSel
=
281 getKeywordSelector(Ctx
, "insertObject", "atIndex");
282 ReplaceObjectAtIndexWithObjectSel
=
283 getKeywordSelector(Ctx
, "replaceObjectAtIndex", "withObject");
284 SetObjectAtIndexedSubscriptSel
=
285 getKeywordSelector(Ctx
, "setObject", "atIndexedSubscript");
286 ArrayByAddingObjectSel
= getKeywordSelector(Ctx
, "arrayByAddingObject");
289 if (S
== ArrayWithObjectSel
|| S
== AddObjectSel
||
290 S
== InsertObjectAtIndexSel
|| S
== ArrayByAddingObjectSel
) {
292 } else if (S
== SetObjectAtIndexedSubscriptSel
) {
294 CanBeSubscript
= true;
295 } else if (S
== ReplaceObjectAtIndexWithObjectSel
) {
298 } else if (Class
== FC_NSDictionary
) {
299 Selector S
= msg
.getSelector();
301 if (S
.isUnarySelector())
304 if (DictionaryWithObjectForKeySel
.isNull()) {
305 ASTContext
&Ctx
= C
.getASTContext();
306 DictionaryWithObjectForKeySel
=
307 getKeywordSelector(Ctx
, "dictionaryWithObject", "forKey");
308 SetObjectForKeySel
= getKeywordSelector(Ctx
, "setObject", "forKey");
309 SetObjectForKeyedSubscriptSel
=
310 getKeywordSelector(Ctx
, "setObject", "forKeyedSubscript");
311 RemoveObjectForKeySel
= getKeywordSelector(Ctx
, "removeObjectForKey");
314 if (S
== DictionaryWithObjectForKeySel
|| S
== SetObjectForKeySel
) {
316 warnIfNilArg(C
, msg
, /* Arg */1, Class
);
317 } else if (S
== SetObjectForKeyedSubscriptSel
) {
318 CanBeSubscript
= true;
320 } else if (S
== RemoveObjectForKeySel
) {
325 // If argument is '0', report a warning.
326 if ((Arg
!= InvalidArgIndex
))
327 warnIfNilArg(C
, msg
, Arg
, Class
, CanBeSubscript
);
330 void NilArgChecker::checkPostStmt(const ObjCArrayLiteral
*AL
,
331 CheckerContext
&C
) const {
332 unsigned NumOfElements
= AL
->getNumElements();
333 for (unsigned i
= 0; i
< NumOfElements
; ++i
) {
334 warnIfNilExpr(AL
->getElement(i
), "Array element cannot be nil", C
);
338 void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral
*DL
,
339 CheckerContext
&C
) const {
340 unsigned NumOfElements
= DL
->getNumElements();
341 for (unsigned i
= 0; i
< NumOfElements
; ++i
) {
342 ObjCDictionaryElement Element
= DL
->getKeyValueElement(i
);
343 warnIfNilExpr(Element
.Key
, "Dictionary key cannot be nil", C
);
344 warnIfNilExpr(Element
.Value
, "Dictionary value cannot be nil", C
);
348 //===----------------------------------------------------------------------===//
349 // Checking for mismatched types passed to CFNumberCreate/CFNumberGetValue.
350 //===----------------------------------------------------------------------===//
353 class CFNumberChecker
: public Checker
< check::PreStmt
<CallExpr
> > {
354 mutable std::unique_ptr
<APIMisuse
> BT
;
355 mutable IdentifierInfo
*ICreate
= nullptr, *IGetValue
= nullptr;
357 CFNumberChecker() = default;
359 void checkPreStmt(const CallExpr
*CE
, CheckerContext
&C
) const;
361 } // end anonymous namespace
364 kCFNumberSInt8Type
= 1,
365 kCFNumberSInt16Type
= 2,
366 kCFNumberSInt32Type
= 3,
367 kCFNumberSInt64Type
= 4,
368 kCFNumberFloat32Type
= 5,
369 kCFNumberFloat64Type
= 6,
370 kCFNumberCharType
= 7,
371 kCFNumberShortType
= 8,
372 kCFNumberIntType
= 9,
373 kCFNumberLongType
= 10,
374 kCFNumberLongLongType
= 11,
375 kCFNumberFloatType
= 12,
376 kCFNumberDoubleType
= 13,
377 kCFNumberCFIndexType
= 14,
378 kCFNumberNSIntegerType
= 15,
379 kCFNumberCGFloatType
= 16
382 static std::optional
<uint64_t> GetCFNumberSize(ASTContext
&Ctx
, uint64_t i
) {
383 static const unsigned char FixedSize
[] = { 8, 16, 32, 64, 32, 64 };
385 if (i
< kCFNumberCharType
)
386 return FixedSize
[i
-1];
391 case kCFNumberCharType
: T
= Ctx
.CharTy
; break;
392 case kCFNumberShortType
: T
= Ctx
.ShortTy
; break;
393 case kCFNumberIntType
: T
= Ctx
.IntTy
; break;
394 case kCFNumberLongType
: T
= Ctx
.LongTy
; break;
395 case kCFNumberLongLongType
: T
= Ctx
.LongLongTy
; break;
396 case kCFNumberFloatType
: T
= Ctx
.FloatTy
; break;
397 case kCFNumberDoubleType
: T
= Ctx
.DoubleTy
; break;
398 case kCFNumberCFIndexType
:
399 case kCFNumberNSIntegerType
:
400 case kCFNumberCGFloatType
:
401 // FIXME: We need a way to map from names to Type*.
406 return Ctx
.getTypeSize(T
);
410 static const char* GetCFNumberTypeStr(uint64_t i
) {
411 static const char* Names
[] = {
412 "kCFNumberSInt8Type",
413 "kCFNumberSInt16Type",
414 "kCFNumberSInt32Type",
415 "kCFNumberSInt64Type",
416 "kCFNumberFloat32Type",
417 "kCFNumberFloat64Type",
419 "kCFNumberShortType",
422 "kCFNumberLongLongType",
423 "kCFNumberFloatType",
424 "kCFNumberDoubleType",
425 "kCFNumberCFIndexType",
426 "kCFNumberNSIntegerType",
427 "kCFNumberCGFloatType"
430 return i
<= kCFNumberCGFloatType
? Names
[i
-1] : "Invalid CFNumberType";
434 void CFNumberChecker::checkPreStmt(const CallExpr
*CE
,
435 CheckerContext
&C
) const {
436 ProgramStateRef state
= C
.getState();
437 const FunctionDecl
*FD
= C
.getCalleeDecl(CE
);
441 ASTContext
&Ctx
= C
.getASTContext();
443 ICreate
= &Ctx
.Idents
.get("CFNumberCreate");
444 IGetValue
= &Ctx
.Idents
.get("CFNumberGetValue");
446 if (!(FD
->getIdentifier() == ICreate
|| FD
->getIdentifier() == IGetValue
) ||
447 CE
->getNumArgs() != 3)
450 // Get the value of the "theType" argument.
451 SVal TheTypeVal
= C
.getSVal(CE
->getArg(1));
453 // FIXME: We really should allow ranges of valid theType values, and
454 // bifurcate the state appropriately.
455 std::optional
<nonloc::ConcreteInt
> V
=
456 dyn_cast
<nonloc::ConcreteInt
>(TheTypeVal
);
460 uint64_t NumberKind
= V
->getValue().getLimitedValue();
461 std::optional
<uint64_t> OptCFNumberSize
= GetCFNumberSize(Ctx
, NumberKind
);
463 // FIXME: In some cases we can emit an error.
464 if (!OptCFNumberSize
)
467 uint64_t CFNumberSize
= *OptCFNumberSize
;
469 // Look at the value of the integer being passed by reference. Essentially
470 // we want to catch cases where the value passed in is not equal to the
471 // size of the type being created.
472 SVal TheValueExpr
= C
.getSVal(CE
->getArg(2));
474 // FIXME: Eventually we should handle arbitrary locations. We can do this
475 // by having an enhanced memory model that does low-level typing.
476 std::optional
<loc::MemRegionVal
> LV
= TheValueExpr
.getAs
<loc::MemRegionVal
>();
480 const TypedValueRegion
* R
= dyn_cast
<TypedValueRegion
>(LV
->stripCasts());
484 QualType T
= Ctx
.getCanonicalType(R
->getValueType());
486 // FIXME: If the pointee isn't an integer type, should we flag a warning?
487 // People can do weird stuff with pointers.
489 if (!T
->isIntegralOrEnumerationType())
492 uint64_t PrimitiveTypeSize
= Ctx
.getTypeSize(T
);
494 if (PrimitiveTypeSize
== CFNumberSize
)
497 // FIXME: We can actually create an abstract "CFNumber" object that has
498 // the bits initialized to the provided values.
499 ExplodedNode
*N
= C
.generateNonFatalErrorNode();
501 SmallString
<128> sbuf
;
502 llvm::raw_svector_ostream
os(sbuf
);
503 bool isCreate
= (FD
->getIdentifier() == ICreate
);
506 os
<< (PrimitiveTypeSize
== 8 ? "An " : "A ")
507 << PrimitiveTypeSize
<< "-bit integer is used to initialize a "
508 << "CFNumber object that represents "
509 << (CFNumberSize
== 8 ? "an " : "a ")
510 << CFNumberSize
<< "-bit integer; ";
512 os
<< "A CFNumber object that represents "
513 << (CFNumberSize
== 8 ? "an " : "a ")
514 << CFNumberSize
<< "-bit integer is used to initialize "
515 << (PrimitiveTypeSize
== 8 ? "an " : "a ")
516 << PrimitiveTypeSize
<< "-bit integer; ";
519 if (PrimitiveTypeSize
< CFNumberSize
)
520 os
<< (CFNumberSize
- PrimitiveTypeSize
)
521 << " bits of the CFNumber value will "
522 << (isCreate
? "be garbage." : "overwrite adjacent storage.");
524 os
<< (PrimitiveTypeSize
- CFNumberSize
)
525 << " bits of the integer value will be "
526 << (isCreate
? "lost." : "garbage.");
529 BT
.reset(new APIMisuse(this, "Bad use of CFNumber APIs"));
531 auto report
= std::make_unique
<PathSensitiveBugReport
>(*BT
, os
.str(), N
);
532 report
->addRange(CE
->getArg(2)->getSourceRange());
533 C
.emitReport(std::move(report
));
537 //===----------------------------------------------------------------------===//
538 // CFRetain/CFRelease/CFMakeCollectable/CFAutorelease checking for null arguments.
539 //===----------------------------------------------------------------------===//
542 class CFRetainReleaseChecker
: public Checker
<check::PreCall
> {
543 mutable APIMisuse BT
{this, "null passed to CF memory management function"};
544 const CallDescriptionSet ModelledCalls
= {
547 {{"CFMakeCollectable"}, 1},
548 {{"CFAutorelease"}, 1},
552 void checkPreCall(const CallEvent
&Call
, CheckerContext
&C
) const;
554 } // end anonymous namespace
556 void CFRetainReleaseChecker::checkPreCall(const CallEvent
&Call
,
557 CheckerContext
&C
) const {
558 // TODO: Make this check part of CallDescription.
559 if (!Call
.isGlobalCFunction())
562 // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease.
563 if (!ModelledCalls
.contains(Call
))
566 // Get the argument's value.
567 SVal ArgVal
= Call
.getArgSVal(0);
568 std::optional
<DefinedSVal
> DefArgVal
= ArgVal
.getAs
<DefinedSVal
>();
573 ProgramStateRef state
= C
.getState();
574 ProgramStateRef stateNonNull
, stateNull
;
575 std::tie(stateNonNull
, stateNull
) = state
->assume(*DefArgVal
);
578 ExplodedNode
*N
= C
.generateErrorNode(stateNull
);
583 raw_svector_ostream
OS(Str
);
584 OS
<< "Null pointer argument in call to "
585 << cast
<FunctionDecl
>(Call
.getDecl())->getName();
587 auto report
= std::make_unique
<PathSensitiveBugReport
>(BT
, OS
.str(), N
);
588 report
->addRange(Call
.getArgSourceRange(0));
589 bugreporter::trackExpressionValue(N
, Call
.getArgExpr(0), *report
);
590 C
.emitReport(std::move(report
));
594 // From here on, we know the argument is non-null.
595 C
.addTransition(stateNonNull
);
598 //===----------------------------------------------------------------------===//
599 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
600 //===----------------------------------------------------------------------===//
603 class ClassReleaseChecker
: public Checker
<check::PreObjCMessage
> {
604 mutable Selector releaseS
;
605 mutable Selector retainS
;
606 mutable Selector autoreleaseS
;
607 mutable Selector drainS
;
608 mutable std::unique_ptr
<BugType
> BT
;
611 void checkPreObjCMessage(const ObjCMethodCall
&msg
, CheckerContext
&C
) const;
613 } // end anonymous namespace
615 void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall
&msg
,
616 CheckerContext
&C
) const {
618 BT
.reset(new APIMisuse(
619 this, "message incorrectly sent to class instead of class instance"));
621 ASTContext
&Ctx
= C
.getASTContext();
622 releaseS
= GetNullarySelector("release", Ctx
);
623 retainS
= GetNullarySelector("retain", Ctx
);
624 autoreleaseS
= GetNullarySelector("autorelease", Ctx
);
625 drainS
= GetNullarySelector("drain", Ctx
);
628 if (msg
.isInstanceMessage())
630 const ObjCInterfaceDecl
*Class
= msg
.getReceiverInterface();
633 Selector S
= msg
.getSelector();
634 if (!(S
== releaseS
|| S
== retainS
|| S
== autoreleaseS
|| S
== drainS
))
637 if (ExplodedNode
*N
= C
.generateNonFatalErrorNode()) {
638 SmallString
<200> buf
;
639 llvm::raw_svector_ostream
os(buf
);
643 os
<< "' message should be sent to instances "
644 "of class '" << Class
->getName()
645 << "' and not the class directly";
647 auto report
= std::make_unique
<PathSensitiveBugReport
>(*BT
, os
.str(), N
);
648 report
->addRange(msg
.getSourceRange());
649 C
.emitReport(std::move(report
));
653 //===----------------------------------------------------------------------===//
654 // Check for passing non-Objective-C types to variadic methods that expect
655 // only Objective-C types.
656 //===----------------------------------------------------------------------===//
659 class VariadicMethodTypeChecker
: public Checker
<check::PreObjCMessage
> {
660 mutable Selector arrayWithObjectsS
;
661 mutable Selector dictionaryWithObjectsAndKeysS
;
662 mutable Selector setWithObjectsS
;
663 mutable Selector orderedSetWithObjectsS
;
664 mutable Selector initWithObjectsS
;
665 mutable Selector initWithObjectsAndKeysS
;
666 mutable std::unique_ptr
<BugType
> BT
;
668 bool isVariadicMessage(const ObjCMethodCall
&msg
) const;
671 void checkPreObjCMessage(const ObjCMethodCall
&msg
, CheckerContext
&C
) const;
673 } // end anonymous namespace
675 /// isVariadicMessage - Returns whether the given message is a variadic message,
676 /// where all arguments must be Objective-C types.
678 VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall
&msg
) const {
679 const ObjCMethodDecl
*MD
= msg
.getDecl();
681 if (!MD
|| !MD
->isVariadic() || isa
<ObjCProtocolDecl
>(MD
->getDeclContext()))
684 Selector S
= msg
.getSelector();
686 if (msg
.isInstanceMessage()) {
687 // FIXME: Ideally we'd look at the receiver interface here, but that's not
688 // useful for init, because alloc returns 'id'. In theory, this could lead
689 // to false positives, for example if there existed a class that had an
690 // initWithObjects: implementation that does accept non-Objective-C pointer
691 // types, but the chance of that happening is pretty small compared to the
692 // gains that this analysis gives.
693 const ObjCInterfaceDecl
*Class
= MD
->getClassInterface();
695 switch (findKnownClass(Class
)) {
697 case FC_NSOrderedSet
:
699 return S
== initWithObjectsS
;
700 case FC_NSDictionary
:
701 return S
== initWithObjectsAndKeysS
;
706 const ObjCInterfaceDecl
*Class
= msg
.getReceiverInterface();
708 switch (findKnownClass(Class
)) {
710 return S
== arrayWithObjectsS
;
711 case FC_NSOrderedSet
:
712 return S
== orderedSetWithObjectsS
;
714 return S
== setWithObjectsS
;
715 case FC_NSDictionary
:
716 return S
== dictionaryWithObjectsAndKeysS
;
723 void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall
&msg
,
724 CheckerContext
&C
) const {
726 BT
.reset(new APIMisuse(this,
727 "Arguments passed to variadic method aren't all "
728 "Objective-C pointer types"));
730 ASTContext
&Ctx
= C
.getASTContext();
731 arrayWithObjectsS
= GetUnarySelector("arrayWithObjects", Ctx
);
732 dictionaryWithObjectsAndKeysS
=
733 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx
);
734 setWithObjectsS
= GetUnarySelector("setWithObjects", Ctx
);
735 orderedSetWithObjectsS
= GetUnarySelector("orderedSetWithObjects", Ctx
);
737 initWithObjectsS
= GetUnarySelector("initWithObjects", Ctx
);
738 initWithObjectsAndKeysS
= GetUnarySelector("initWithObjectsAndKeys", Ctx
);
741 if (!isVariadicMessage(msg
))
744 // We are not interested in the selector arguments since they have
745 // well-defined types, so the compiler will issue a warning for them.
746 unsigned variadicArgsBegin
= msg
.getSelector().getNumArgs();
748 // We're not interested in the last argument since it has to be nil or the
749 // compiler would have issued a warning for it elsewhere.
750 unsigned variadicArgsEnd
= msg
.getNumArgs() - 1;
752 if (variadicArgsEnd
<= variadicArgsBegin
)
755 // Verify that all arguments have Objective-C types.
756 std::optional
<ExplodedNode
*> errorNode
;
758 for (unsigned I
= variadicArgsBegin
; I
!= variadicArgsEnd
; ++I
) {
759 QualType ArgTy
= msg
.getArgExpr(I
)->getType();
760 if (ArgTy
->isObjCObjectPointerType())
763 // Block pointers are treaded as Objective-C pointers.
764 if (ArgTy
->isBlockPointerType())
767 // Ignore pointer constants.
768 if (isa
<loc::ConcreteInt
>(msg
.getArgSVal(I
)))
771 // Ignore pointer types annotated with 'NSObject' attribute.
772 if (C
.getASTContext().isObjCNSObjectType(ArgTy
))
775 // Ignore CF references, which can be toll-free bridged.
776 if (coreFoundation::isCFObjectRef(ArgTy
))
779 // Generate only one error node to use for all bug reports.
781 errorNode
= C
.generateNonFatalErrorNode();
786 SmallString
<128> sbuf
;
787 llvm::raw_svector_ostream
os(sbuf
);
789 StringRef TypeName
= GetReceiverInterfaceName(msg
);
790 if (!TypeName
.empty())
791 os
<< "Argument to '" << TypeName
<< "' method '";
793 os
<< "Argument to method '";
795 msg
.getSelector().print(os
);
796 os
<< "' should be an Objective-C pointer type, not '";
797 ArgTy
.print(os
, C
.getLangOpts());
801 std::make_unique
<PathSensitiveBugReport
>(*BT
, os
.str(), *errorNode
);
802 R
->addRange(msg
.getArgSourceRange(I
));
803 C
.emitReport(std::move(R
));
807 //===----------------------------------------------------------------------===//
808 // Improves the modeling of loops over Cocoa collections.
809 //===----------------------------------------------------------------------===//
811 // The map from container symbol to the container count symbol.
812 // We currently will remember the last container count symbol encountered.
813 REGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap
, SymbolRef
, SymbolRef
)
814 REGISTER_MAP_WITH_PROGRAMSTATE(ContainerNonEmptyMap
, SymbolRef
, bool)
817 class ObjCLoopChecker
818 : public Checker
<check::PostStmt
<ObjCForCollectionStmt
>,
819 check::PostObjCMessage
,
821 check::PointerEscape
> {
822 mutable IdentifierInfo
*CountSelectorII
= nullptr;
824 bool isCollectionCountMethod(const ObjCMethodCall
&M
,
825 CheckerContext
&C
) const;
828 ObjCLoopChecker() = default;
829 void checkPostStmt(const ObjCForCollectionStmt
*FCS
, CheckerContext
&C
) const;
830 void checkPostObjCMessage(const ObjCMethodCall
&M
, CheckerContext
&C
) const;
831 void checkDeadSymbols(SymbolReaper
&SymReaper
, CheckerContext
&C
) const;
832 ProgramStateRef
checkPointerEscape(ProgramStateRef State
,
833 const InvalidatedSymbols
&Escaped
,
834 const CallEvent
*Call
,
835 PointerEscapeKind Kind
) const;
837 } // end anonymous namespace
839 static bool isKnownNonNilCollectionType(QualType T
) {
840 const ObjCObjectPointerType
*PT
= T
->getAs
<ObjCObjectPointerType
>();
844 const ObjCInterfaceDecl
*ID
= PT
->getInterfaceDecl();
848 switch (findKnownClass(ID
)) {
850 case FC_NSDictionary
:
851 case FC_NSEnumerator
:
852 case FC_NSOrderedSet
:
860 /// Assumes that the collection is non-nil.
862 /// If the collection is known to be nil, returns NULL to indicate an infeasible
864 static ProgramStateRef
checkCollectionNonNil(CheckerContext
&C
,
865 ProgramStateRef State
,
866 const ObjCForCollectionStmt
*FCS
) {
870 SVal CollectionVal
= C
.getSVal(FCS
->getCollection());
871 std::optional
<DefinedSVal
> KnownCollection
=
872 CollectionVal
.getAs
<DefinedSVal
>();
873 if (!KnownCollection
)
876 ProgramStateRef StNonNil
, StNil
;
877 std::tie(StNonNil
, StNil
) = State
->assume(*KnownCollection
);
878 if (StNil
&& !StNonNil
) {
879 // The collection is nil. This path is infeasible.
886 /// Assumes that the collection elements are non-nil.
888 /// This only applies if the collection is one of those known not to contain
890 static ProgramStateRef
checkElementNonNil(CheckerContext
&C
,
891 ProgramStateRef State
,
892 const ObjCForCollectionStmt
*FCS
) {
896 // See if the collection is one where we /know/ the elements are non-nil.
897 if (!isKnownNonNilCollectionType(FCS
->getCollection()->getType()))
900 const LocationContext
*LCtx
= C
.getLocationContext();
901 const Stmt
*Element
= FCS
->getElement();
903 // FIXME: Copied from ExprEngineObjC.
904 std::optional
<Loc
> ElementLoc
;
905 if (const DeclStmt
*DS
= dyn_cast
<DeclStmt
>(Element
)) {
906 const VarDecl
*ElemDecl
= cast
<VarDecl
>(DS
->getSingleDecl());
907 assert(ElemDecl
->getInit() == nullptr);
908 ElementLoc
= State
->getLValue(ElemDecl
, LCtx
);
910 ElementLoc
= State
->getSVal(Element
, LCtx
).getAs
<Loc
>();
916 // Go ahead and assume the value is non-nil.
917 SVal Val
= State
->getSVal(*ElementLoc
);
918 return State
->assume(cast
<DefinedOrUnknownSVal
>(Val
), true);
921 /// Returns NULL state if the collection is known to contain elements
922 /// (or is known not to contain elements if the Assumption parameter is false.)
923 static ProgramStateRef
924 assumeCollectionNonEmpty(CheckerContext
&C
, ProgramStateRef State
,
925 SymbolRef CollectionS
, bool Assumption
) {
926 if (!State
|| !CollectionS
)
929 const SymbolRef
*CountS
= State
->get
<ContainerCountMap
>(CollectionS
);
931 const bool *KnownNonEmpty
= State
->get
<ContainerNonEmptyMap
>(CollectionS
);
933 return State
->set
<ContainerNonEmptyMap
>(CollectionS
, Assumption
);
934 return (Assumption
== *KnownNonEmpty
) ? State
: nullptr;
937 SValBuilder
&SvalBuilder
= C
.getSValBuilder();
938 SVal CountGreaterThanZeroVal
=
939 SvalBuilder
.evalBinOp(State
, BO_GT
,
940 nonloc::SymbolVal(*CountS
),
941 SvalBuilder
.makeIntVal(0, (*CountS
)->getType()),
942 SvalBuilder
.getConditionType());
943 std::optional
<DefinedSVal
> CountGreaterThanZero
=
944 CountGreaterThanZeroVal
.getAs
<DefinedSVal
>();
945 if (!CountGreaterThanZero
) {
946 // The SValBuilder cannot construct a valid SVal for this condition.
947 // This means we cannot properly reason about it.
951 return State
->assume(*CountGreaterThanZero
, Assumption
);
954 static ProgramStateRef
955 assumeCollectionNonEmpty(CheckerContext
&C
, ProgramStateRef State
,
956 const ObjCForCollectionStmt
*FCS
,
961 SymbolRef CollectionS
= C
.getSVal(FCS
->getCollection()).getAsSymbol();
962 return assumeCollectionNonEmpty(C
, State
, CollectionS
, Assumption
);
965 /// If the fist block edge is a back edge, we are reentering the loop.
966 static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode
*N
,
967 const ObjCForCollectionStmt
*FCS
) {
971 ProgramPoint P
= N
->getLocation();
972 if (std::optional
<BlockEdge
> BE
= P
.getAs
<BlockEdge
>()) {
973 return BE
->getSrc()->getLoopTarget() == FCS
;
976 // Keep looking for a block edge.
977 for (const ExplodedNode
*N
: N
->preds()) {
978 if (alreadyExecutedAtLeastOneLoopIteration(N
, FCS
))
985 void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt
*FCS
,
986 CheckerContext
&C
) const {
987 ProgramStateRef State
= C
.getState();
989 // Check if this is the branch for the end of the loop.
990 if (!ExprEngine::hasMoreIteration(State
, FCS
, C
.getLocationContext())) {
991 if (!alreadyExecutedAtLeastOneLoopIteration(C
.getPredecessor(), FCS
))
992 State
= assumeCollectionNonEmpty(C
, State
, FCS
, /*Assumption*/false);
994 // Otherwise, this is a branch that goes through the loop body.
996 State
= checkCollectionNonNil(C
, State
, FCS
);
997 State
= checkElementNonNil(C
, State
, FCS
);
998 State
= assumeCollectionNonEmpty(C
, State
, FCS
, /*Assumption*/true);
1002 C
.generateSink(C
.getState(), C
.getPredecessor());
1003 else if (State
!= C
.getState())
1004 C
.addTransition(State
);
1007 bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall
&M
,
1008 CheckerContext
&C
) const {
1009 Selector S
= M
.getSelector();
1010 // Initialize the identifiers on first use.
1011 if (!CountSelectorII
)
1012 CountSelectorII
= &C
.getASTContext().Idents
.get("count");
1014 // If the method returns collection count, record the value.
1015 return S
.isUnarySelector() &&
1016 (S
.getIdentifierInfoForSlot(0) == CountSelectorII
);
1019 void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall
&M
,
1020 CheckerContext
&C
) const {
1021 if (!M
.isInstanceMessage())
1024 const ObjCInterfaceDecl
*ClassID
= M
.getReceiverInterface();
1028 FoundationClass Class
= findKnownClass(ClassID
);
1029 if (Class
!= FC_NSDictionary
&&
1030 Class
!= FC_NSArray
&&
1031 Class
!= FC_NSSet
&&
1032 Class
!= FC_NSOrderedSet
)
1035 SymbolRef ContainerS
= M
.getReceiverSVal().getAsSymbol();
1039 // If we are processing a call to "count", get the symbolic value returned by
1040 // a call to "count" and add it to the map.
1041 if (!isCollectionCountMethod(M
, C
))
1044 const Expr
*MsgExpr
= M
.getOriginExpr();
1045 SymbolRef CountS
= C
.getSVal(MsgExpr
).getAsSymbol();
1047 ProgramStateRef State
= C
.getState();
1049 C
.getSymbolManager().addSymbolDependency(ContainerS
, CountS
);
1050 State
= State
->set
<ContainerCountMap
>(ContainerS
, CountS
);
1052 if (const bool *NonEmpty
= State
->get
<ContainerNonEmptyMap
>(ContainerS
)) {
1053 State
= State
->remove
<ContainerNonEmptyMap
>(ContainerS
);
1054 State
= assumeCollectionNonEmpty(C
, State
, ContainerS
, *NonEmpty
);
1057 C
.addTransition(State
);
1061 static SymbolRef
getMethodReceiverIfKnownImmutable(const CallEvent
*Call
) {
1062 const ObjCMethodCall
*Message
= dyn_cast_or_null
<ObjCMethodCall
>(Call
);
1066 const ObjCMethodDecl
*MD
= Message
->getDecl();
1070 const ObjCInterfaceDecl
*StaticClass
;
1071 if (isa
<ObjCProtocolDecl
>(MD
->getDeclContext())) {
1072 // We can't find out where the method was declared without doing more work.
1073 // Instead, see if the receiver is statically typed as a known immutable
1075 StaticClass
= Message
->getOriginExpr()->getReceiverInterface();
1077 StaticClass
= MD
->getClassInterface();
1083 switch (findKnownClass(StaticClass
, /*IncludeSuper=*/false)) {
1087 case FC_NSDictionary
:
1088 case FC_NSEnumerator
:
1090 case FC_NSOrderedSet
:
1096 return Message
->getReceiverSVal().getAsSymbol();
1100 ObjCLoopChecker::checkPointerEscape(ProgramStateRef State
,
1101 const InvalidatedSymbols
&Escaped
,
1102 const CallEvent
*Call
,
1103 PointerEscapeKind Kind
) const {
1104 SymbolRef ImmutableReceiver
= getMethodReceiverIfKnownImmutable(Call
);
1106 // Remove the invalidated symbols from the collection count map.
1107 for (SymbolRef Sym
: Escaped
) {
1108 // Don't invalidate this symbol's count if we know the method being called
1109 // is declared on an immutable class. This isn't completely correct if the
1110 // receiver is also passed as an argument, but in most uses of NSArray,
1111 // NSDictionary, etc. this isn't likely to happen in a dangerous way.
1112 if (Sym
== ImmutableReceiver
)
1115 // The symbol escaped. Pessimistically, assume that the count could have
1117 State
= State
->remove
<ContainerCountMap
>(Sym
);
1118 State
= State
->remove
<ContainerNonEmptyMap
>(Sym
);
1123 void ObjCLoopChecker::checkDeadSymbols(SymbolReaper
&SymReaper
,
1124 CheckerContext
&C
) const {
1125 ProgramStateRef State
= C
.getState();
1127 // Remove the dead symbols from the collection count map.
1128 ContainerCountMapTy Tracked
= State
->get
<ContainerCountMap
>();
1129 for (SymbolRef Sym
: llvm::make_first_range(Tracked
)) {
1130 if (SymReaper
.isDead(Sym
)) {
1131 State
= State
->remove
<ContainerCountMap
>(Sym
);
1132 State
= State
->remove
<ContainerNonEmptyMap
>(Sym
);
1136 C
.addTransition(State
);
1140 /// \class ObjCNonNilReturnValueChecker
1141 /// The checker restricts the return values of APIs known to
1142 /// never (or almost never) return 'nil'.
1143 class ObjCNonNilReturnValueChecker
1144 : public Checker
<check::PostObjCMessage
,
1145 check::PostStmt
<ObjCArrayLiteral
>,
1146 check::PostStmt
<ObjCDictionaryLiteral
>,
1147 check::PostStmt
<ObjCBoxedExpr
> > {
1148 mutable bool Initialized
= false;
1149 mutable Selector ObjectAtIndex
;
1150 mutable Selector ObjectAtIndexedSubscript
;
1151 mutable Selector NullSelector
;
1154 ObjCNonNilReturnValueChecker() = default;
1156 ProgramStateRef
assumeExprIsNonNull(const Expr
*NonNullExpr
,
1157 ProgramStateRef State
,
1158 CheckerContext
&C
) const;
1159 void assumeExprIsNonNull(const Expr
*E
, CheckerContext
&C
) const {
1160 C
.addTransition(assumeExprIsNonNull(E
, C
.getState(), C
));
1163 void checkPostStmt(const ObjCArrayLiteral
*E
, CheckerContext
&C
) const {
1164 assumeExprIsNonNull(E
, C
);
1166 void checkPostStmt(const ObjCDictionaryLiteral
*E
, CheckerContext
&C
) const {
1167 assumeExprIsNonNull(E
, C
);
1169 void checkPostStmt(const ObjCBoxedExpr
*E
, CheckerContext
&C
) const {
1170 assumeExprIsNonNull(E
, C
);
1173 void checkPostObjCMessage(const ObjCMethodCall
&M
, CheckerContext
&C
) const;
1175 } // end anonymous namespace
1178 ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr
*NonNullExpr
,
1179 ProgramStateRef State
,
1180 CheckerContext
&C
) const {
1181 SVal Val
= C
.getSVal(NonNullExpr
);
1182 if (std::optional
<DefinedOrUnknownSVal
> DV
=
1183 Val
.getAs
<DefinedOrUnknownSVal
>())
1184 return State
->assume(*DV
, true);
1188 void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall
&M
,
1191 ProgramStateRef State
= C
.getState();
1194 ASTContext
&Ctx
= C
.getASTContext();
1195 ObjectAtIndex
= GetUnarySelector("objectAtIndex", Ctx
);
1196 ObjectAtIndexedSubscript
= GetUnarySelector("objectAtIndexedSubscript", Ctx
);
1197 NullSelector
= GetNullarySelector("null", Ctx
);
1200 // Check the receiver type.
1201 if (const ObjCInterfaceDecl
*Interface
= M
.getReceiverInterface()) {
1203 // Assume that object returned from '[self init]' or '[super init]' is not
1204 // 'nil' if we are processing an inlined function/method.
1206 // A defensive callee will (and should) check if the object returned by
1207 // '[super init]' is 'nil' before doing it's own initialization. However,
1208 // since 'nil' is rarely returned in practice, we should not warn when the
1209 // caller to the defensive constructor uses the object in contexts where
1210 // 'nil' is not accepted.
1211 if (!C
.inTopFrame() && M
.getDecl() &&
1212 M
.getDecl()->getMethodFamily() == OMF_init
&&
1213 M
.isReceiverSelfOrSuper()) {
1214 State
= assumeExprIsNonNull(M
.getOriginExpr(), State
, C
);
1217 FoundationClass Cl
= findKnownClass(Interface
);
1219 // Objects returned from
1220 // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
1222 if (Cl
== FC_NSArray
|| Cl
== FC_NSOrderedSet
) {
1223 Selector Sel
= M
.getSelector();
1224 if (Sel
== ObjectAtIndex
|| Sel
== ObjectAtIndexedSubscript
) {
1225 // Go ahead and assume the value is non-nil.
1226 State
= assumeExprIsNonNull(M
.getOriginExpr(), State
, C
);
1230 // Objects returned from [NSNull null] are not nil.
1231 if (Cl
== FC_NSNull
) {
1232 if (M
.getSelector() == NullSelector
) {
1233 // Go ahead and assume the value is non-nil.
1234 State
= assumeExprIsNonNull(M
.getOriginExpr(), State
, C
);
1238 C
.addTransition(State
);
1241 //===----------------------------------------------------------------------===//
1242 // Check registration.
1243 //===----------------------------------------------------------------------===//
1245 void ento::registerNilArgChecker(CheckerManager
&mgr
) {
1246 mgr
.registerChecker
<NilArgChecker
>();
1249 bool ento::shouldRegisterNilArgChecker(const CheckerManager
&mgr
) {
1253 void ento::registerCFNumberChecker(CheckerManager
&mgr
) {
1254 mgr
.registerChecker
<CFNumberChecker
>();
1257 bool ento::shouldRegisterCFNumberChecker(const CheckerManager
&mgr
) {
1261 void ento::registerCFRetainReleaseChecker(CheckerManager
&mgr
) {
1262 mgr
.registerChecker
<CFRetainReleaseChecker
>();
1265 bool ento::shouldRegisterCFRetainReleaseChecker(const CheckerManager
&mgr
) {
1269 void ento::registerClassReleaseChecker(CheckerManager
&mgr
) {
1270 mgr
.registerChecker
<ClassReleaseChecker
>();
1273 bool ento::shouldRegisterClassReleaseChecker(const CheckerManager
&mgr
) {
1277 void ento::registerVariadicMethodTypeChecker(CheckerManager
&mgr
) {
1278 mgr
.registerChecker
<VariadicMethodTypeChecker
>();
1281 bool ento::shouldRegisterVariadicMethodTypeChecker(const CheckerManager
&mgr
) {
1285 void ento::registerObjCLoopChecker(CheckerManager
&mgr
) {
1286 mgr
.registerChecker
<ObjCLoopChecker
>();
1289 bool ento::shouldRegisterObjCLoopChecker(const CheckerManager
&mgr
) {
1293 void ento::registerObjCNonNilReturnValueChecker(CheckerManager
&mgr
) {
1294 mgr
.registerChecker
<ObjCNonNilReturnValueChecker
>();
1297 bool ento::shouldRegisterObjCNonNilReturnValueChecker(const CheckerManager
&mgr
) {