diff options
author | dim <dim@FreeBSD.org> | 2011-05-02 19:39:53 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2011-05-02 19:39:53 +0000 |
commit | 110eaaceddcec790f7e6a5e3bf1261c9aa1e73ab (patch) | |
tree | 64a10f4c4154739d4a8191d7e1b52ce497f4ebd6 /lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp | |
parent | a0fb00f9837bd0d2e5948f16f6a6b82a7a628f51 (diff) | |
download | FreeBSD-src-110eaaceddcec790f7e6a5e3bf1261c9aa1e73ab.zip FreeBSD-src-110eaaceddcec790f7e6a5e3bf1261c9aa1e73ab.tar.gz |
Vendor import of clang trunk r130700:
http://llvm.org/svn/llvm-project/cfe/trunk@130700
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp | 380 |
1 files changed, 235 insertions, 145 deletions
diff --git a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index 7a1b978..63a5917 100644 --- a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -1,4 +1,4 @@ -//=- NSErrorCheckerer.cpp - Coding conventions for uses of NSError -*- C++ -*-==// +//=- NSErrorChecker.cpp - Coding conventions for uses of NSError -*- C++ -*-==// // // The LLVM Compiler Infrastructure // @@ -15,11 +15,12 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/StaticAnalyzer/Checkers/DereferenceChecker.h" -#include "BasicObjCFoundationChecks.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Decl.h" #include "llvm/ADT/SmallVector.h" @@ -27,142 +28,263 @@ using namespace clang; using namespace ento; -namespace { -class NSErrorChecker : public BugType { - const Decl &CodeDecl; - const bool isNSErrorWarning; - IdentifierInfo * const II; - ExprEngine &Eng; +static bool IsNSError(QualType T, IdentifierInfo *II); +static bool IsCFError(QualType T, IdentifierInfo *II); - void CheckSignature(const ObjCMethodDecl& MD, QualType& ResultTy, - llvm::SmallVectorImpl<VarDecl*>& ErrorParams); +//===----------------------------------------------------------------------===// +// NSErrorMethodChecker +//===----------------------------------------------------------------------===// - void CheckSignature(const FunctionDecl& MD, QualType& ResultTy, - llvm::SmallVectorImpl<VarDecl*>& ErrorParams); +namespace { +class NSErrorMethodChecker + : public Checker< check::ASTDecl<ObjCMethodDecl> > { + mutable IdentifierInfo *II; - bool CheckNSErrorArgument(QualType ArgTy); - bool CheckCFErrorArgument(QualType ArgTy); +public: + NSErrorMethodChecker() : II(0) { } - void CheckParamDeref(const VarDecl *V, const LocationContext *LC, - const GRState *state, BugReporter& BR); + void checkASTDecl(const ObjCMethodDecl *D, + AnalysisManager &mgr, BugReporter &BR) const; +}; +} - void EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl); +void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D, + AnalysisManager &mgr, + BugReporter &BR) const { + if (!D->isThisDeclarationADefinition()) + return; + if (!D->getResultType()->isVoidType()) + return; -public: - NSErrorChecker(const Decl &D, bool isNSError, ExprEngine& eng) - : BugType(isNSError ? "NSError** null dereference" - : "CFErrorRef* null dereference", - "Coding conventions (Apple)"), - CodeDecl(D), - isNSErrorWarning(isNSError), - II(&eng.getContext().Idents.get(isNSErrorWarning ? "NSError":"CFErrorRef")), - Eng(eng) {} - - void FlushReports(BugReporter& BR); -}; + if (!II) + II = &D->getASTContext().Idents.get("NSError"); -} // end anonymous namespace + bool hasNSError = false; + for (ObjCMethodDecl::param_iterator + I = D->param_begin(), E = D->param_end(); I != E; ++I) { + if (IsNSError((*I)->getType(), II)) { + hasNSError = true; + break; + } + } -void ento::RegisterNSErrorChecks(BugReporter& BR, ExprEngine &Eng, - const Decl &D) { - BR.Register(new NSErrorChecker(D, true, Eng)); - BR.Register(new NSErrorChecker(D, false, Eng)); + if (hasNSError) { + const char *err = "Method accepting NSError** " + "should have a non-void return value to indicate whether or not an " + "error occurred"; + BR.EmitBasicReport("Bad return type when passing NSError**", + "Coding conventions (Apple)", err, D->getLocation()); + } } -void NSErrorChecker::FlushReports(BugReporter& BR) { - // Get the analysis engine and the exploded analysis graph. - ExplodedGraph& G = Eng.getGraph(); +//===----------------------------------------------------------------------===// +// CFErrorFunctionChecker +//===----------------------------------------------------------------------===// - // Get the ASTContext, which is useful for querying type information. - ASTContext &Ctx = BR.getContext(); +namespace { +class CFErrorFunctionChecker + : public Checker< check::ASTDecl<FunctionDecl> > { + mutable IdentifierInfo *II; - QualType ResultTy; - llvm::SmallVector<VarDecl*, 5> ErrorParams; +public: + CFErrorFunctionChecker() : II(0) { } - if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CodeDecl)) - CheckSignature(*MD, ResultTy, ErrorParams); - else if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(&CodeDecl)) - CheckSignature(*FD, ResultTy, ErrorParams); - else - return; + void checkASTDecl(const FunctionDecl *D, + AnalysisManager &mgr, BugReporter &BR) const; +}; +} - if (ErrorParams.empty()) +void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D, + AnalysisManager &mgr, + BugReporter &BR) const { + if (!D->isThisDeclarationADefinition()) return; + if (!D->getResultType()->isVoidType()) + return; + + if (!II) + II = &D->getASTContext().Idents.get("CFErrorRef"); - if (ResultTy == Ctx.VoidTy) EmitRetTyWarning(BR, CodeDecl); + bool hasCFError = false; + for (FunctionDecl::param_const_iterator + I = D->param_begin(), E = D->param_end(); I != E; ++I) { + if (IsCFError((*I)->getType(), II)) { + hasCFError = true; + break; + } + } - for (ExplodedGraph::roots_iterator RI=G.roots_begin(), RE=G.roots_end(); - RI!=RE; ++RI) { - // Scan the parameters for an implicit null dereference. - for (llvm::SmallVectorImpl<VarDecl*>::iterator I=ErrorParams.begin(), - E=ErrorParams.end(); I!=E; ++I) - CheckParamDeref(*I, (*RI)->getLocationContext(), (*RI)->getState(), BR); + if (hasCFError) { + const char *err = "Function accepting CFErrorRef* " + "should have a non-void return value to indicate whether or not an " + "error occurred"; + BR.EmitBasicReport("Bad return type when passing CFErrorRef*", + "Coding conventions (Apple)", err, D->getLocation()); } } -void NSErrorChecker::EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); +//===----------------------------------------------------------------------===// +// NSOrCFErrorDerefChecker +//===----------------------------------------------------------------------===// - if (isa<ObjCMethodDecl>(CodeDecl)) - os << "Method"; - else - os << "Function"; +namespace { - os << " accepting "; - os << (isNSErrorWarning ? "NSError**" : "CFErrorRef*"); - os << " should have a non-void return value to indicate whether or not an " - "error occurred"; +class NSErrorDerefBug : public BugType { +public: + NSErrorDerefBug() : BugType("NSError** null dereference", + "Coding conventions (Apple)") {} +}; + +class CFErrorDerefBug : public BugType { +public: + CFErrorDerefBug() : BugType("CFErrorRef* null dereference", + "Coding conventions (Apple)") {} +}; - BR.EmitBasicReport(isNSErrorWarning - ? "Bad return type when passing NSError**" - : "Bad return type when passing CFError*", - getCategory(), os.str(), - CodeDecl.getLocation()); } -void -NSErrorChecker::CheckSignature(const ObjCMethodDecl& M, QualType& ResultTy, - llvm::SmallVectorImpl<VarDecl*>& ErrorParams) { +namespace { +class NSOrCFErrorDerefChecker + : public Checker< check::Location, + check::Event<ImplicitNullDerefEvent> > { + mutable IdentifierInfo *NSErrorII, *CFErrorII; +public: + bool ShouldCheckNSError, ShouldCheckCFError; + NSOrCFErrorDerefChecker() : NSErrorII(0), CFErrorII(0), + ShouldCheckNSError(0), ShouldCheckCFError(0) { } + + void checkLocation(SVal loc, bool isLoad, CheckerContext &C) const; + void checkEvent(ImplicitNullDerefEvent event) const; +}; +} - ResultTy = M.getResultType(); +namespace { struct NSErrorOut {}; } +namespace { struct CFErrorOut {}; } + +typedef llvm::ImmutableMap<SymbolRef, unsigned> ErrorOutFlag; + +namespace clang { +namespace ento { + template <> + struct GRStateTrait<NSErrorOut> : public GRStatePartialTrait<ErrorOutFlag> { + static void *GDMIndex() { static int index = 0; return &index; } + }; + template <> + struct GRStateTrait<CFErrorOut> : public GRStatePartialTrait<ErrorOutFlag> { + static void *GDMIndex() { static int index = 0; return &index; } + }; +} +} - for (ObjCMethodDecl::param_iterator I=M.param_begin(), - E=M.param_end(); I!=E; ++I) { +template <typename T> +static bool hasFlag(SVal val, const GRState *state) { + if (SymbolRef sym = val.getAsSymbol()) + if (const unsigned *attachedFlags = state->get<T>(sym)) + return *attachedFlags; + return false; +} - QualType T = (*I)->getType(); +template <typename T> +static void setFlag(const GRState *state, SVal val, CheckerContext &C) { + // We tag the symbol that the SVal wraps. + if (SymbolRef sym = val.getAsSymbol()) + C.addTransition(state->set<T>(sym, true)); +} - if (isNSErrorWarning) { - if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I); - } - else if (CheckCFErrorArgument(T)) - ErrorParams.push_back(*I); +static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) { + const StackFrameContext * + SFC = C.getPredecessor()->getLocationContext()->getCurrentStackFrame(); + if (const loc::MemRegionVal* X = dyn_cast<loc::MemRegionVal>(&val)) { + const MemRegion* R = X->getRegion(); + if (const VarRegion *VR = R->getAs<VarRegion>()) + if (const StackArgumentsSpaceRegion * + stackReg = dyn_cast<StackArgumentsSpaceRegion>(VR->getMemorySpace())) + if (stackReg->getStackFrame() == SFC) + return VR->getValueType(); } + + return QualType(); } -void -NSErrorChecker::CheckSignature(const FunctionDecl& F, QualType& ResultTy, - llvm::SmallVectorImpl<VarDecl*>& ErrorParams) { +void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad, + CheckerContext &C) const { + if (!isLoad) + return; + if (loc.isUndef() || !isa<Loc>(loc)) + return; - ResultTy = F.getResultType(); + ASTContext &Ctx = C.getASTContext(); + const GRState *state = C.getState(); - for (FunctionDecl::param_const_iterator I = F.param_begin(), - E = F.param_end(); I != E; ++I) { + // If we are loading from NSError**/CFErrorRef* parameter, mark the resulting + // SVal so that we can later check it when handling the + // ImplicitNullDerefEvent event. + // FIXME: Cumbersome! Maybe add hook at construction of SVals at start of + // function ? - QualType T = (*I)->getType(); + QualType parmT = parameterTypeFromSVal(loc, C); + if (parmT.isNull()) + return; - if (isNSErrorWarning) { - if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I); - } - else if (CheckCFErrorArgument(T)) - ErrorParams.push_back(*I); + if (!NSErrorII) + NSErrorII = &Ctx.Idents.get("NSError"); + if (!CFErrorII) + CFErrorII = &Ctx.Idents.get("CFErrorRef"); + + if (ShouldCheckNSError && IsNSError(parmT, NSErrorII)) { + setFlag<NSErrorOut>(state, state->getSVal(cast<Loc>(loc)), C); + return; + } + + if (ShouldCheckCFError && IsCFError(parmT, CFErrorII)) { + setFlag<CFErrorOut>(state, state->getSVal(cast<Loc>(loc)), C); + return; } } +void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const { + if (event.IsLoad) + return; + + SVal loc = event.Location; + const GRState *state = event.SinkNode->getState(); + BugReporter &BR = *event.BR; + + bool isNSError = hasFlag<NSErrorOut>(loc, state); + bool isCFError = false; + if (!isNSError) + isCFError = hasFlag<CFErrorOut>(loc, state); + + if (!(isNSError || isCFError)) + return; -bool NSErrorChecker::CheckNSErrorArgument(QualType ArgTy) { + // Storing to possible null NSError/CFErrorRef out parameter. - const PointerType* PPT = ArgTy->getAs<PointerType>(); + // Emit an error. + std::string err; + llvm::raw_string_ostream os(err); + os << "Potential null dereference. According to coding standards "; + + if (isNSError) + os << "in 'Creating and Returning NSError Objects' the parameter '"; + else + os << "documented in CoreFoundation/CFError.h the parameter '"; + + os << "' may be null."; + + BugType *bug = 0; + if (isNSError) + bug = new NSErrorDerefBug(); + else + bug = new CFErrorDerefBug(); + EnhancedBugReport *report = new EnhancedBugReport(*bug, os.str(), + event.SinkNode); + BR.EmitReport(report); +} + +static bool IsNSError(QualType T, IdentifierInfo *II) { + + const PointerType* PPT = T->getAs<PointerType>(); if (!PPT) return false; @@ -181,9 +303,8 @@ bool NSErrorChecker::CheckNSErrorArgument(QualType ArgTy) { return false; } -bool NSErrorChecker::CheckCFErrorArgument(QualType ArgTy) { - - const PointerType* PPT = ArgTy->getAs<PointerType>(); +static bool IsCFError(QualType T, IdentifierInfo *II) { + const PointerType* PPT = T->getAs<PointerType>(); if (!PPT) return false; const TypedefType* TT = PPT->getPointeeType()->getAs<TypedefType>(); @@ -192,47 +313,16 @@ bool NSErrorChecker::CheckCFErrorArgument(QualType ArgTy) { return TT->getDecl()->getIdentifier() == II; } -void NSErrorChecker::CheckParamDeref(const VarDecl *Param, - const LocationContext *LC, - const GRState *rootState, - BugReporter& BR) { - - SVal ParamL = rootState->getLValue(Param, LC); - const MemRegion* ParamR = cast<loc::MemRegionVal>(ParamL).getRegionAs<VarRegion>(); - assert (ParamR && "Parameters always have VarRegions."); - SVal ParamSVal = rootState->getSVal(ParamR); - - // FIXME: For now assume that ParamSVal is symbolic. We need to generalize - // this later. - SymbolRef ParamSym = ParamSVal.getAsLocSymbol(); - if (!ParamSym) - return; +void ento::registerNSErrorChecker(CheckerManager &mgr) { + mgr.registerChecker<NSErrorMethodChecker>(); + NSOrCFErrorDerefChecker * + checker = mgr.registerChecker<NSOrCFErrorDerefChecker>(); + checker->ShouldCheckNSError = true; +} - // Iterate over the implicit-null dereferences. - ExplodedNode *const* I, *const* E; - llvm::tie(I, E) = GetImplicitNullDereferences(Eng); - for ( ; I != E; ++I) { - const GRState *state = (*I)->getState(); - SVal location = state->getSVal((*I)->getLocationAs<StmtPoint>()->getStmt()); - if (location.getAsSymbol() != ParamSym) - continue; - - // Emit an error. - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - os << "Potential null dereference. According to coding standards "; - - if (isNSErrorWarning) - os << "in 'Creating and Returning NSError Objects' the parameter '"; - else - os << "documented in CoreFoundation/CFError.h the parameter '"; - - os << Param << "' may be null."; - - BugReport *report = new BugReport(*this, os.str(), *I); - // FIXME: Notable symbols are now part of the report. We should - // add support for notable symbols in BugReport. - // BR.addNotableSymbol(SV->getSymbol()); - BR.EmitReport(report); - } +void ento::registerCFErrorChecker(CheckerManager &mgr) { + mgr.registerChecker<CFErrorFunctionChecker>(); + NSOrCFErrorDerefChecker * + checker = mgr.registerChecker<NSOrCFErrorDerefChecker>(); + checker->ShouldCheckCFError = true; } |