33#include "llvm/ADT/SmallString.h"
34#include "llvm/ADT/StringMap.h"
35#include "llvm/Support/raw_ostream.h"
43class APIMisuse :
public BugType {
45 APIMisuse(
const CheckerBase *checker,
const char *name)
56 return ID->getIdentifier()->getName();
72 bool IncludeSuperclasses =
true) {
73 static llvm::StringMap<FoundationClass> Classes;
74 if (Classes.empty()) {
85 FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());
86 if (result ==
FC_None && IncludeSuperclasses)
98 class NilArgChecker :
public Checker<check::PreObjCMessage,
99 check::PostStmt<ObjCDictionaryLiteral>,
100 check::PostStmt<ObjCArrayLiteral> > {
101 mutable std::unique_ptr<APIMisuse> BT;
103 mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors;
104 mutable Selector ArrayWithObjectSel;
106 mutable Selector InsertObjectAtIndexSel;
107 mutable Selector ReplaceObjectAtIndexWithObjectSel;
108 mutable Selector SetObjectAtIndexedSubscriptSel;
109 mutable Selector ArrayByAddingObjectSel;
110 mutable Selector DictionaryWithObjectForKeySel;
111 mutable Selector SetObjectForKeySel;
112 mutable Selector SetObjectForKeyedSubscriptSel;
113 mutable Selector RemoveObjectForKeySel;
115 void warnIfNilExpr(
const Expr *E,
122 bool CanBeSubscript =
false)
const;
139void NilArgChecker::warnIfNilExpr(
const Expr *E,
143 if (State->isNull(
C.getSVal(E)).isConstrainedTrue()) {
155 bool CanBeSubscript)
const {
158 if (!State->isNull(msg.
getArgSVal(Arg)).isConstrainedTrue())
168 llvm::raw_svector_ostream os(sbuf);
173 os <<
"Array element cannot be nil";
176 os <<
"Value stored into '";
183 llvm_unreachable(
"Missing foundation class for the subscript expr");
188 os <<
"Value argument ";
191 os <<
"Key argument ";
195 os <<
"' cannot be nil";
199 os <<
"' cannot be nil";
214 BT.reset(
new APIMisuse(
this,
"nil argument"));
216 auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
219 C.emitReport(std::move(R));
230 static const unsigned InvalidArgIndex =
UINT_MAX;
231 unsigned Arg = InvalidArgIndex;
232 bool CanBeSubscript =
false;
237 if (S.isUnarySelector())
240 if (StringSelectors.empty()) {
255 StringSelectors[KnownSel] = 0;
257 auto I = StringSelectors.find(S);
258 if (I == StringSelectors.end())
264 if (S.isUnarySelector())
267 if (ArrayWithObjectSel.isNull()) {
271 InsertObjectAtIndexSel =
273 ReplaceObjectAtIndexWithObjectSel =
275 SetObjectAtIndexedSubscriptSel =
280 if (S == ArrayWithObjectSel || S == AddObjectSel ||
281 S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) {
283 }
else if (S == SetObjectAtIndexedSubscriptSel) {
285 CanBeSubscript =
true;
286 }
else if (S == ReplaceObjectAtIndexWithObjectSel) {
292 if (S.isUnarySelector())
295 if (DictionaryWithObjectForKeySel.isNull()) {
297 DictionaryWithObjectForKeySel =
300 SetObjectForKeyedSubscriptSel =
305 if (S == DictionaryWithObjectForKeySel || S == SetObjectForKeySel) {
307 warnIfNilArg(
C, msg, 1, Class);
308 }
else if (S == SetObjectForKeyedSubscriptSel) {
309 CanBeSubscript =
true;
311 }
else if (S == RemoveObjectForKeySel) {
317 if ((Arg != InvalidArgIndex))
318 warnIfNilArg(
C, msg, Arg, Class, CanBeSubscript);
324 for (
unsigned i = 0; i < NumOfElements; ++i) {
325 warnIfNilExpr(AL->
getElement(i),
"Array element cannot be nil",
C);
332 for (
unsigned i = 0; i < NumOfElements; ++i) {
334 warnIfNilExpr(Element.
Key,
"Dictionary key cannot be nil",
C);
335 warnIfNilExpr(Element.
Value,
"Dictionary value cannot be nil",
C);
344class CFNumberChecker :
public Checker< check::PreStmt<CallExpr> > {
345 mutable std::unique_ptr<APIMisuse> BT;
348 CFNumberChecker() : ICreate(nullptr), IGetValue(nullptr) {}
374 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
377 return FixedSize[i-1];
401static const char* GetCFNumberTypeStr(uint64_t i) {
402 static const char* Names[] = {
403 "kCFNumberSInt8Type",
404 "kCFNumberSInt16Type",
405 "kCFNumberSInt32Type",
406 "kCFNumberSInt64Type",
407 "kCFNumberFloat32Type",
408 "kCFNumberFloat64Type",
410 "kCFNumberShortType",
413 "kCFNumberLongLongType",
414 "kCFNumberFloatType",
415 "kCFNumberDoubleType",
416 "kCFNumberCFIndexType",
417 "kCFNumberNSIntegerType",
418 "kCFNumberCGFloatType"
425void CFNumberChecker::checkPreStmt(
const CallExpr *CE,
434 ICreate = &Ctx.
Idents.
get(
"CFNumberCreate");
435 IGetValue = &Ctx.
Idents.
get(
"CFNumberGetValue");
446 std::optional<nonloc::ConcreteInt>
V =
447 dyn_cast<nonloc::ConcreteInt>(TheTypeVal);
451 uint64_t NumberKind =
V->getValue().getLimitedValue();
452 std::optional<uint64_t> OptCFNumberSize =
GetCFNumberSize(Ctx, NumberKind);
455 if (!OptCFNumberSize)
458 uint64_t CFNumberSize = *OptCFNumberSize;
485 if (PrimitiveTypeSize == CFNumberSize)
493 llvm::raw_svector_ostream os(sbuf);
497 os << (PrimitiveTypeSize == 8 ?
"An " :
"A ")
498 << PrimitiveTypeSize <<
"-bit integer is used to initialize a "
499 <<
"CFNumber object that represents "
500 << (CFNumberSize == 8 ?
"an " :
"a ")
501 << CFNumberSize <<
"-bit integer; ";
503 os <<
"A CFNumber object that represents "
504 << (CFNumberSize == 8 ?
"an " :
"a ")
505 << CFNumberSize <<
"-bit integer is used to initialize "
506 << (PrimitiveTypeSize == 8 ?
"an " :
"a ")
507 << PrimitiveTypeSize <<
"-bit integer; ";
510 if (PrimitiveTypeSize < CFNumberSize)
511 os << (CFNumberSize - PrimitiveTypeSize)
512 <<
" bits of the CFNumber value will "
513 << (isCreate ?
"be garbage." :
"overwrite adjacent storage.");
515 os << (PrimitiveTypeSize - CFNumberSize)
516 <<
" bits of the integer value will be "
517 << (isCreate ?
"lost." :
"garbage.");
520 BT.reset(
new APIMisuse(
this,
"Bad use of CFNumber APIs"));
522 auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
524 C.emitReport(std::move(report));
533class CFRetainReleaseChecker :
public Checker<check::PreCall> {
534 mutable APIMisuse BT{
this,
"null passed to CF memory management function"};
538 {{
"CFMakeCollectable"}, 1},
539 {{
"CFAutorelease"}, 1},
547void CFRetainReleaseChecker::checkPreCall(
const CallEvent &Call,
550 if (!
Call.isGlobalCFunction())
554 if (!ModelledCalls.contains(Call))
566 std::tie(stateNonNull, stateNull) = state->assume(*DefArgVal);
574 raw_svector_ostream
OS(Str);
575 OS <<
"Null pointer argument in call to "
576 << cast<FunctionDecl>(
Call.getDecl())->getName();
578 auto report = std::make_unique<PathSensitiveBugReport>(BT,
OS.str(), N);
579 report->addRange(
Call.getArgSourceRange(0));
581 C.emitReport(std::move(report));
586 C.addTransition(stateNonNull);
594class ClassReleaseChecker :
public Checker<check::PreObjCMessage> {
599 mutable std::unique_ptr<BugType> BT;
606void ClassReleaseChecker::checkPreObjCMessage(
const ObjCMethodCall &msg,
609 BT.reset(
new APIMisuse(
610 this,
"message incorrectly sent to class instead of class instance"));
625 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
630 llvm::raw_svector_ostream os(buf);
634 os <<
"' message should be sent to instances "
635 "of class '" <<
Class->getName()
636 <<
"' and not the class directly";
638 auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
640 C.emitReport(std::move(report));
650class VariadicMethodTypeChecker :
public Checker<check::PreObjCMessage> {
652 mutable Selector dictionaryWithObjectsAndKeysS;
654 mutable Selector orderedSetWithObjectsS;
656 mutable Selector initWithObjectsAndKeysS;
657 mutable std::unique_ptr<BugType> BT;
669VariadicMethodTypeChecker::isVariadicMessage(
const ObjCMethodCall &msg)
const {
690 return S == initWithObjectsS;
692 return S == initWithObjectsAndKeysS;
701 return S == arrayWithObjectsS;
703 return S == orderedSetWithObjectsS;
705 return S == setWithObjectsS;
707 return S == dictionaryWithObjectsAndKeysS;
714void VariadicMethodTypeChecker::checkPreObjCMessage(
const ObjCMethodCall &msg,
717 BT.reset(
new APIMisuse(
this,
718 "Arguments passed to variadic method aren't all "
719 "Objective-C pointer types"));
723 dictionaryWithObjectsAndKeysS =
732 if (!isVariadicMessage(msg))
741 unsigned variadicArgsEnd = msg.
getNumArgs() - 1;
743 if (variadicArgsEnd <= variadicArgsBegin)
747 std::optional<ExplodedNode *> errorNode;
749 for (
unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
763 if (
C.getASTContext().isObjCNSObjectType(ArgTy))
772 errorNode =
C.generateNonFatalErrorNode();
778 llvm::raw_svector_ostream os(sbuf);
782 os <<
"Argument to '" <<
TypeName <<
"' method '";
784 os <<
"Argument to method '";
787 os <<
"' should be an Objective-C pointer type, not '";
788 ArgTy.
print(os,
C.getLangOpts());
792 std::make_unique<PathSensitiveBugReport>(*BT, os.str(), *errorNode);
794 C.emitReport(std::move(R));
809 :
public Checker<check::PostStmt<ObjCForCollectionStmt>,
810 check::PostObjCMessage,
812 check::PointerEscape > {
819 ObjCLoopChecker() : CountSelectorII(nullptr) {}
862 std::optional<DefinedSVal> KnownCollection =
864 if (!KnownCollection)
868 std::tie(StNonNil, StNil) = State->assume(*KnownCollection);
869 if (StNil && !StNonNil) {
895 std::optional<Loc> ElementLoc;
896 if (
const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
897 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
898 assert(ElemDecl->
getInit() ==
nullptr);
899 ElementLoc = State->getLValue(ElemDecl, LCtx);
901 ElementLoc = State->getSVal(Element, LCtx).getAs<
Loc>();
908 SVal Val = State->getSVal(*ElementLoc);
909 return State->assume(cast<DefinedOrUnknownSVal>(Val),
true);
916 SymbolRef CollectionS,
bool Assumption) {
917 if (!State || !CollectionS)
920 const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS);
922 const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS);
924 return State->set<ContainerNonEmptyMap>(CollectionS, Assumption);
925 return (Assumption == *KnownNonEmpty) ? State :
nullptr;
929 SVal CountGreaterThanZeroVal =
932 SvalBuilder.
makeIntVal(0, (*CountS)->getType()),
934 std::optional<DefinedSVal> CountGreaterThanZero =
936 if (!CountGreaterThanZero) {
942 return State->assume(*CountGreaterThanZero, Assumption);
963 if (std::optional<BlockEdge> BE =
P.getAs<
BlockEdge>()) {
964 return BE->getSrc()->getLoopTarget() == FCS;
994 C.generateSink(
C.getState(),
C.getPredecessor());
995 else if (State !=
C.getState())
996 C.addTransition(State);
999bool ObjCLoopChecker::isCollectionCountMethod(
const ObjCMethodCall &M,
1003 if (!CountSelectorII)
1004 CountSelectorII = &
C.getASTContext().Idents.get(
"count");
1007 return S.isUnarySelector() &&
1008 (S.getIdentifierInfoForSlot(0) == CountSelectorII);
1011void ObjCLoopChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
1033 if (!isCollectionCountMethod(M,
C))
1037 SymbolRef CountS =
C.getSVal(MsgExpr).getAsSymbol();
1041 C.getSymbolManager().addSymbolDependency(ContainerS, CountS);
1042 State = State->set<ContainerCountMap>(ContainerS, CountS);
1044 if (
const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) {
1045 State = State->remove<ContainerNonEmptyMap>(ContainerS);
1049 C.addTransition(State);
1054 const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call);
1067 StaticClass = Message->getOriginExpr()->getReceiverInterface();
1088 return Message->getReceiverSVal().getAsSymbol();
1099 for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
1108 if (Sym == ImmutableReceiver)
1113 State = State->remove<ContainerCountMap>(Sym);
1114 State = State->remove<ContainerNonEmptyMap>(Sym);
1119void ObjCLoopChecker::checkDeadSymbols(
SymbolReaper &SymReaper,
1124 ContainerCountMapTy Tracked = State->get<ContainerCountMap>();
1125 for (ContainerCountMapTy::iterator I = Tracked.begin(),
1126 E = Tracked.end(); I != E; ++I) {
1128 if (SymReaper.
isDead(Sym)) {
1129 State = State->remove<ContainerCountMap>(Sym);
1130 State = State->remove<ContainerNonEmptyMap>(Sym);
1134 C.addTransition(State);
1141class ObjCNonNilReturnValueChecker
1142 :
public Checker<check::PostObjCMessage,
1143 check::PostStmt<ObjCArrayLiteral>,
1144 check::PostStmt<ObjCDictionaryLiteral>,
1145 check::PostStmt<ObjCBoxedExpr> > {
1148 mutable Selector ObjectAtIndexedSubscript;
1158 C.addTransition(assumeExprIsNonNull(E,
C.getState(),
C));
1162 assumeExprIsNonNull(E,
C);
1165 assumeExprIsNonNull(E,
C);
1168 assumeExprIsNonNull(E,
C);
1176ObjCNonNilReturnValueChecker::assumeExprIsNonNull(
const Expr *NonNullExpr,
1179 SVal Val =
C.getSVal(NonNullExpr);
1180 if (std::optional<DefinedOrUnknownSVal> DV =
1182 return State->assume(*DV,
true);
1186void ObjCNonNilReturnValueChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
1194 ObjectAtIndexedSubscript =
GetUnarySelector(
"objectAtIndexedSubscript", Ctx);
1209 if (!
C.inTopFrame() && M.
getDecl() &&
1222 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
1236 C.addTransition(State);
1247bool ento::shouldRegisterNilArgChecker(
const CheckerManager &mgr) {
1255bool ento::shouldRegisterCFNumberChecker(
const CheckerManager &mgr) {
1263bool ento::shouldRegisterCFRetainReleaseChecker(
const CheckerManager &mgr) {
1271bool ento::shouldRegisterClassReleaseChecker(
const CheckerManager &mgr) {
1275void ento::registerVariadicMethodTypeChecker(
CheckerManager &mgr) {
1279bool ento::shouldRegisterVariadicMethodTypeChecker(
const CheckerManager &mgr) {
1287bool ento::shouldRegisterObjCLoopChecker(
const CheckerManager &mgr) {
1291void ento::registerObjCNonNilReturnValueChecker(
CheckerManager &mgr) {
1295bool 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.
pred_iterator pred_begin()
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
const ExplodedNode *const * const_pred_iterator
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 Call(InterpState &S, CodePtr &PC, const Function *Func)
@ C
Languages that the frontend can parse and compile.
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.
static Selector getKeywordSelector(ASTContext &Ctx, IdentifierInfos *... IIs)
YAML serialization mapping.
An element in an Objective-C dictionary literal.
Expr * Value
The value of the dictionary element.
Expr * Key
The key for the dictionary element.