33 #include "llvm/ADT/SmallString.h"
34 #include "llvm/ADT/StringMap.h"
35 #include "llvm/Support/raw_ostream.h"
37 using namespace clang;
41 class APIMisuse :
public BugType {
43 APIMisuse(
const CheckerBase *checker,
const char *name)
44 :
BugType(checker, name,
"API Misuse (Apple)") {}
54 return ID->getIdentifier()->getName();
70 bool IncludeSuperclasses =
true) {
71 static llvm::StringMap<FoundationClass> Classes;
72 if (Classes.empty()) {
84 if (result ==
FC_None && IncludeSuperclasses)
96 class NilArgChecker :
public Checker<check::PreObjCMessage,
97 check::PostStmt<ObjCDictionaryLiteral>,
98 check::PostStmt<ObjCArrayLiteral> > {
99 mutable std::unique_ptr<APIMisuse> BT;
101 mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors;
102 mutable Selector ArrayWithObjectSel;
104 mutable Selector InsertObjectAtIndexSel;
105 mutable Selector ReplaceObjectAtIndexWithObjectSel;
106 mutable Selector SetObjectAtIndexedSubscriptSel;
107 mutable Selector ArrayByAddingObjectSel;
108 mutable Selector DictionaryWithObjectForKeySel;
109 mutable Selector SetObjectForKeySel;
110 mutable Selector SetObjectForKeyedSubscriptSel;
111 mutable Selector RemoveObjectForKeySel;
113 void warnIfNilExpr(
const Expr *
E,
120 bool CanBeSubscript =
false)
const;
137 void NilArgChecker::warnIfNilExpr(
const Expr *
E,
141 if (State->isNull(C.
getSVal(E)).isConstrainedTrue()) {
144 generateBugReport(N, Msg, E->getSourceRange(),
E,
C);
154 bool CanBeSubscript)
const {
157 if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
162 llvm::raw_svector_ostream os(sbuf);
167 os <<
"Array element cannot be nil";
170 os <<
"Value stored into '";
177 llvm_unreachable(
"Missing foundation class for the subscript expr");
182 os <<
"Value argument ";
185 os <<
"Key argument ";
189 os <<
"' cannot be nil";
193 os <<
"' cannot be nil";
197 generateBugReport(N, os.str(), msg.getArgSourceRange(Arg),
208 BT.reset(
new APIMisuse(
this,
"nil argument"));
210 auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
216 void NilArgChecker::checkPreObjCMessage(
const ObjCMethodCall &msg,
224 static const unsigned InvalidArgIndex =
UINT_MAX;
225 unsigned Arg = InvalidArgIndex;
226 bool CanBeSubscript =
false;
234 if (StringSelectors.empty()) {
252 StringSelectors[KnownSel] = 0;
254 auto I = StringSelectors.find(S);
255 if (
I == StringSelectors.end())
264 if (ArrayWithObjectSel.isNull()) {
268 InsertObjectAtIndexSel =
270 ReplaceObjectAtIndexWithObjectSel =
272 SetObjectAtIndexedSubscriptSel =
274 ArrayByAddingObjectSel =
278 if (S == ArrayWithObjectSel || S == AddObjectSel ||
279 S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) {
281 }
else if (S == SetObjectAtIndexedSubscriptSel) {
283 CanBeSubscript =
true;
284 }
else if (S == ReplaceObjectAtIndexWithObjectSel) {
293 if (DictionaryWithObjectForKeySel.isNull()) {
295 DictionaryWithObjectForKeySel =
299 SetObjectForKeyedSubscriptSel =
301 RemoveObjectForKeySel =
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);
344 class CFNumberCreateChecker :
public Checker< check::PreStmt<CallExpr> > {
345 mutable std::unique_ptr<APIMisuse> BT;
348 CFNumberCreateChecker() : II(nullptr) {}
354 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
378 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
381 return FixedSize[i-1];
405 static const char* GetCFNumberTypeStr(uint64_t i) {
406 static const char* Names[] = {
407 "kCFNumberSInt8Type",
408 "kCFNumberSInt16Type",
409 "kCFNumberSInt32Type",
410 "kCFNumberSInt64Type",
411 "kCFNumberFloat32Type",
412 "kCFNumberFloat64Type",
414 "kCFNumberShortType",
417 "kCFNumberLongLongType",
418 "kCFNumberFloatType",
419 "kCFNumberDoubleType",
420 "kCFNumberCFIndexType",
421 "kCFNumberNSIntegerType",
422 "kCFNumberCGFloatType"
429 void CFNumberCreateChecker::checkPreStmt(
const CallExpr *CE,
445 SVal TheTypeVal = state->getSVal(CE->
getArg(1), LCtx);
453 uint64_t NumberKind = V->getValue().getLimitedValue();
460 uint64_t TargetSize = *OptTargetSize;
465 SVal TheValueExpr = state->getSVal(CE->
getArg(2), LCtx);
488 if (SourceSize == TargetSize)
501 llvm::raw_svector_ostream os(sbuf);
503 os << (SourceSize == 8 ?
"An " :
"A ")
504 << SourceSize <<
" bit integer is used to initialize a CFNumber "
505 "object that represents "
506 << (TargetSize == 8 ?
"an " :
"a ")
507 << TargetSize <<
" bit integer. ";
509 if (SourceSize < TargetSize)
510 os << (TargetSize - SourceSize)
511 <<
" bits of the CFNumber value will be garbage." ;
513 os << (SourceSize - TargetSize)
514 <<
" bits of the input integer will be lost.";
517 BT.reset(
new APIMisuse(
this,
"Bad use of CFNumberCreate"));
519 auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
520 report->addRange(CE->
getArg(2)->getSourceRange());
530 class CFRetainReleaseChecker :
public Checker< check::PreStmt<CallExpr> > {
531 mutable std::unique_ptr<APIMisuse> BT;
534 CFRetainReleaseChecker()
542 void CFRetainReleaseChecker::checkPreStmt(
const CallExpr *CE,
559 BT.reset(
new APIMisuse(
560 this,
"null passed to CF memory management function"));
565 if (!(FuncII == Retain || FuncII == Release || FuncII ==
MakeCollectable ||
589 std::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
591 if (stateTrue && !stateFalse) {
596 const char *description;
597 if (FuncII == Retain)
598 description =
"Null pointer argument in call to CFRetain";
599 else if (FuncII == Release)
600 description =
"Null pointer argument in call to CFRelease";
602 description =
"Null pointer argument in call to CFMakeCollectable";
604 description =
"Null pointer argument in call to CFAutorelease";
606 llvm_unreachable(
"impossible case");
608 auto report = llvm::make_unique<BugReport>(*BT, description, N);
609 report->addRange(Arg->getSourceRange());
624 class ClassReleaseChecker :
public Checker<check::PreObjCMessage> {
629 mutable std::unique_ptr<BugType> BT;
636 void ClassReleaseChecker::checkPreObjCMessage(
const ObjCMethodCall &msg,
640 BT.reset(
new APIMisuse(
641 this,
"message incorrectly sent to class instead of class instance"));
656 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
661 llvm::raw_svector_ostream os(buf);
665 os <<
"' message should be sent to instances "
666 "of class '" << Class->
getName()
667 <<
"' and not the class directly";
669 auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
681 class VariadicMethodTypeChecker :
public Checker<check::PreObjCMessage> {
683 mutable Selector dictionaryWithObjectsAndKeysS;
685 mutable Selector orderedSetWithObjectsS;
687 mutable Selector initWithObjectsAndKeysS;
688 mutable std::unique_ptr<BugType> BT;
700 VariadicMethodTypeChecker::isVariadicMessage(
const ObjCMethodCall &msg)
const {
721 return S == initWithObjectsS;
723 return S == initWithObjectsAndKeysS;
732 return S == arrayWithObjectsS;
734 return S == orderedSetWithObjectsS;
736 return S == setWithObjectsS;
738 return S == dictionaryWithObjectsAndKeysS;
745 void VariadicMethodTypeChecker::checkPreObjCMessage(
const ObjCMethodCall &msg,
748 BT.reset(
new APIMisuse(
this,
749 "Arguments passed to variadic method aren't all "
750 "Objective-C pointer types"));
754 dictionaryWithObjectsAndKeysS =
763 if (!isVariadicMessage(msg))
772 unsigned variadicArgsEnd = msg.
getNumArgs() - 1;
774 if (variadicArgsEnd <= variadicArgsBegin)
780 for (
unsigned I = variadicArgsBegin;
I != variadicArgsEnd; ++
I) {
802 if (!errorNode.hasValue())
805 if (!errorNode.getValue())
809 llvm::raw_svector_ostream os(sbuf);
812 if (!TypeName.empty())
813 os <<
"Argument to '" << TypeName <<
"' method '";
815 os <<
"Argument to method '";
818 os <<
"' should be an Objective-C pointer type, not '";
822 auto R = llvm::make_unique<BugReport>(*BT, os.str(), errorNode.getValue());
823 R->addRange(msg.getArgSourceRange(
I));
838 class ObjCLoopChecker
839 :
public Checker<check::PostStmt<ObjCForCollectionStmt>,
840 check::PostObjCMessage,
842 check::PointerEscape > {
849 ObjCLoopChecker() : CountSelectorII(nullptr) {}
893 if (!KnownCollection)
897 std::tie(StNonNil, StNil) = State->assume(*KnownCollection);
898 if (StNil && !StNonNil) {
925 if (
const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
926 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
927 assert(ElemDecl->
getInit() ==
nullptr);
928 ElementLoc = State->getLValue(ElemDecl, LCtx);
930 ElementLoc = State->getSVal(Element, LCtx).getAs<
Loc>();
937 SVal Val = State->getSVal(*ElementLoc);
945 SymbolRef CollectionS,
bool Assumption) {
946 if (!State || !CollectionS)
949 const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS);
951 const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS);
953 return State->set<ContainerNonEmptyMap>(CollectionS, Assumption);
954 return (Assumption == *KnownNonEmpty) ? State :
nullptr;
958 SVal CountGreaterThanZeroVal =
961 SvalBuilder.
makeIntVal(0, (*CountS)->getType()),
965 if (!CountGreaterThanZero) {
971 return State->assume(*CountGreaterThanZero, Assumption);
995 return BE->getSrc()->getLoopTarget() == FCS;
1031 bool ObjCLoopChecker::isCollectionCountMethod(
const ObjCMethodCall &M,
1035 if (!CountSelectorII)
1043 void ObjCLoopChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
1065 if (!isCollectionCountMethod(M, C))
1074 State = State->set<ContainerCountMap>(ContainerS, CountS);
1076 if (
const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) {
1077 State = State->remove<ContainerNonEmptyMap>(ContainerS);
1087 const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call);
1132 for (InvalidatedSymbols::const_iterator
I = Escaped.begin(),
1141 if (Sym == ImmutableReceiver)
1146 State = State->remove<ContainerCountMap>(Sym);
1147 State = State->remove<ContainerNonEmptyMap>(Sym);
1152 void ObjCLoopChecker::checkDeadSymbols(
SymbolReaper &SymReaper,
1157 ContainerCountMapTy Tracked = State->get<ContainerCountMap>();
1159 E = Tracked.end();
I !=
E; ++
I) {
1161 if (SymReaper.
isDead(Sym)) {
1162 State = State->remove<ContainerCountMap>(Sym);
1163 State = State->remove<ContainerNonEmptyMap>(Sym);
1174 class ObjCNonNilReturnValueChecker
1175 :
public Checker<check::PostObjCMessage,
1176 check::PostStmt<ObjCArrayLiteral>,
1177 check::PostStmt<ObjCDictionaryLiteral>,
1178 check::PostStmt<ObjCBoxedExpr> > {
1181 mutable Selector ObjectAtIndexedSubscript;
1195 assumeExprIsNonNull(E, C);
1198 assumeExprIsNonNull(E, C);
1201 assumeExprIsNonNull(E, C);
1209 ObjCNonNilReturnValueChecker::assumeExprIsNonNull(
const Expr *NonNullExpr,
1214 return State->assume(*DV,
true);
1218 void ObjCNonNilReturnValueChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
1226 ObjectAtIndexedSubscript =
GetUnarySelector(
"objectAtIndexedSubscript", Ctx);
1254 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
1291 void ento::registerVariadicMethodTypeChecker(
CheckerManager &mgr) {
1300 ento::registerObjCNonNilReturnValueChecker(
CheckerManager &mgr) {
Defines the clang::ASTContext interface.
static Selector GetNullarySelector(StringRef name, ASTContext &Ctx)
Utility function for constructing a nullary selector.
FunctionDecl - An instance of this class is created to represent a function declaration or definition...
TypedValueRegion - An abstract class representing regions having a typed value.
nonloc::ConcreteInt makeIntVal(const IntegerLiteral *integer)
StringRef getName() const
getName - Get the name of identifier for this declaration as a StringRef.
Smart pointer class that efficiently represents Objective-C method names.
A (possibly-)qualified type.
ExplodedNode * generateErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
ObjCInterfaceDecl * getClassInterface()
bool isInstanceMessage() const
static Selector GetUnarySelector(StringRef name, ASTContext &Ctx)
Utility function for constructing an unary selector.
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...
IdentifierInfo * getIdentifier() const
getIdentifier - Get the identifier that names this declaration, if there is one.
The argument acts as if has been passed to CFMakeCollectable, which transfers the object to the Garba...
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
virtual QualType getValueType() const =0
static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg)
const Expr * getInit() const
SourceRange getSourceRange() const override
static Selector getKeywordSelector(ASTContext &Ctx, va_list argp)
bool isBlockPointerType() const
Value representing integer constant.
static ProgramStateRef checkCollectionNonNil(CheckerContext &C, ProgramStateRef State, const ObjCForCollectionStmt *FCS)
Assumes that the collection is non-nil.
ObjCDictionaryElement getKeyValueElement(unsigned Index) const
VarDecl - An instance of this class is created to represent a variable declaration or definition...
bool isReceiverSelfOrSuper() const
Checks if the receiver refers to 'self' or 'super'.
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
ObjCMethodDecl - Represents an instance or class method declaration.
ExplodedNode * getPredecessor()
Returns the previous node in the exploded graph, which includes the state of the program before the c...
Defines the Objective-C statement AST node classes.
bool isZeroConstant() const
const ObjCInterfaceDecl * getReceiverInterface() const
Get the interface for the receiver.
One of these records is kept for each identifier that is lexed.
An element in an Objective-C dictionary literal.
IdentifierInfo * getIdentifierInfoForSlot(unsigned argIndex) const
Retrieve the identifier at a given position in the selector.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
const FunctionDecl * getCalleeDecl(const CallExpr *CE) const
Get the declaration of the called function (path-sensitive).
The argument is treated as if an -autorelease message had been sent to the referenced object...
static ProgramStateRef checkElementNonNil(CheckerContext &C, ProgramStateRef State, const ObjCForCollectionStmt *FCS)
Assumes that the collection elements are non-nil.
void addSymbolDependency(const SymbolRef Primary, const SymbolRef Dependent)
Add artificial symbol dependency.
ObjCArrayLiteral - used for objective-c array containers; as in: @["Hello", NSApp, [NSNumber numberWithInt:42]];.
Represents any expression that calls an Objective-C method.
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
ObjCMethodFamily getMethodFamily() const
Determines the family of this method.
Expr * Key
The key for the dictionary element.
void print(llvm::raw_ostream &OS) const
Prints the full selector name (e.g. "foo:bar:").
Represents an ObjC class declaration.
detail::InMemoryDirectory::const_iterator I
static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call)
static bool isKnownNonNilCollectionType(QualType T)
The return type of classify().
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
ID
Defines the set of possible language-specific address spaces.
bool isUnarySelector() const
bool inTopFrame() const
Return true if the current LocationContext has no caller context.
bool isDead(SymbolRef sym) const
Returns whether or not a symbol has been confirmed dead.
DefinedOrUnknownSVal makeZeroVal(QualType type)
Construct an SVal representing '0' for the specified type.
Expr - This represents one expression.
static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID, bool IncludeSuperclasses=true)
StringRef getName() const
Return the actual identifier string.
const ProgramStateRef & getState() const
unsigned getNumArgs() const
Optional< T > getAs() const
Convert to the specified SVal type, returning None if this SVal is not of the desired type...
ObjCDictionaryLiteral - AST node to represent objective-c dictionary literals; as in:"name" : NSUserN...
DeclContext * getDeclContext()
ObjCInterfaceDecl * getReceiverInterface() const
Retrieve the Objective-C interface to which this message is being directed, if known.
SymbolManager & getSymbolManager()
unsigned getNumElements() const
getNumElements - Return number of elements of objective-c array literal.
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine()) const
Expr * getElement(unsigned Index)
getExpr - Return the Expr at the specified index.
bool isCFObjectRef(QualType T)
SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type)
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
QualType getConditionType() const
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
bool isIntegralOrEnumerationType() const
Determine whether this type is an integral or enumeration type.
CHECKER * registerChecker()
Used to register checkers.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
const TemplateArgument * iterator
ExplodedNode * generateSink(ProgramStateRef State, ExplodedNode *Pred, const ProgramPointTag *Tag=nullptr)
Generate a sink node.
DeclStmt - Adaptor class for mixing declarations with statements and expressions. ...
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
Selector getSelector() const
A class responsible for cleaning up unused symbols.
ObjCBoxedExpr - used for generalized expression boxing.
const ObjCMethodDecl * getDecl() const override
Expr * Value
The value of the dictionary element.
ASTContext & getASTContext()
static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N, const ObjCForCollectionStmt *FCS)
If the fist block edge is a back edge, we are reentering the loop.
Represents symbolic expression.
detail::InMemoryDirectory::const_iterator E
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
CanQualType getCanonicalType(QualType T) const
Return the canonical (structural) type corresponding to the specified potentially non-canonical type ...
Represents an abstract call to a function or method along a particular path.
Optional< T > getAs() const
Convert to the specified ProgramPoint type, returning None if this ProgramPoint is not of the desired...
ObjCMessageKind getMessageKind() const
Returns how the message was written in the source (property access, subscript, or explicit message se...
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
Represents a pointer to an Objective C object.
const LangOptions & getLangOpts() const
const ExplodedNode *const * const_pred_iterator
const T * getAs() const
Member-template getAs<specific type>'.
Represents Objective-C's collection statement.
ObjCInterfaceDecl * getInterfaceDecl() const
If this pointer points to an Objective @interface type, gets the declaration for that interface...
unsigned getNumArgs() const override
const Expr * getArgExpr(unsigned Index) const override
DefinedOrUnknownSVal evalEQ(ProgramStateRef state, DefinedOrUnknownSVal lhs, DefinedOrUnknownSVal rhs)
bool isObjCObjectPointerType() const
bool trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S, BugReport &R, bool IsArg=false, bool EnableNullFPSuppression=true)
Attempts to add visitors to trace a null or undefined value back to its point of origin, whether it is a symbol constrained to null or an explicit assignment.
pred_iterator pred_begin()
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
SValBuilder & getSValBuilder()
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
ObjCInterfaceDecl * getSuperClass() const
static Optional< uint64_t > GetCFNumberSize(ASTContext &Ctx, uint64_t i)
A trivial tuple used to represent a source range.
static bool isObjCNSObjectType(QualType Ty)
Return true if this is an NSObject object with its NSObject attribute set.
virtual const ObjCMessageExpr * getOriginExpr() const
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
unsigned getNumElements() const
getNumElements - Return number of elements of objective-c dictionary literal.
TypedRegion - An abstract class representing regions that are typed.
const LocationContext * getLocationContext() const
SVal getSVal(const Stmt *S) const
Get the value of arbitrary expressions at this point in the path.