diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Checker/BasicObjCFoundationChecks.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Checker/BasicObjCFoundationChecks.cpp | 585 |
1 files changed, 0 insertions, 585 deletions
diff --git a/contrib/llvm/tools/clang/lib/Checker/BasicObjCFoundationChecks.cpp b/contrib/llvm/tools/clang/lib/Checker/BasicObjCFoundationChecks.cpp deleted file mode 100644 index 3c1a6d1..0000000 --- a/contrib/llvm/tools/clang/lib/Checker/BasicObjCFoundationChecks.cpp +++ /dev/null @@ -1,585 +0,0 @@ -//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*-- -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines BasicObjCFoundationChecks, a class that encapsulates -// a set of simple checks to run on Objective-C code using Apple's Foundation -// classes. -// -//===----------------------------------------------------------------------===// - -#include "BasicObjCFoundationChecks.h" - -#include "clang/Checker/PathSensitive/ExplodedGraph.h" -#include "clang/Checker/PathSensitive/GRSimpleAPICheck.h" -#include "clang/Checker/PathSensitive/GRExprEngine.h" -#include "clang/Checker/PathSensitive/GRState.h" -#include "clang/Checker/BugReporter/BugType.h" -#include "clang/Checker/PathSensitive/MemRegion.h" -#include "clang/Checker/PathSensitive/CheckerVisitor.h" -#include "clang/Checker/Checkers/LocalCheckers.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/Expr.h" -#include "clang/AST/ExprObjC.h" -#include "clang/AST/ASTContext.h" - -using namespace clang; - -static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) { - QualType T; - switch (ME->getReceiverKind()) { - case ObjCMessageExpr::Instance: - T = ME->getInstanceReceiver()->getType(); - break; - - case ObjCMessageExpr::SuperInstance: - T = ME->getSuperType(); - break; - - case ObjCMessageExpr::Class: - case ObjCMessageExpr::SuperClass: - return 0; - } - - if (const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>()) - return PT->getInterfaceType(); - - return NULL; -} - -static const char* GetReceiverNameType(const ObjCMessageExpr* ME) { - if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME)) - return ReceiverType->getDecl()->getIdentifier()->getNameStart(); - return NULL; -} - -namespace { - -class APIMisuse : public BugType { -public: - APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} -}; - -class BasicObjCFoundationChecks : public GRSimpleAPICheck { - APIMisuse *BT; - BugReporter& BR; - ASTContext &Ctx; - - bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix); - bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME); - - bool CheckNilArg(ExplodedNode* N, unsigned Arg); - -public: - BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br) - : BT(0), BR(br), Ctx(ctx) {} - - bool Audit(ExplodedNode* N, GRStateManager&); - -private: - void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "Argument to '" << GetReceiverNameType(ME) << "' method '" - << ME->getSelector().getAsString() << "' cannot be nil."; - - // Lazily create the BugType object for NilArg. This will be owned - // by the BugReporter object 'BR' once we call BR.EmitWarning. - if (!BT) BT = new APIMisuse("nil argument"); - - RangedBugReport *R = new RangedBugReport(*BT, os.str(), N); - R->addRange(ME->getArg(Arg)->getSourceRange()); - BR.EmitReport(R); - } -}; - -} // end anonymous namespace - - -GRSimpleAPICheck* -clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) { - return new BasicObjCFoundationChecks(Ctx, BR); -} - - - -bool BasicObjCFoundationChecks::Audit(ExplodedNode* N, - GRStateManager&) { - - const ObjCMessageExpr* ME = - cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt()); - - const ObjCInterfaceType *ReceiverType = GetReceiverType(ME); - - if (!ReceiverType) - return false; - - if (isNSString(ReceiverType, - ReceiverType->getDecl()->getIdentifier()->getName())) - return AuditNSString(N, ME); - - return false; -} - -static inline bool isNil(SVal X) { - return isa<loc::ConcreteInt>(X); -} - -//===----------------------------------------------------------------------===// -// Error reporting. -//===----------------------------------------------------------------------===// - -bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) { - const ObjCMessageExpr* ME = - cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt()); - - const Expr * E = ME->getArg(Arg); - - if (isNil(N->getState()->getSVal(E))) { - WarnNilArg(N, ME, Arg); - return true; - } - - return false; -} - -//===----------------------------------------------------------------------===// -// NSString checking. -//===----------------------------------------------------------------------===// - -bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T, - llvm::StringRef ClassName) { - return ClassName == "NSString" || ClassName == "NSMutableString"; -} - -bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N, - const ObjCMessageExpr* ME) { - - Selector S = ME->getSelector(); - - if (S.isUnarySelector()) - return false; - - // FIXME: This is going to be really slow doing these checks with - // lexical comparisons. - - std::string NameStr = S.getAsString(); - llvm::StringRef Name(NameStr); - assert(!Name.empty()); - - // FIXME: Checking for initWithFormat: will not work in most cases - // yet because [NSString alloc] returns id, not NSString*. We will - // need support for tracking expected-type information in the analyzer - // to find these errors. - if (Name == "caseInsensitiveCompare:" || - Name == "compare:" || - Name == "compare:options:" || - Name == "compare:options:range:" || - Name == "compare:options:range:locale:" || - Name == "componentsSeparatedByCharactersInSet:" || - Name == "initWithFormat:") - return CheckNilArg(N, 0); - - return false; -} - -//===----------------------------------------------------------------------===// -// Error reporting. -//===----------------------------------------------------------------------===// - -namespace { - -class AuditCFNumberCreate : public GRSimpleAPICheck { - APIMisuse* BT; - - // FIXME: Either this should be refactored into GRSimpleAPICheck, or - // it should always be passed with a call to Audit. The latter - // approach makes this class more stateless. - ASTContext& Ctx; - IdentifierInfo* II; - BugReporter& BR; - -public: - AuditCFNumberCreate(ASTContext& ctx, BugReporter& br) - : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){} - - ~AuditCFNumberCreate() {} - - bool Audit(ExplodedNode* N, GRStateManager&); - -private: - void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N, - uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); -}; -} // end anonymous namespace - -enum CFNumberType { - kCFNumberSInt8Type = 1, - kCFNumberSInt16Type = 2, - kCFNumberSInt32Type = 3, - kCFNumberSInt64Type = 4, - kCFNumberFloat32Type = 5, - kCFNumberFloat64Type = 6, - kCFNumberCharType = 7, - kCFNumberShortType = 8, - kCFNumberIntType = 9, - kCFNumberLongType = 10, - kCFNumberLongLongType = 11, - kCFNumberFloatType = 12, - kCFNumberDoubleType = 13, - kCFNumberCFIndexType = 14, - kCFNumberNSIntegerType = 15, - kCFNumberCGFloatType = 16 -}; - -namespace { - template<typename T> - class Optional { - bool IsKnown; - T Val; - public: - Optional() : IsKnown(false), Val(0) {} - Optional(const T& val) : IsKnown(true), Val(val) {} - - bool isKnown() const { return IsKnown; } - - const T& getValue() const { - assert (isKnown()); - return Val; - } - - operator const T&() const { - return getValue(); - } - }; -} - -static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) { - static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; - - if (i < kCFNumberCharType) - return FixedSize[i-1]; - - QualType T; - - switch (i) { - case kCFNumberCharType: T = Ctx.CharTy; break; - case kCFNumberShortType: T = Ctx.ShortTy; break; - case kCFNumberIntType: T = Ctx.IntTy; break; - case kCFNumberLongType: T = Ctx.LongTy; break; - case kCFNumberLongLongType: T = Ctx.LongLongTy; break; - case kCFNumberFloatType: T = Ctx.FloatTy; break; - case kCFNumberDoubleType: T = Ctx.DoubleTy; break; - case kCFNumberCFIndexType: - case kCFNumberNSIntegerType: - case kCFNumberCGFloatType: - // FIXME: We need a way to map from names to Type*. - default: - return Optional<uint64_t>(); - } - - return Ctx.getTypeSize(T); -} - -#if 0 -static const char* GetCFNumberTypeStr(uint64_t i) { - static const char* Names[] = { - "kCFNumberSInt8Type", - "kCFNumberSInt16Type", - "kCFNumberSInt32Type", - "kCFNumberSInt64Type", - "kCFNumberFloat32Type", - "kCFNumberFloat64Type", - "kCFNumberCharType", - "kCFNumberShortType", - "kCFNumberIntType", - "kCFNumberLongType", - "kCFNumberLongLongType", - "kCFNumberFloatType", - "kCFNumberDoubleType", - "kCFNumberCFIndexType", - "kCFNumberNSIntegerType", - "kCFNumberCGFloatType" - }; - - return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType"; -} -#endif - -bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){ - const CallExpr* CE = - cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt()); - const Expr* Callee = CE->getCallee(); - SVal CallV = N->getState()->getSVal(Callee); - const FunctionDecl* FD = CallV.getAsFunctionDecl(); - - if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3) - return false; - - // Get the value of the "theType" argument. - SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1)); - - // FIXME: We really should allow ranges of valid theType values, and - // bifurcate the state appropriately. - nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal); - - if (!V) - return false; - - uint64_t NumberKind = V->getValue().getLimitedValue(); - Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind); - - // FIXME: In some cases we can emit an error. - if (!TargetSize.isKnown()) - return false; - - // Look at the value of the integer being passed by reference. Essentially - // we want to catch cases where the value passed in is not equal to the - // size of the type being created. - SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2)); - - // FIXME: Eventually we should handle arbitrary locations. We can do this - // by having an enhanced memory model that does low-level typing. - loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr); - - if (!LV) - return false; - - const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts()); - - if (!R) - return false; - - QualType T = Ctx.getCanonicalType(R->getValueType()); - - // FIXME: If the pointee isn't an integer type, should we flag a warning? - // People can do weird stuff with pointers. - - if (!T->isIntegerType()) - return false; - - uint64_t SourceSize = Ctx.getTypeSize(T); - - // CHECK: is SourceSize == TargetSize - - if (SourceSize == TargetSize) - return false; - - AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind); - - // FIXME: We can actually create an abstract "CFNumber" object that has - // the bits initialized to the provided values. - return SourceSize < TargetSize; -} - -void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex, - ExplodedNode *N, - uint64_t SourceSize, uint64_t TargetSize, - uint64_t NumberKind) { - - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - - os << (SourceSize == 8 ? "An " : "A ") - << SourceSize << " bit integer is used to initialize a CFNumber " - "object that represents " - << (TargetSize == 8 ? "an " : "a ") - << TargetSize << " bit integer. "; - - if (SourceSize < TargetSize) - os << (TargetSize - SourceSize) - << " bits of the CFNumber value will be garbage." ; - else - os << (SourceSize - TargetSize) - << " bits of the input integer will be lost."; - - // Lazily create the BugType object. This will be owned - // by the BugReporter object 'BR' once we call BR.EmitWarning. - if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate"); - RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); - report->addRange(Ex->getSourceRange()); - BR.EmitReport(report); -} - -GRSimpleAPICheck* -clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) { - return new AuditCFNumberCreate(Ctx, BR); -} - -//===----------------------------------------------------------------------===// -// CFRetain/CFRelease checking for null arguments. -//===----------------------------------------------------------------------===// - -namespace { -class CFRetainReleaseChecker : public CheckerVisitor<CFRetainReleaseChecker> { - APIMisuse *BT; - IdentifierInfo *Retain, *Release; - -public: - CFRetainReleaseChecker(ASTContext& Ctx): BT(NULL), - Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")) - {} - - static void *getTag() { static int x = 0; return &x; } - - void PreVisitCallExpr(CheckerContext& C, const CallExpr* CE); -}; -} // end anonymous namespace - - -void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C, - const CallExpr* CE) { - // If the CallExpr doesn't have exactly 1 argument just give up checking. - if (CE->getNumArgs() != 1) - return; - - // Get the function declaration of the callee. - const GRState* state = C.getState(); - SVal X = state->getSVal(CE->getCallee()); - const FunctionDecl* FD = X.getAsFunctionDecl(); - - if (!FD) - return; - - // Check if we called CFRetain/CFRelease. - const IdentifierInfo *FuncII = FD->getIdentifier(); - if (!(FuncII == Retain || FuncII == Release)) - return; - - // FIXME: The rest of this just checks that the argument is non-null. - // It should probably be refactored and combined with AttrNonNullChecker. - - // Get the argument's value. - const Expr *Arg = CE->getArg(0); - SVal ArgVal = state->getSVal(Arg); - DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal); - if (!DefArgVal) - return; - - // Get a NULL value. - ValueManager &ValMgr = C.getValueManager(); - DefinedSVal Zero = cast<DefinedSVal>(ValMgr.makeZeroVal(Arg->getType())); - - // Make an expression asserting that they're equal. - SValuator &SVator = ValMgr.getSValuator(); - DefinedOrUnknownSVal ArgIsNull = SVator.EvalEQ(state, Zero, *DefArgVal); - - // Are they equal? - const GRState *stateTrue, *stateFalse; - llvm::tie(stateTrue, stateFalse) = state->Assume(ArgIsNull); - - if (stateTrue && !stateFalse) { - ExplodedNode *N = C.GenerateSink(stateTrue); - if (!N) - return; - - if (!BT) - BT = new APIMisuse("null passed to CFRetain/CFRelease"); - - const char *description = (FuncII == Retain) - ? "Null pointer argument in call to CFRetain" - : "Null pointer argument in call to CFRelease"; - - EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N); - report->addRange(Arg->getSourceRange()); - report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg); - - C.EmitReport(report); - return; - } - - // From here on, we know the argument is non-null. - C.addTransition(stateFalse); -} - -//===----------------------------------------------------------------------===// -// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. -//===----------------------------------------------------------------------===// - -namespace { -class ClassReleaseChecker : - public CheckerVisitor<ClassReleaseChecker> { - Selector releaseS; - Selector retainS; - Selector autoreleaseS; - Selector drainS; - BugType *BT; -public: - ClassReleaseChecker(ASTContext &Ctx) - : releaseS(GetNullarySelector("release", Ctx)), - retainS(GetNullarySelector("retain", Ctx)), - autoreleaseS(GetNullarySelector("autorelease", Ctx)), - drainS(GetNullarySelector("drain", Ctx)), - BT(0) {} - - static void *getTag() { static int x = 0; return &x; } - - void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); -}; -} - -void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C, - const ObjCMessageExpr *ME) { - ObjCInterfaceDecl *Class = 0; - switch (ME->getReceiverKind()) { - case ObjCMessageExpr::Class: - Class = ME->getClassReceiver()->getAs<ObjCObjectType>()->getInterface(); - break; - - case ObjCMessageExpr::SuperClass: - Class = ME->getSuperType()->getAs<ObjCObjectType>()->getInterface(); - break; - - case ObjCMessageExpr::Instance: - case ObjCMessageExpr::SuperInstance: - return; - } - - Selector S = ME->getSelector(); - if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) - return; - - if (!BT) - BT = new APIMisuse("message incorrectly sent to class instead of class " - "instance"); - - ExplodedNode *N = C.GenerateNode(); - - if (!N) - return; - - llvm::SmallString<200> buf; - llvm::raw_svector_ostream os(buf); - - os << "The '" << S.getAsString() << "' message should be sent to instances " - "of class '" << Class->getName() - << "' and not the class directly"; - - RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); - report->addRange(ME->getSourceRange()); - C.EmitReport(report); -} - -//===----------------------------------------------------------------------===// -// Check registration. -//===----------------------------------------------------------------------===// - -void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) { - ASTContext& Ctx = Eng.getContext(); - BugReporter &BR = Eng.getBugReporter(); - - Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR), - Stmt::ObjCMessageExprClass); - Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass); - - RegisterNSErrorChecks(BR, Eng, D); - RegisterNSAutoreleasePoolChecks(Eng); - - Eng.registerCheck(new CFRetainReleaseChecker(Ctx)); - Eng.registerCheck(new ClassReleaseChecker(Ctx)); -} |