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"
44class APIMisuse :
public BugType {
46 APIMisuse(
const CheckerBase *checker,
const char *name)
57 return ID->getIdentifier()->getName();
73 bool IncludeSuperclasses =
true) {
74 static llvm::StringMap<FoundationClass> Classes;
75 if (Classes.empty()) {
86 FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());
87 if (result ==
FC_None && IncludeSuperclasses)
99class 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;
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;
132void NilArgChecker::warnIfNilExpr(
const Expr *E,
135 auto Location =
C.getSVal(E).getAs<
Loc>();
153 dispatchEvent({*Location,
false, N, &
C.getBugReporter(),
164 bool CanBeSubscript)
const {
167 if (!State->isNull(msg.
getArgSVal(Arg)).isConstrainedTrue())
177 llvm::raw_svector_ostream os(sbuf);
182 os <<
"Array element cannot be nil";
185 os <<
"Value stored into '";
192 llvm_unreachable(
"Missing foundation class for the subscript expr");
197 os <<
"Value argument ";
200 os <<
"Key argument ";
204 os <<
"' cannot be nil";
208 os <<
"' cannot be nil";
223 BT.reset(
new APIMisuse(
this,
"nil argument"));
225 auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
228 C.emitReport(std::move(R));
239 static const unsigned InvalidArgIndex =
UINT_MAX;
240 unsigned Arg = InvalidArgIndex;
241 bool CanBeSubscript =
false;
246 if (S.isUnarySelector())
249 if (StringSelectors.empty()) {
264 StringSelectors[KnownSel] = 0;
266 auto I = StringSelectors.find(S);
267 if (I == StringSelectors.end())
273 if (S.isUnarySelector())
276 if (ArrayWithObjectSel.isNull()) {
280 InsertObjectAtIndexSel =
282 ReplaceObjectAtIndexWithObjectSel =
284 SetObjectAtIndexedSubscriptSel =
289 if (S == ArrayWithObjectSel || S == AddObjectSel ||
290 S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) {
292 }
else if (S == SetObjectAtIndexedSubscriptSel) {
294 CanBeSubscript =
true;
295 }
else if (S == ReplaceObjectAtIndexWithObjectSel) {
301 if (S.isUnarySelector())
304 if (DictionaryWithObjectForKeySel.isNull()) {
306 DictionaryWithObjectForKeySel =
309 SetObjectForKeyedSubscriptSel =
314 if (S == DictionaryWithObjectForKeySel || S == SetObjectForKeySel) {
316 warnIfNilArg(
C, msg, 1, Class);
317 }
else if (S == SetObjectForKeyedSubscriptSel) {
318 CanBeSubscript =
true;
320 }
else if (S == RemoveObjectForKeySel) {
326 if ((Arg != InvalidArgIndex))
327 warnIfNilArg(
C, msg, Arg, Class, CanBeSubscript);
333 for (
unsigned i = 0; i < NumOfElements; ++i) {
334 warnIfNilExpr(AL->
getElement(i),
"Array element cannot be nil",
C);
341 for (
unsigned i = 0; i < NumOfElements; ++i) {
343 warnIfNilExpr(Element.Key,
"Dictionary key cannot be nil",
C);
344 warnIfNilExpr(Element.Value,
"Dictionary value cannot be nil",
C);
353class CFNumberChecker :
public Checker< check::PreStmt<CallExpr> > {
354 mutable std::unique_ptr<APIMisuse> BT;
357 CFNumberChecker() =
default;
383 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
386 return FixedSize[i-1];
410static 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"
434void CFNumberChecker::checkPreStmt(
const CallExpr *CE,
443 ICreate = &Ctx.
Idents.
get(
"CFNumberCreate");
444 IGetValue = &Ctx.
Idents.
get(
"CFNumberGetValue");
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);
464 if (!OptCFNumberSize)
467 uint64_t CFNumberSize = *OptCFNumberSize;
494 if (PrimitiveTypeSize == CFNumberSize)
502 llvm::raw_svector_ostream os(sbuf);
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);
533 C.emitReport(std::move(report));
542class CFRetainReleaseChecker :
public Checker<check::PreCall> {
543 mutable APIMisuse BT{
this,
"null passed to CF memory management function"};
547 {{
"CFMakeCollectable"}, 1},
548 {{
"CFAutorelease"}, 1},
556void CFRetainReleaseChecker::checkPreCall(
const CallEvent &
Call,
559 if (!
Call.isGlobalCFunction())
563 if (!ModelledCalls.contains(
Call))
575 std::tie(stateNonNull, stateNull) = state->assume(*DefArgVal);
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));
590 C.emitReport(std::move(report));
595 C.addTransition(stateNonNull);
603class ClassReleaseChecker :
public Checker<check::PreObjCMessage> {
608 mutable std::unique_ptr<BugType> BT;
615void ClassReleaseChecker::checkPreObjCMessage(
const ObjCMethodCall &msg,
618 BT.reset(
new APIMisuse(
619 this,
"message incorrectly sent to class instead of class instance"));
634 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
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);
649 C.emitReport(std::move(report));
659class VariadicMethodTypeChecker :
public Checker<check::PreObjCMessage> {
661 mutable Selector dictionaryWithObjectsAndKeysS;
663 mutable Selector orderedSetWithObjectsS;
665 mutable Selector initWithObjectsAndKeysS;
666 mutable std::unique_ptr<BugType> BT;
678VariadicMethodTypeChecker::isVariadicMessage(
const ObjCMethodCall &msg)
const {
699 return S == initWithObjectsS;
701 return S == initWithObjectsAndKeysS;
710 return S == arrayWithObjectsS;
712 return S == orderedSetWithObjectsS;
714 return S == setWithObjectsS;
716 return S == dictionaryWithObjectsAndKeysS;
723void VariadicMethodTypeChecker::checkPreObjCMessage(
const ObjCMethodCall &msg,
726 BT.reset(
new APIMisuse(
this,
727 "Arguments passed to variadic method aren't all "
728 "Objective-C pointer types"));
732 dictionaryWithObjectsAndKeysS =
741 if (!isVariadicMessage(msg))
750 unsigned variadicArgsEnd = msg.
getNumArgs() - 1;
752 if (variadicArgsEnd <= variadicArgsBegin)
756 std::optional<ExplodedNode *> errorNode;
758 for (
unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
772 if (
C.getASTContext().isObjCNSObjectType(ArgTy))
781 errorNode =
C.generateNonFatalErrorNode();
787 llvm::raw_svector_ostream os(sbuf);
791 os <<
"Argument to '" <<
TypeName <<
"' method '";
793 os <<
"Argument to method '";
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);
803 C.emitReport(std::move(R));
818 :
public Checker<check::PostStmt<ObjCForCollectionStmt>,
819 check::PostObjCMessage,
821 check::PointerEscape > {
828 ObjCLoopChecker() =
default;
871 std::optional<DefinedSVal> KnownCollection =
873 if (!KnownCollection)
877 std::tie(StNonNil, StNil) = State->assume(*KnownCollection);
878 if (StNil && !StNonNil) {
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>();
917 SVal Val = State->getSVal(*ElementLoc);
918 return State->assume(cast<DefinedOrUnknownSVal>(Val),
true);
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;
938 SVal CountGreaterThanZeroVal =
941 SvalBuilder.
makeIntVal(0, (*CountS)->getType()),
943 std::optional<DefinedSVal> CountGreaterThanZero =
945 if (!CountGreaterThanZero) {
951 return State->assume(*CountGreaterThanZero, Assumption);
972 if (std::optional<BlockEdge> BE =
P.getAs<
BlockEdge>()) {
973 return BE->getSrc()->getLoopTarget() == FCS;
1002 C.generateSink(
C.getState(),
C.getPredecessor());
1003 else if (State !=
C.getState())
1004 C.addTransition(State);
1007bool ObjCLoopChecker::isCollectionCountMethod(
const ObjCMethodCall &M,
1011 if (!CountSelectorII)
1012 CountSelectorII = &
C.getASTContext().Idents.get(
"count");
1015 return S.isUnarySelector() &&
1016 (S.getIdentifierInfoForSlot(0) == CountSelectorII);
1019void ObjCLoopChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
1041 if (!isCollectionCountMethod(M,
C))
1045 SymbolRef CountS =
C.getSVal(MsgExpr).getAsSymbol();
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);
1057 C.addTransition(State);
1075 StaticClass = Message->getOriginExpr()->getReceiverInterface();
1096 return Message->getReceiverSVal().getAsSymbol();
1112 if (Sym == ImmutableReceiver)
1117 State = State->remove<ContainerCountMap>(Sym);
1118 State = State->remove<ContainerNonEmptyMap>(Sym);
1123void ObjCLoopChecker::checkDeadSymbols(
SymbolReaper &SymReaper,
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);
1143class ObjCNonNilReturnValueChecker
1144 :
public Checker<check::PostObjCMessage,
1145 check::PostStmt<ObjCArrayLiteral>,
1146 check::PostStmt<ObjCDictionaryLiteral>,
1147 check::PostStmt<ObjCBoxedExpr> > {
1150 mutable Selector ObjectAtIndexedSubscript;
1154 ObjCNonNilReturnValueChecker() =
default;
1160 C.addTransition(assumeExprIsNonNull(E,
C.getState(),
C));
1164 assumeExprIsNonNull(E,
C);
1167 assumeExprIsNonNull(E,
C);
1170 assumeExprIsNonNull(E,
C);
1178ObjCNonNilReturnValueChecker::assumeExprIsNonNull(
const Expr *NonNullExpr,
1181 SVal Val =
C.getSVal(NonNullExpr);
1182 if (std::optional<DefinedOrUnknownSVal> DV =
1184 return State->assume(*DV,
true);
1188void ObjCNonNilReturnValueChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
1196 ObjectAtIndexedSubscript =
GetUnarySelector(
"objectAtIndexedSubscript", Ctx);
1211 if (!
C.inTopFrame() && M.
getDecl() &&
1224 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
1238 C.addTransition(State);
1249bool ento::shouldRegisterNilArgChecker(
const CheckerManager &mgr) {
1257bool ento::shouldRegisterCFNumberChecker(
const CheckerManager &mgr) {
1265bool ento::shouldRegisterCFRetainReleaseChecker(
const CheckerManager &mgr) {
1273bool ento::shouldRegisterClassReleaseChecker(
const CheckerManager &mgr) {
1277void ento::registerVariadicMethodTypeChecker(
CheckerManager &mgr) {
1281bool ento::shouldRegisterVariadicMethodTypeChecker(
const CheckerManager &mgr) {
1289bool ento::shouldRegisterObjCLoopChecker(
const CheckerManager &mgr) {
1293void ento::registerObjCNonNilReturnValueChecker(
CheckerManager &mgr) {
1297bool ento::shouldRegisterObjCNonNilReturnValueChecker(
const CheckerManager &mgr) {
Defines the clang::ASTContext interface.
static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N, const ObjCForCollectionStmt *FCS)
If the fist block edge is a back edge, we are reentering the loop.
static ProgramStateRef checkCollectionNonNil(CheckerContext &C, ProgramStateRef State, const ObjCForCollectionStmt *FCS)
Assumes that the collection is non-nil.
static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID, bool IncludeSuperclasses=true)
static bool isKnownNonNilCollectionType(QualType T)
static ProgramStateRef assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, SymbolRef CollectionS, bool Assumption)
Returns NULL state if the collection is known to contain elements (or is known not to contain element...
static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call)
static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg)
static std::optional< uint64_t > GetCFNumberSize(ASTContext &Ctx, uint64_t i)
static ProgramStateRef checkElementNonNil(CheckerContext &C, ProgramStateRef State, const ObjCForCollectionStmt *FCS)
Assumes that the collection elements are non-nil.
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
Defines the Objective-C statement AST node classes.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
CanQualType getCanonicalType(QualType T) const
Return the canonical (structural) type corresponding to the specified potentially non-canonical type ...
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
DeclContext * getDeclContext()
The return type of classify().
This represents one expression.
Represents a function declaration or definition.
One of these records is kept for each identifier that is lexed.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
ObjCArrayLiteral - used for objective-c array containers; as in: @["Hello", NSApp,...
Expr * getElement(unsigned Index)
getElement - Return the Element at the specified index.
unsigned getNumElements() const
getNumElements - Return number of elements of objective-c array literal.
ObjCBoxedExpr - used for generalized expression boxing.
ObjCDictionaryLiteral - AST node to represent objective-c dictionary literals; as in:"name" : NSUserN...
unsigned getNumElements() const
getNumElements - Return number of elements of objective-c dictionary literal.
ObjCDictionaryElement getKeyValueElement(unsigned Index) const
Represents Objective-C's collection statement.
Represents an ObjC class declaration.
ObjCMethodDecl - Represents an instance or class method declaration.
ObjCMethodFamily getMethodFamily() const
Determines the family of this method.
ObjCInterfaceDecl * getClassInterface()
Represents a pointer to an Objective C object.
ObjCInterfaceDecl * getInterfaceDecl() const
If this pointer points to an Objective @interface type, gets the declaration for that interface.
A (possibly-)qualified type.
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine(), unsigned Indentation=0) const
Smart pointer class that efficiently represents Objective-C method names.
void print(llvm::raw_ostream &OS) const
Prints the full selector name (e.g. "foo:bar:").
unsigned getNumArgs() const
A trivial tuple used to represent a source range.
Stmt - This represents one statement.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
bool isBlockPointerType() const
bool isIntegralOrEnumerationType() const
Determine whether this type is an integral or enumeration type.
bool isObjCObjectPointerType() const
const T * getAs() const
Member-template getAs<specific type>'.
Represents a variable declaration or definition.
const Expr * getInit() const
An immutable set of CallDescriptions.
Represents an abstract call to a function or method along a particular path.
virtual SourceRange getArgSourceRange(unsigned Index) const
Returns the source range for errors associated with this argument.
virtual SVal getArgSVal(unsigned Index) const
Returns the value of a given argument at the time of the call.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
static bool hasMoreIteration(ProgramStateRef State, const ObjCForCollectionStmt *O, const LocationContext *LC)
Represents any expression that calls an Objective-C method.
const ObjCMethodDecl * getDecl() const override
Returns the declaration of the function or method that will be called.
bool isInstanceMessage() const
const Expr * getArgExpr(unsigned Index) const override
Returns the expression associated with a given argument.
ObjCMessageKind getMessageKind() const
Returns how the message was written in the source (property access, subscript, or explicit message se...
unsigned getNumArgs() const override
Returns the number of arguments (explicit and implicit).
const ObjCMessageExpr * getOriginExpr() const override
Returns the expression whose value will be the result of this call.
SourceRange getSourceRange() const override
Returns a source range for the entire call, suitable for outputting in diagnostics.
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
const ObjCInterfaceDecl * getReceiverInterface() const
Get the interface for the receiver.
bool isReceiverSelfOrSuper() const
Checks if the receiver refers to 'self' or 'super'.
Selector getSelector() const
A Range represents the closed range [from, to].
nonloc::ConcreteInt makeIntVal(const IntegerLiteral *integer)
QualType getConditionType() const
SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type)
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
A class responsible for cleaning up unused symbols.
bool isDead(SymbolRef sym)
Returns whether or not a symbol has been confirmed dead.
TypedValueRegion - An abstract class representing regions having a typed value.
virtual QualType getValueType() const =0
Represents symbolic expression that isn't a location.
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
bool isCFObjectRef(QualType T)
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
bool Null(InterpState &S, CodePtr OpPC)
@ NonNull
Values of this type can never be null.
Selector GetUnarySelector(StringRef name, ASTContext &Ctx)
Utility function for constructing an unary selector.
Selector GetNullarySelector(StringRef name, ASTContext &Ctx)
Utility function for constructing a nullary selector.
@ Interface
The "__interface" keyword introduces the elaborated-type-specifier.
@ Class
The "class" keyword introduces the elaborated-type-specifier.
static Selector getKeywordSelector(ASTContext &Ctx, IdentifierInfos *... IIs)
YAML serialization mapping.
An element in an Objective-C dictionary literal.