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()) {
153 bool CanBeSubscript)
const {
156 if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
161 llvm::raw_svector_ostream os(sbuf);
166 os <<
"Array element cannot be nil";
169 os <<
"Value stored into '";
176 llvm_unreachable(
"Missing foundation class for the subscript expr");
181 os <<
"Value argument ";
184 os <<
"Key argument ";
188 os <<
"' cannot be nil";
192 os <<
"' cannot be nil";
196 generateBugReport(N, os.str(), msg.getArgSourceRange(Arg),
207 BT.reset(
new APIMisuse(
this,
"nil argument"));
209 auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
211 bugreporter::trackNullOrUndefValue(N, E, *R);
215 void NilArgChecker::checkPreObjCMessage(
const ObjCMethodCall &msg,
223 static const unsigned InvalidArgIndex =
UINT_MAX;
224 unsigned Arg = InvalidArgIndex;
225 bool CanBeSubscript =
false;
233 if (StringSelectors.empty()) {
248 StringSelectors[KnownSel] = 0;
250 auto I = StringSelectors.find(S);
251 if (I == StringSelectors.end())
260 if (ArrayWithObjectSel.isNull()) {
264 InsertObjectAtIndexSel =
266 ReplaceObjectAtIndexWithObjectSel =
268 SetObjectAtIndexedSubscriptSel =
273 if (S == ArrayWithObjectSel || S == AddObjectSel ||
274 S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) {
276 }
else if (S == SetObjectAtIndexedSubscriptSel) {
278 CanBeSubscript =
true;
279 }
else if (S == ReplaceObjectAtIndexWithObjectSel) {
288 if (DictionaryWithObjectForKeySel.isNull()) {
290 DictionaryWithObjectForKeySel =
293 SetObjectForKeyedSubscriptSel =
298 if (S == DictionaryWithObjectForKeySel || S == SetObjectForKeySel) {
300 warnIfNilArg(C, msg, 1, Class);
301 }
else if (S == SetObjectForKeyedSubscriptSel) {
302 CanBeSubscript =
true;
304 }
else if (S == RemoveObjectForKeySel) {
310 if ((Arg != InvalidArgIndex))
311 warnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
317 for (
unsigned i = 0; i < NumOfElements; ++i) {
318 warnIfNilExpr(AL->
getElement(i),
"Array element cannot be nil", C);
325 for (
unsigned i = 0; i < NumOfElements; ++i) {
327 warnIfNilExpr(Element.
Key,
"Dictionary key cannot be nil", C);
328 warnIfNilExpr(Element.
Value,
"Dictionary value cannot be nil", C);
337 class CFNumberChecker :
public Checker< check::PreStmt<CallExpr> > {
338 mutable std::unique_ptr<APIMisuse> BT;
341 CFNumberChecker() : ICreate(
nullptr), IGetValue(
nullptr) {}
347 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
371 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
374 return FixedSize[i-1];
398 static const char* GetCFNumberTypeStr(uint64_t i) {
399 static const char* Names[] = {
400 "kCFNumberSInt8Type",
401 "kCFNumberSInt16Type",
402 "kCFNumberSInt32Type",
403 "kCFNumberSInt64Type",
404 "kCFNumberFloat32Type",
405 "kCFNumberFloat64Type",
407 "kCFNumberShortType",
410 "kCFNumberLongLongType",
411 "kCFNumberFloatType",
412 "kCFNumberDoubleType",
413 "kCFNumberCFIndexType",
414 "kCFNumberNSIntegerType",
415 "kCFNumberCGFloatType" 422 void CFNumberChecker::checkPreStmt(
const CallExpr *CE,
431 ICreate = &Ctx.
Idents.
get(
"CFNumberCreate");
432 IGetValue = &Ctx.
Idents.
get(
"CFNumberGetValue");
447 uint64_t NumberKind = V->getValue().getLimitedValue();
451 if (!OptCFNumberSize)
454 uint64_t CFNumberSize = *OptCFNumberSize;
481 if (PrimitiveTypeSize == CFNumberSize)
489 llvm::raw_svector_ostream os(sbuf);
493 os << (PrimitiveTypeSize == 8 ?
"An " :
"A ")
494 << PrimitiveTypeSize <<
"-bit integer is used to initialize a " 495 <<
"CFNumber object that represents " 496 << (CFNumberSize == 8 ?
"an " :
"a ")
497 << CFNumberSize <<
"-bit integer; ";
499 os <<
"A CFNumber object that represents " 500 << (CFNumberSize == 8 ?
"an " :
"a ")
501 << CFNumberSize <<
"-bit integer is used to initialize " 502 << (PrimitiveTypeSize == 8 ?
"an " :
"a ")
503 << PrimitiveTypeSize <<
"-bit integer; ";
506 if (PrimitiveTypeSize < CFNumberSize)
507 os << (CFNumberSize - PrimitiveTypeSize)
508 <<
" bits of the CFNumber value will " 509 << (isCreate ?
"be garbage." :
"overwrite adjacent storage.");
511 os << (PrimitiveTypeSize - CFNumberSize)
512 <<
" bits of the integer value will be " 513 << (isCreate ?
"lost." :
"garbage.");
516 BT.reset(
new APIMisuse(
this,
"Bad use of CFNumber APIs"));
518 auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
529 class CFRetainReleaseChecker :
public Checker< check::PreStmt<CallExpr> > {
530 mutable std::unique_ptr<APIMisuse> BT;
534 CFRetainReleaseChecker()
541 void CFRetainReleaseChecker::checkPreStmt(
const CallExpr *CE,
558 BT.reset(
new APIMisuse(
559 this,
"null passed to CF memory management function"));
564 if (!(FuncII == Retain || FuncII == Release || FuncII ==
MakeCollectable ||
588 std::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
590 if (stateTrue && !stateFalse) {
595 const char *description;
596 if (FuncII == Retain)
597 description =
"Null pointer argument in call to CFRetain";
598 else if (FuncII == Release)
599 description =
"Null pointer argument in call to CFRelease";
601 description =
"Null pointer argument in call to CFMakeCollectable";
603 description =
"Null pointer argument in call to CFAutorelease";
605 llvm_unreachable(
"impossible case");
607 auto report = llvm::make_unique<BugReport>(*BT, description, N);
609 bugreporter::trackNullOrUndefValue(N, Arg, *report);
623 class ClassReleaseChecker :
public Checker<check::PreObjCMessage> {
628 mutable std::unique_ptr<BugType> BT;
635 void ClassReleaseChecker::checkPreObjCMessage(
const ObjCMethodCall &msg,
638 BT.reset(
new APIMisuse(
639 this,
"message incorrectly sent to class instead of class instance"));
654 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
659 llvm::raw_svector_ostream os(buf);
663 os <<
"' message should be sent to instances " 664 "of class '" << Class->
getName()
665 <<
"' and not the class directly";
667 auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
679 class VariadicMethodTypeChecker :
public Checker<check::PreObjCMessage> {
681 mutable Selector dictionaryWithObjectsAndKeysS;
683 mutable Selector orderedSetWithObjectsS;
685 mutable Selector initWithObjectsAndKeysS;
686 mutable std::unique_ptr<BugType> BT;
698 VariadicMethodTypeChecker::isVariadicMessage(
const ObjCMethodCall &msg)
const {
719 return S == initWithObjectsS;
721 return S == initWithObjectsAndKeysS;
730 return S == arrayWithObjectsS;
732 return S == orderedSetWithObjectsS;
734 return S == setWithObjectsS;
736 return S == dictionaryWithObjectsAndKeysS;
743 void VariadicMethodTypeChecker::checkPreObjCMessage(
const ObjCMethodCall &msg,
746 BT.reset(
new APIMisuse(
this,
747 "Arguments passed to variadic method aren't all " 748 "Objective-C pointer types"));
752 dictionaryWithObjectsAndKeysS =
761 if (!isVariadicMessage(msg))
770 unsigned variadicArgsEnd = msg.
getNumArgs() - 1;
772 if (variadicArgsEnd <= variadicArgsBegin)
778 for (
unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
800 if (!errorNode.hasValue())
803 if (!errorNode.getValue())
807 llvm::raw_svector_ostream os(sbuf);
810 if (!TypeName.empty())
811 os <<
"Argument to '" << TypeName <<
"' method '";
813 os <<
"Argument to method '";
816 os <<
"' should be an Objective-C pointer type, not '";
820 auto R = llvm::make_unique<BugReport>(*BT, os.str(), errorNode.getValue());
821 R->addRange(msg.getArgSourceRange(I));
836 class ObjCLoopChecker
837 :
public Checker<check::PostStmt<ObjCForCollectionStmt>,
838 check::PostObjCMessage,
840 check::PointerEscape > {
847 ObjCLoopChecker() : CountSelectorII(
nullptr) {}
891 if (!KnownCollection)
895 std::tie(StNonNil, StNil) = State->assume(*KnownCollection);
896 if (StNil && !StNonNil) {
923 if (
const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
924 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
925 assert(ElemDecl->
getInit() ==
nullptr);
926 ElementLoc = State->getLValue(ElemDecl, LCtx);
928 ElementLoc = State->getSVal(Element, LCtx).getAs<
Loc>();
935 SVal Val = State->getSVal(*ElementLoc);
943 SymbolRef CollectionS,
bool Assumption) {
944 if (!State || !CollectionS)
947 const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS);
949 const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS);
951 return State->set<ContainerNonEmptyMap>(CollectionS, Assumption);
952 return (Assumption == *KnownNonEmpty) ?
State :
nullptr;
956 SVal CountGreaterThanZeroVal =
959 SvalBuilder.
makeIntVal(0, (*CountS)->getType()),
963 if (!CountGreaterThanZero) {
969 return State->assume(*CountGreaterThanZero, Assumption);
991 return BE->getSrc()->getLoopTarget() == FCS;
1027 bool ObjCLoopChecker::isCollectionCountMethod(
const ObjCMethodCall &M,
1031 if (!CountSelectorII)
1039 void ObjCLoopChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
1061 if (!isCollectionCountMethod(M, C))
1070 State = State->set<ContainerCountMap>(ContainerS, CountS);
1072 if (
const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) {
1073 State = State->remove<ContainerNonEmptyMap>(ContainerS);
1082 const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call);
1127 for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
1136 if (Sym == ImmutableReceiver)
1141 State = State->remove<ContainerCountMap>(Sym);
1142 State = State->remove<ContainerNonEmptyMap>(Sym);
1147 void ObjCLoopChecker::checkDeadSymbols(
SymbolReaper &SymReaper,
1152 ContainerCountMapTy Tracked = State->get<ContainerCountMap>();
1153 for (ContainerCountMapTy::iterator I = Tracked.begin(),
1154 E = Tracked.end(); I != E; ++I) {
1156 if (SymReaper.
isDead(Sym)) {
1157 State = State->remove<ContainerCountMap>(Sym);
1158 State = State->remove<ContainerNonEmptyMap>(Sym);
1169 class ObjCNonNilReturnValueChecker
1170 :
public Checker<check::PostObjCMessage,
1171 check::PostStmt<ObjCArrayLiteral>,
1172 check::PostStmt<ObjCDictionaryLiteral>,
1173 check::PostStmt<ObjCBoxedExpr> > {
1176 mutable Selector ObjectAtIndexedSubscript;
1180 ObjCNonNilReturnValueChecker() :
Initialized(
false) {}
1190 assumeExprIsNonNull(E, C);
1193 assumeExprIsNonNull(E, C);
1196 assumeExprIsNonNull(E, C);
1204 ObjCNonNilReturnValueChecker::assumeExprIsNonNull(
const Expr *NonNullExpr,
1209 return State->assume(*DV,
true);
1213 void ObjCNonNilReturnValueChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
1221 ObjectAtIndexedSubscript =
GetUnarySelector(
"objectAtIndexedSubscript", Ctx);
1249 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
1286 void ento::registerVariadicMethodTypeChecker(
CheckerManager &mgr) {
1295 ento::registerObjCNonNilReturnValueChecker(
CheckerManager &mgr) {
Defines the clang::ASTContext interface.
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
Represents a function declaration or definition.
TypedValueRegion - An abstract class representing regions having a typed value.
nonloc::ConcreteInt makeIntVal(const IntegerLiteral *integer)
ObjCDictionaryElement getKeyValueElement(unsigned Index) const
Smart pointer class that efficiently represents Objective-C method names.
A (possibly-)qualified type.
bool isBlockPointerType() const
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()
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine(), unsigned Indentation=0) const
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...
Stmt - This represents one statement.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
ObjCInterfaceDecl * getReceiverInterface() const
Retrieve the Objective-C interface to which this message is being directed, if known.
The argument acts as if has been passed to CFMakeCollectable, which transfers the object to the Garba...
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)
SourceRange getSourceRange() const override
A Range represents the closed range [from, to].
Value representing integer constant.
static ProgramStateRef checkCollectionNonNil(CheckerContext &C, ProgramStateRef State, const ObjCForCollectionStmt *FCS)
Assumes that the collection is non-nil.
Represents a variable declaration or definition.
const T * getAs() const
Member-template getAs<specific type>'.
const FunctionDecl * getCalleeDecl(const CallExpr *CE) const
Get the declaration of the called function (path-sensitive).
ObjCMethodDecl - Represents an instance or class method declaration.
bool isDead(SymbolRef sym) const
Returns whether or not a symbol has been confirmed dead.
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.
SVal getSVal(const Stmt *S) const
Get the value of arbitrary expressions at this point in the path.
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
One of these records is kept for each identifier that is lexed.
An element in an Objective-C dictionary literal.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
static Selector getKeywordSelector(ASTContext &Ctx, IdentifierInfos *... IIs)
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.
const ObjCInterfaceDecl * getReceiverInterface() const
Get the interface for the receiver.
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]];.
i32 captured_struct **param SharedsTy A type which contains references the shared variables *param Shareds Context with the list of shared variables from the p *TaskFunction *param Data Additional data for task generation like final * state
bool isIntegralOrEnumerationType() const
Determine whether this type is an integral or enumeration type.
bool isUnarySelector() const
ObjCMethodFamily getMethodFamily() const
Determines the family of this method.
Represents any expression that calls an Objective-C method.
Selector GetNullarySelector(StringRef name, ASTContext &Ctx)
Utility function for constructing a nullary selector.
Expr * Key
The key for the dictionary element.
Represents an ObjC class declaration.
static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call)
static bool isKnownNonNilCollectionType(QualType T)
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
bool isReceiverSelfOrSuper() const
Checks if the receiver refers to 'self' or 'super'.
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.
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)
ObjCDictionaryLiteral - AST node to represent objective-c dictionary literals; as in:"name" : NSUserN...
DeclContext * getDeclContext()
QualType getConditionType() const
ObjCInterfaceDecl * getSuperClass() const
SymbolManager & getSymbolManager()
void print(llvm::raw_ostream &OS) const
Prints the full selector name (e.g. "foo:bar:").
Expr * getElement(unsigned Index)
getElement - Return the Element at the specified index.
bool isCFObjectRef(QualType T)
SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type)
unsigned getNumArgs() const
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
Optional< T > getAs() const
Convert to the specified SVal type, returning None if this SVal is not of the desired type...
CHECKER * registerChecker(AT... Args)
Used to register checkers.
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
IdentifierInfo * getIdentifierInfoForSlot(unsigned argIndex) const
Retrieve the identifier at a given position in the selector.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
ExplodedNode * generateSink(ProgramStateRef State, ExplodedNode *Pred, const ProgramPointTag *Tag=nullptr)
Generate a sink node.
unsigned getNumElements() const
getNumElements - Return number of elements of objective-c dictionary literal.
DeclStmt - Adaptor class for mixing declarations with statements and expressions. ...
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
bool isObjCObjectPointerType() 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.
StringRef getName() const
Return the actual identifier string.
virtual const ObjCMessageExpr * getOriginExpr() const
Selector getSelector() const
Dataflow Directional Tag Classes.
ASTContext & getASTContext()
bool isZeroConstant() const
static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N, const ObjCForCollectionStmt *FCS)
If the fist block edge is a back edge, we are reentering the loop.
const Expr * getInit() const
SVal evalEQ(ProgramStateRef state, SVal lhs, SVal rhs)
Represents symbolic expression that isn't a location.
Represents an abstract call to a function or method along a particular path.
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.
bool isInstanceMessage() const
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
ObjCInterfaceDecl * getInterfaceDecl() const
If this pointer points to an Objective @interface type, gets the declaration for that interface...
unsigned getNumElements() const
getNumElements - Return number of elements of objective-c array literal.
Represents Objective-C's collection statement.
const ProgramStateRef & getState() const
unsigned getNumArgs() const override
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
CanQualType getCanonicalType(QualType T) const
Return the canonical (structural) type corresponding to the specified potentially non-canonical type ...
const Expr * getArgExpr(unsigned Index) const override
Selector GetUnarySelector(StringRef name, ASTContext &Ctx)
Utility function for constructing an unary selector.
pred_iterator pred_begin()
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
SValBuilder & getSValBuilder()
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
static Optional< uint64_t > GetCFNumberSize(ASTContext &Ctx, uint64_t i)
const ExplodedNode *const * const_pred_iterator
A trivial tuple used to represent a source range.
Optional< T > getAs() const
Convert to the specified ProgramPoint type, returning None if this ProgramPoint is not of the desired...
bool inTopFrame() const
Return true if the current LocationContext has no caller context.
static bool isObjCNSObjectType(QualType Ty)
Return true if this is an NSObject object with its NSObject attribute set.
const LocationContext * getLocationContext() const
const LangOptions & getLangOpts() const
TypedRegion - An abstract class representing regions that are typed.