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 warnIfNilArg(C, msg, 1, Class, CanBeSubscript);
312 }
else if (S == RemoveObjectForKeySel) {
318 if ((Arg != InvalidArgIndex))
319 warnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
325 for (
unsigned i = 0; i < NumOfElements; ++i) {
326 warnIfNilExpr(AL->
getElement(i),
"Array element cannot be nil",
C);
333 for (
unsigned i = 0; i < NumOfElements; ++i) {
335 warnIfNilExpr(Element.
Key,
"Dictionary key cannot be nil", C);
336 warnIfNilExpr(Element.
Value,
"Dictionary value cannot be nil", C);
345 class CFNumberCreateChecker :
public Checker< check::PreStmt<CallExpr> > {
346 mutable std::unique_ptr<APIMisuse> BT;
349 CFNumberCreateChecker() : II(nullptr) {}
355 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
379 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
382 return FixedSize[i-1];
406 static const char* GetCFNumberTypeStr(uint64_t i) {
407 static const char* Names[] = {
408 "kCFNumberSInt8Type",
409 "kCFNumberSInt16Type",
410 "kCFNumberSInt32Type",
411 "kCFNumberSInt64Type",
412 "kCFNumberFloat32Type",
413 "kCFNumberFloat64Type",
415 "kCFNumberShortType",
418 "kCFNumberLongLongType",
419 "kCFNumberFloatType",
420 "kCFNumberDoubleType",
421 "kCFNumberCFIndexType",
422 "kCFNumberNSIntegerType",
423 "kCFNumberCGFloatType"
430 void CFNumberCreateChecker::checkPreStmt(
const CallExpr *CE,
446 SVal TheTypeVal = state->getSVal(CE->
getArg(1), LCtx);
454 uint64_t NumberKind = V->
getValue().getLimitedValue();
461 uint64_t TargetSize = *OptTargetSize;
466 SVal TheValueExpr = state->getSVal(CE->
getArg(2), LCtx);
489 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)
778 Optional<ExplodedNode*> errorNode;
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) {
924 Optional<Loc> ElementLoc;
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()),
963 Optional<DefinedSVal> CountGreaterThanZero =
965 if (!CountGreaterThanZero) {
971 return State->assume(*CountGreaterThanZero, Assumption);
995 if (BE->getSrc()->getLoopTarget() == FCS)
1033 bool ObjCLoopChecker::isCollectionCountMethod(
const ObjCMethodCall &M,
1037 if (!CountSelectorII)
1048 void ObjCLoopChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
1070 if (!isCollectionCountMethod(M, C))
1079 State = State->set<ContainerCountMap>(ContainerS, CountS);
1081 if (
const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) {
1082 State = State->remove<ContainerNonEmptyMap>(ContainerS);
1092 const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call);
1137 for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
1146 if (Sym == ImmutableReceiver)
1151 State = State->remove<ContainerCountMap>(Sym);
1152 State = State->remove<ContainerNonEmptyMap>(Sym);
1157 void ObjCLoopChecker::checkDeadSymbols(
SymbolReaper &SymReaper,
1162 ContainerCountMapTy Tracked = State->get<ContainerCountMap>();
1163 for (ContainerCountMapTy::iterator I = Tracked.begin(),
1164 E = Tracked.end(); I != E; ++I) {
1166 if (SymReaper.
isDead(Sym)) {
1167 State = State->remove<ContainerCountMap>(Sym);
1168 State = State->remove<ContainerNonEmptyMap>(Sym);
1179 class ObjCNonNilReturnValueChecker
1180 :
public Checker<check::PostObjCMessage,
1181 check::PostStmt<ObjCArrayLiteral>,
1182 check::PostStmt<ObjCDictionaryLiteral>,
1183 check::PostStmt<ObjCBoxedExpr> > {
1186 mutable Selector ObjectAtIndexedSubscript;
1200 assumeExprIsNonNull(E, C);
1203 assumeExprIsNonNull(E, C);
1206 assumeExprIsNonNull(E, C);
1214 ObjCNonNilReturnValueChecker::assumeExprIsNonNull(
const Expr *NonNullExpr,
1219 return State->assume(*DV,
true);
1223 void ObjCNonNilReturnValueChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
1231 ObjectAtIndexedSubscript =
GetUnarySelector(
"objectAtIndexedSubscript", Ctx);
1259 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
1296 void ento::registerVariadicMethodTypeChecker(
CheckerManager &mgr) {
1305 ento::registerObjCNonNilReturnValueChecker(
CheckerManager &mgr) {
Defines the clang::ASTContext interface.
static Selector GetNullarySelector(StringRef name, ASTContext &Ctx)
Utility function for constructing a nullary selector.
TypedValueRegion - An abstract class representing regions having a typed value.
nonloc::ConcreteInt makeIntVal(const IntegerLiteral *integer)
StringRef getName() const
Smart pointer class that efficiently represents Objective-C method names.
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)
IdentifierInfo * getIdentifier() const
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). Uses the default CheckerContex...
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)
ObjCDictionaryElement getKeyValueElement(unsigned Index) const
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.
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
Symbolic value. These values used to capture symbolic execution of the program.
const ObjCInterfaceDecl * getReceiverInterface() const
Get the interface for the receiver.
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).
static ProgramStateRef checkElementNonNil(CheckerContext &C, ProgramStateRef State, const ObjCForCollectionStmt *FCS)
void addSymbolDependency(const SymbolRef Primary, const SymbolRef Dependent)
Add artificial symbol dependency.
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.
static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call)
static bool isKnownNonNilCollectionType(QualType T)
ExplodedNode * generateSink(ProgramStateRef State=nullptr, ExplodedNode *Pred=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a sink node. Generating a sink stops exploration of the given path.
The return type of classify(). Represents the C++11 expression taxonomy.
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.
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...
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)
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.
Selector getSelector() const
A class responsible for cleaning up unused symbols.
REGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData, SymbolRef, MacOSKeychainAPIChecker::AllocationState) static bool isEnclosingFunctionParam(const Expr *E)
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.
unsigned getNumArgs() const
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
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
const LangOptions & getLangOpts() const
const ExplodedNode *const * const_pred_iterator
Represents Objective-C's collection statement.
ObjCInterfaceDecl * getInterfaceDecl() const
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)
pred_iterator pred_begin()
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef. Otherwise, return 0.
SValBuilder & getSValBuilder()
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
const llvm::APSInt & getValue() const
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
unsigned getNumElements() const
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.