diff options
author | Ted Kremenek <kremenek@apple.com> | 2010-12-23 19:38:26 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2010-12-23 19:38:26 +0000 |
commit | 21142581d55918beed544a757e4af3bb865b1812 (patch) | |
tree | c630a6cf82f6953372f1ea8f700d0d0a9372472c /lib/StaticAnalyzer/Checkers | |
parent | fd03843f0597de5eeced69ca9ae45c478fb2b153 (diff) |
Chris Lattner has strong opinions about directory
layout. :)
Rename the 'EntoSA' directories to 'StaticAnalyzer'.
Internally we will still use the 'ento' namespace
for the analyzer engine (unless there are further
sabre rattlings...).
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@122514 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/StaticAnalyzer/Checkers')
53 files changed, 14056 insertions, 0 deletions
diff --git a/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp b/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp new file mode 100644 index 0000000000..46e2de5c26 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp @@ -0,0 +1,96 @@ +//== AdjustedReturnValueChecker.cpp -----------------------------*- 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 AdjustedReturnValueChecker, a simple check to see if the +// return value of a function call is different than the one the caller thinks +// it is. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class AdjustedReturnValueChecker : + public CheckerVisitor<AdjustedReturnValueChecker> { +public: + AdjustedReturnValueChecker() {} + + void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); + + static void *getTag() { + static int x = 0; return &x; + } +}; +} + +void ento::RegisterAdjustedReturnValueChecker(ExprEngine &Eng) { + Eng.registerCheck(new AdjustedReturnValueChecker()); +} + +void AdjustedReturnValueChecker::PostVisitCallExpr(CheckerContext &C, + const CallExpr *CE) { + + // Get the result type of the call. + QualType expectedResultTy = CE->getType(); + + // Fetch the signature of the called function. + const GRState *state = C.getState(); + + SVal V = state->getSVal(CE); + + if (V.isUnknown()) + return; + + // Casting to void? Discard the value. + if (expectedResultTy->isVoidType()) { + C.generateNode(state->BindExpr(CE, UnknownVal())); + return; + } + + const MemRegion *callee = state->getSVal(CE->getCallee()).getAsRegion(); + if (!callee) + return; + + QualType actualResultTy; + + if (const FunctionTextRegion *FT = dyn_cast<FunctionTextRegion>(callee)) { + const FunctionDecl *FD = FT->getDecl(); + actualResultTy = FD->getResultType(); + } + else if (const BlockDataRegion *BD = dyn_cast<BlockDataRegion>(callee)) { + const BlockTextRegion *BR = BD->getCodeRegion(); + const BlockPointerType *BT=BR->getLocationType()->getAs<BlockPointerType>(); + const FunctionType *FT = BT->getPointeeType()->getAs<FunctionType>(); + actualResultTy = FT->getResultType(); + } + + // Can this happen? + if (actualResultTy.isNull()) + return; + + // For now, ignore references. + if (actualResultTy->getAs<ReferenceType>()) + return; + + + // Are they the same? + if (expectedResultTy != actualResultTy) { + // FIXME: Do more checking and actual emit an error. At least performing + // the cast avoids some assertion failures elsewhere. + SValBuilder &svalBuilder = C.getSValBuilder(); + V = svalBuilder.evalCast(V, expectedResultTy, actualResultTy); + C.generateNode(state->BindExpr(CE, V)); + } +} diff --git a/lib/StaticAnalyzer/Checkers/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Checkers/AnalysisConsumer.cpp new file mode 100644 index 0000000000..ee4eae2ebf --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/AnalysisConsumer.cpp @@ -0,0 +1,610 @@ +//===--- AnalysisConsumer.cpp - ASTConsumer for running Analyses ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// "Meta" ASTConsumer for running different source analyses. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/AnalysisConsumer.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/ParentMap.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/Analyses/UninitializedValues.h" +#include "clang/Analysis/CFG.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/ManagerRegistry.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/TransferFuncs.h" +#include "clang/StaticAnalyzer/PathDiagnosticClients.h" + +// FIXME: Restructure checker registration. +#include "ExprEngineExperimentalChecks.h" +#include "ExprEngineInternalChecks.h" + +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/AnalyzerOptions.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/ADT/OwningPtr.h" + +using namespace clang; +using namespace ento; + +static ExplodedNode::Auditor* CreateUbiViz(); + +//===----------------------------------------------------------------------===// +// Special PathDiagnosticClients. +//===----------------------------------------------------------------------===// + +static PathDiagnosticClient* +createPlistHTMLDiagnosticClient(const std::string& prefix, + const Preprocessor &PP) { + PathDiagnosticClient *PD = + createHTMLDiagnosticClient(llvm::sys::path::parent_path(prefix), PP); + return createPlistDiagnosticClient(prefix, PP, PD); +} + +//===----------------------------------------------------------------------===// +// AnalysisConsumer declaration. +//===----------------------------------------------------------------------===// + +namespace { + +class AnalysisConsumer : public ASTConsumer { +public: + typedef void (*CodeAction)(AnalysisConsumer &C, AnalysisManager &M, Decl *D); + typedef void (*TUAction)(AnalysisConsumer &C, AnalysisManager &M, + TranslationUnitDecl &TU); + +private: + typedef std::vector<CodeAction> Actions; + typedef std::vector<TUAction> TUActions; + + Actions FunctionActions; + Actions ObjCMethodActions; + Actions ObjCImplementationActions; + Actions CXXMethodActions; + TUActions TranslationUnitActions; // Remove this. + +public: + ASTContext* Ctx; + const Preprocessor &PP; + const std::string OutDir; + AnalyzerOptions Opts; + + // PD is owned by AnalysisManager. + PathDiagnosticClient *PD; + + StoreManagerCreator CreateStoreMgr; + ConstraintManagerCreator CreateConstraintMgr; + + llvm::OwningPtr<AnalysisManager> Mgr; + + AnalysisConsumer(const Preprocessor& pp, + const std::string& outdir, + const AnalyzerOptions& opts) + : Ctx(0), PP(pp), OutDir(outdir), + Opts(opts), PD(0) { + DigestAnalyzerOptions(); + } + + void DigestAnalyzerOptions() { + // Create the PathDiagnosticClient. + if (!OutDir.empty()) { + switch (Opts.AnalysisDiagOpt) { + default: +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) \ + case PD_##NAME: PD = CREATEFN(OutDir, PP); break; +#include "clang/Frontend/Analyses.def" + } + } else if (Opts.AnalysisDiagOpt == PD_TEXT) { + // Create the text client even without a specified output file since + // it just uses diagnostic notes. + PD = createTextPathDiagnosticClient("", PP); + } + + // Create the analyzer component creators. + if (ManagerRegistry::StoreMgrCreator != 0) { + CreateStoreMgr = ManagerRegistry::StoreMgrCreator; + } + else { + switch (Opts.AnalysisStoreOpt) { + default: + assert(0 && "Unknown store manager."); +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN) \ + case NAME##Model: CreateStoreMgr = CREATEFN; break; +#include "clang/Frontend/Analyses.def" + } + } + + if (ManagerRegistry::ConstraintMgrCreator != 0) + CreateConstraintMgr = ManagerRegistry::ConstraintMgrCreator; + else { + switch (Opts.AnalysisConstraintsOpt) { + default: + assert(0 && "Unknown store manager."); +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \ + case NAME##Model: CreateConstraintMgr = CREATEFN; break; +#include "clang/Frontend/Analyses.def" + } + } + } + + void DisplayFunction(const Decl *D) { + if (!Opts.AnalyzerDisplayProgress) + return; + + SourceManager &SM = Mgr->getASTContext().getSourceManager(); + PresumedLoc Loc = SM.getPresumedLoc(D->getLocation()); + if (Loc.isValid()) { + llvm::errs() << "ANALYZE: " << Loc.getFilename(); + + if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) { + const NamedDecl *ND = cast<NamedDecl>(D); + llvm::errs() << ' ' << ND << '\n'; + } + else if (isa<BlockDecl>(D)) { + llvm::errs() << ' ' << "block(line:" << Loc.getLine() << ",col:" + << Loc.getColumn() << '\n'; + } + else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { + Selector S = MD->getSelector(); + llvm::errs() << ' ' << S.getAsString(); + } + } + } + + void addCodeAction(CodeAction action) { + FunctionActions.push_back(action); + ObjCMethodActions.push_back(action); + CXXMethodActions.push_back(action); + } + + void addTranslationUnitAction(TUAction action) { + TranslationUnitActions.push_back(action); + } + + void addObjCImplementationAction(CodeAction action) { + ObjCImplementationActions.push_back(action); + } + + virtual void Initialize(ASTContext &Context) { + Ctx = &Context; + Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(), + PP.getLangOptions(), PD, + CreateStoreMgr, CreateConstraintMgr, + /* Indexer */ 0, + Opts.MaxNodes, Opts.MaxLoop, + Opts.VisualizeEGDot, Opts.VisualizeEGUbi, + Opts.PurgeDead, Opts.EagerlyAssume, + Opts.TrimGraph, Opts.InlineCall, + Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors, + Opts.CFGAddInitializers)); + } + + virtual void HandleTranslationUnit(ASTContext &C); + void HandleCode(Decl *D, Actions& actions); +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// AnalysisConsumer implementation. +//===----------------------------------------------------------------------===// + +void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { + + TranslationUnitDecl *TU = C.getTranslationUnitDecl(); + + for (DeclContext::decl_iterator I = TU->decls_begin(), E = TU->decls_end(); + I != E; ++I) { + Decl *D = *I; + + switch (D->getKind()) { + case Decl::CXXConstructor: + case Decl::CXXDestructor: + case Decl::CXXConversion: + case Decl::CXXMethod: + case Decl::Function: { + FunctionDecl* FD = cast<FunctionDecl>(D); + // We skip function template definitions, as their semantics is + // only determined when they are instantiated. + if (FD->isThisDeclarationADefinition() && + !FD->isDependentContext()) { + if (!Opts.AnalyzeSpecificFunction.empty() && + FD->getDeclName().getAsString() != Opts.AnalyzeSpecificFunction) + break; + DisplayFunction(FD); + HandleCode(FD, FunctionActions); + } + break; + } + + case Decl::ObjCImplementation: { + ObjCImplementationDecl* ID = cast<ObjCImplementationDecl>(*I); + HandleCode(ID, ObjCImplementationActions); + + for (ObjCImplementationDecl::method_iterator MI = ID->meth_begin(), + ME = ID->meth_end(); MI != ME; ++MI) { + if ((*MI)->isThisDeclarationADefinition()) { + if (!Opts.AnalyzeSpecificFunction.empty() && + Opts.AnalyzeSpecificFunction != (*MI)->getSelector().getAsString()) + break; + DisplayFunction(*MI); + HandleCode(*MI, ObjCMethodActions); + } + } + break; + } + + default: + break; + } + } + + for (TUActions::iterator I = TranslationUnitActions.begin(), + E = TranslationUnitActions.end(); I != E; ++I) { + (*I)(*this, *Mgr, *TU); + } + + // Explicitly destroy the PathDiagnosticClient. This will flush its output. + // FIXME: This should be replaced with something that doesn't rely on + // side-effects in PathDiagnosticClient's destructor. This is required when + // used with option -disable-free. + Mgr.reset(NULL); +} + +static void FindBlocks(DeclContext *D, llvm::SmallVectorImpl<Decl*> &WL) { + if (BlockDecl *BD = dyn_cast<BlockDecl>(D)) + WL.push_back(BD); + + for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end(); + I!=E; ++I) + if (DeclContext *DC = dyn_cast<DeclContext>(*I)) + FindBlocks(DC, WL); +} + +void AnalysisConsumer::HandleCode(Decl *D, Actions& actions) { + + // Don't run the actions if an error has occured with parsing the file. + Diagnostic &Diags = PP.getDiagnostics(); + if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) + return; + + // Don't run the actions on declarations in header files unless + // otherwise specified. + SourceManager &SM = Ctx->getSourceManager(); + SourceLocation SL = SM.getInstantiationLoc(D->getLocation()); + if (!Opts.AnalyzeAll && !SM.isFromMainFile(SL)) + return; + + // Clear the AnalysisManager of old AnalysisContexts. + Mgr->ClearContexts(); + + // Dispatch on the actions. + llvm::SmallVector<Decl*, 10> WL; + WL.push_back(D); + + if (D->hasBody() && Opts.AnalyzeNestedBlocks) + FindBlocks(cast<DeclContext>(D), WL); + + for (Actions::iterator I = actions.begin(), E = actions.end(); I != E; ++I) + for (llvm::SmallVectorImpl<Decl*>::iterator WI=WL.begin(), WE=WL.end(); + WI != WE; ++WI) + (*I)(*this, *Mgr, *WI); +} + +//===----------------------------------------------------------------------===// +// Analyses +//===----------------------------------------------------------------------===// + +static void ActionWarnDeadStores(AnalysisConsumer &C, AnalysisManager& mgr, + Decl *D) { + if (LiveVariables *L = mgr.getLiveVariables(D)) { + BugReporter BR(mgr); + CheckDeadStores(*mgr.getCFG(D), *L, mgr.getParentMap(D), BR); + } +} + +static void ActionWarnUninitVals(AnalysisConsumer &C, AnalysisManager& mgr, + Decl *D) { + if (CFG* c = mgr.getCFG(D)) { + CheckUninitializedValues(*c, mgr.getASTContext(), mgr.getDiagnostic()); + } +} + + +static void ActionExprEngine(AnalysisConsumer &C, AnalysisManager& mgr, + Decl *D, + TransferFuncs* tf) { + + llvm::OwningPtr<TransferFuncs> TF(tf); + + // Construct the analysis engine. We first query for the LiveVariables + // information to see if the CFG is valid. + // FIXME: Inter-procedural analysis will need to handle invalid CFGs. + if (!mgr.getLiveVariables(D)) + return; + ExprEngine Eng(mgr, TF.take()); + + if (C.Opts.EnableExperimentalInternalChecks) + RegisterExperimentalInternalChecks(Eng); + + RegisterAppleChecks(Eng, *D); + + if (C.Opts.EnableExperimentalChecks) + RegisterExperimentalChecks(Eng); + + // Enable idempotent operation checking if it was explicitly turned on, or if + // we are running experimental checks (i.e. everything) + if (C.Opts.IdempotentOps || C.Opts.EnableExperimentalChecks + || C.Opts.EnableExperimentalInternalChecks) + RegisterIdempotentOperationChecker(Eng); + + if (C.Opts.BufferOverflows) + RegisterArrayBoundCheckerV2(Eng); + + // Enable AnalyzerStatsChecker if it was given as an argument + if (C.Opts.AnalyzerStats) + RegisterAnalyzerStatsChecker(Eng); + + // Set the graph auditor. + llvm::OwningPtr<ExplodedNode::Auditor> Auditor; + if (mgr.shouldVisualizeUbigraph()) { + Auditor.reset(CreateUbiViz()); + ExplodedNode::SetAuditor(Auditor.get()); + } + + // Execute the worklist algorithm. + Eng.ExecuteWorkList(mgr.getStackFrame(D, 0), mgr.getMaxNodes()); + + // Release the auditor (if any) so that it doesn't monitor the graph + // created BugReporter. + ExplodedNode::SetAuditor(0); + + // Visualize the exploded graph. + if (mgr.shouldVisualizeGraphviz()) + Eng.ViewGraph(mgr.shouldTrimGraph()); + + // Display warnings. + Eng.getBugReporter().FlushReports(); +} + +static void ActionObjCMemCheckerAux(AnalysisConsumer &C, AnalysisManager& mgr, + Decl *D, bool GCEnabled) { + + TransferFuncs* TF = MakeCFRefCountTF(mgr.getASTContext(), + GCEnabled, + mgr.getLangOptions()); + + ActionExprEngine(C, mgr, D, TF); +} + +static void ActionObjCMemChecker(AnalysisConsumer &C, AnalysisManager& mgr, + Decl *D) { + + switch (mgr.getLangOptions().getGCMode()) { + default: + assert (false && "Invalid GC mode."); + case LangOptions::NonGC: + ActionObjCMemCheckerAux(C, mgr, D, false); + break; + + case LangOptions::GCOnly: + ActionObjCMemCheckerAux(C, mgr, D, true); + break; + + case LangOptions::HybridGC: + ActionObjCMemCheckerAux(C, mgr, D, false); + ActionObjCMemCheckerAux(C, mgr, D, true); + break; + } +} + +static void ActionDisplayLiveVariables(AnalysisConsumer &C, + AnalysisManager& mgr, Decl *D) { + if (LiveVariables* L = mgr.getLiveVariables(D)) { + L->dumpBlockLiveness(mgr.getSourceManager()); + } +} + +static void ActionCFGDump(AnalysisConsumer &C, AnalysisManager& mgr, Decl *D) { + if (CFG *cfg = mgr.getCFG(D)) { + cfg->dump(mgr.getLangOptions()); + } +} + +static void ActionCFGView(AnalysisConsumer &C, AnalysisManager& mgr, Decl *D) { + if (CFG *cfg = mgr.getCFG(D)) { + cfg->viewCFG(mgr.getLangOptions()); + } +} + +static void ActionSecuritySyntacticChecks(AnalysisConsumer &C, + AnalysisManager &mgr, Decl *D) { + BugReporter BR(mgr); + CheckSecuritySyntaxOnly(D, BR); +} + +static void ActionLLVMConventionChecker(AnalysisConsumer &C, + AnalysisManager &mgr, + TranslationUnitDecl &TU) { + BugReporter BR(mgr); + CheckLLVMConventions(TU, BR); +} + +static void ActionWarnObjCDealloc(AnalysisConsumer &C, AnalysisManager& mgr, + Decl *D) { + if (mgr.getLangOptions().getGCMode() == LangOptions::GCOnly) + return; + BugReporter BR(mgr); + CheckObjCDealloc(cast&l |