diff options
author | rdivacky <rdivacky@FreeBSD.org> | 2009-11-04 15:04:32 +0000 |
---|---|---|
committer | rdivacky <rdivacky@FreeBSD.org> | 2009-11-04 15:04:32 +0000 |
commit | b6d5e15aae202f157c6cd63da8fa4b089e7b31e9 (patch) | |
tree | 59e0e47a9831dcf0e21e547927c8ebb7e113bfd1 /lib/Analysis | |
parent | 5563df30b9c8d1fe87a54baae0d6bd86642563f4 (diff) | |
download | FreeBSD-src-b6d5e15aae202f157c6cd63da8fa4b089e7b31e9.zip FreeBSD-src-b6d5e15aae202f157c6cd63da8fa4b089e7b31e9.tar.gz |
Update clang to r86025.
Diffstat (limited to 'lib/Analysis')
-rw-r--r-- | lib/Analysis/AttrNonNullChecker.cpp | 100 | ||||
-rw-r--r-- | lib/Analysis/BadCallChecker.cpp | 44 | ||||
-rw-r--r-- | lib/Analysis/BasicObjCFoundationChecks.cpp | 1 | ||||
-rw-r--r-- | lib/Analysis/BasicObjCFoundationChecks.h | 1 | ||||
-rw-r--r-- | lib/Analysis/BasicStore.cpp | 26 | ||||
-rw-r--r-- | lib/Analysis/CFRefCount.cpp | 34 | ||||
-rw-r--r-- | lib/Analysis/CMakeLists.txt | 10 | ||||
-rw-r--r-- | lib/Analysis/CallGraph.cpp | 4 | ||||
-rw-r--r-- | lib/Analysis/CheckObjCUnusedIVars.cpp | 35 | ||||
-rw-r--r-- | lib/Analysis/DereferenceChecker.cpp | 112 | ||||
-rw-r--r-- | lib/Analysis/DivZeroChecker.cpp | 70 | ||||
-rw-r--r-- | lib/Analysis/GRExprEngine.cpp | 391 | ||||
-rw-r--r-- | lib/Analysis/GRExprEngineInternalChecks.cpp | 390 | ||||
-rw-r--r-- | lib/Analysis/NSAutoreleasePoolChecker.cpp | 84 | ||||
-rw-r--r-- | lib/Analysis/NSErrorChecker.cpp (renamed from lib/Analysis/CheckNSError.cpp) | 31 | ||||
-rw-r--r-- | lib/Analysis/RegionStore.cpp | 39 | ||||
-rw-r--r-- | lib/Analysis/UndefinedArgChecker.cpp | 43 | ||||
-rw-r--r-- | lib/Analysis/UndefinedAssignmentChecker.cpp | 61 | ||||
-rw-r--r-- | lib/Analysis/VLASizeChecker.cpp | 102 |
19 files changed, 928 insertions, 650 deletions
diff --git a/lib/Analysis/AttrNonNullChecker.cpp b/lib/Analysis/AttrNonNullChecker.cpp new file mode 100644 index 0000000..1cf5d0c --- /dev/null +++ b/lib/Analysis/AttrNonNullChecker.cpp @@ -0,0 +1,100 @@ +//===--- AttrNonNullChecker.h - Undefined arguments checker ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines AttrNonNullChecker, a builtin check in GRExprEngine that +// performs checks for arguments declared to have nonnull attribute. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/Checkers/AttrNonNullChecker.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" + +using namespace clang; + +void *AttrNonNullChecker::getTag() { + static int x = 0; + return &x; +} + +void AttrNonNullChecker::PreVisitCallExpr(CheckerContext &C, + const CallExpr *CE) { + const GRState *state = C.getState(); + const GRState *originalState = state; + + // Check if the callee has a 'nonnull' attribute. + SVal X = state->getSVal(CE->getCallee()); + + const FunctionDecl* FD = X.getAsFunctionDecl(); + if (!FD) + return; + + const NonNullAttr* Att = FD->getAttr<NonNullAttr>(); + if (!Att) + return; + + // Iterate through the arguments of CE and check them for null. + unsigned idx = 0; + + for (CallExpr::const_arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E; + ++I, ++idx) { + + if (!Att->isNonNull(idx)) + continue; + + const SVal &V = state->getSVal(*I); + const DefinedSVal *DV = dyn_cast<DefinedSVal>(&V); + + if (!DV) + continue; + + ConstraintManager &CM = C.getConstraintManager(); + const GRState *stateNotNull, *stateNull; + llvm::tie(stateNotNull, stateNull) = CM.AssumeDual(state, *DV); + + if (stateNull && !stateNotNull) { + // Generate an error node. Check for a null node in case + // we cache out. + if (ExplodedNode *errorNode = C.GenerateNode(CE, stateNull, true)) { + + // Lazily allocate the BugType object if it hasn't already been + // created. Ownership is transferred to the BugReporter object once + // the BugReport is passed to 'EmitWarning'. + if (!BT) + BT = new BugType("Argument with 'nonnull' attribute passed null", + "API"); + + EnhancedBugReport *R = + new EnhancedBugReport(*BT, + "Null pointer passed as an argument to a " + "'nonnull' parameter", errorNode); + + // Highlight the range of the argument that was null. + const Expr *arg = *I; + R->addRange(arg->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, arg); + + // Emit the bug report. + C.EmitReport(R); + } + + // Always return. Either we cached out or we just emitted an error. + return; + } + + // If a pointer value passed the check we should assume that it is + // indeed not null from this point forward. + assert(stateNotNull); + state = stateNotNull; + } + + // If we reach here all of the arguments passed the nonnull check. + // If 'state' has been updated generated a new node. + if (state != originalState) + C.addTransition(C.GenerateNode(CE, state)); +} diff --git a/lib/Analysis/BadCallChecker.cpp b/lib/Analysis/BadCallChecker.cpp new file mode 100644 index 0000000..33bb515 --- /dev/null +++ b/lib/Analysis/BadCallChecker.cpp @@ -0,0 +1,44 @@ +//===--- BadCallChecker.h - Bad call checker --------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines BadCallChecker, a builtin check in GRExprEngine that performs +// checks for bad callee at call sites. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/Checkers/BadCallChecker.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" + +using namespace clang; + +void *BadCallChecker::getTag() { + static int x = 0; + return &x; +} + +void BadCallChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { + const Expr *Callee = CE->getCallee()->IgnoreParens(); + SVal L = C.getState()->getSVal(Callee); + + if (L.isUndef() || isa<loc::ConcreteInt>(L)) { + if (ExplodedNode *N = C.GenerateNode(CE, true)) { + if (!BT) + BT = new BuiltinBug(0, "Invalid function call", + "Called function pointer is a null or undefined pointer value"); + + EnhancedBugReport *R = + new EnhancedBugReport(*BT, BT->getDescription().c_str(), N); + + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetCalleeExpr(N)); + + C.EmitReport(R); + } + } +} diff --git a/lib/Analysis/BasicObjCFoundationChecks.cpp b/lib/Analysis/BasicObjCFoundationChecks.cpp index aa2d0ab..4781d5e 100644 --- a/lib/Analysis/BasicObjCFoundationChecks.cpp +++ b/lib/Analysis/BasicObjCFoundationChecks.cpp @@ -535,4 +535,5 @@ void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) { Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass); RegisterNSErrorChecks(BR, Eng, D); + RegisterNSAutoreleasePoolChecks(Eng); } diff --git a/lib/Analysis/BasicObjCFoundationChecks.h b/lib/Analysis/BasicObjCFoundationChecks.h index 1271ae4..ea4d3ec 100644 --- a/lib/Analysis/BasicObjCFoundationChecks.h +++ b/lib/Analysis/BasicObjCFoundationChecks.h @@ -42,6 +42,7 @@ GRSimpleAPICheck *CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR); void RegisterNSErrorChecks(BugReporter& BR, GRExprEngine &Eng, const Decl &D); +void RegisterNSAutoreleasePoolChecks(GRExprEngine &Eng); } // end clang namespace diff --git a/lib/Analysis/BasicStore.cpp b/lib/Analysis/BasicStore.cpp index d81d83c..888af9b 100644 --- a/lib/Analysis/BasicStore.cpp +++ b/lib/Analysis/BasicStore.cpp @@ -92,19 +92,17 @@ public: void iterBindings(Store store, BindingsHandler& f); - const GRState *BindDecl(const GRState *state, const VarDecl *VD, - const LocationContext *LC, SVal InitVal) { - return state->makeWithStore(BindDeclInternal(state->getStore(),VD, LC, + const GRState *BindDecl(const GRState *state, const VarRegion *VR, + SVal InitVal) { + return state->makeWithStore(BindDeclInternal(state->getStore(), VR, &InitVal)); } - const GRState *BindDeclWithNoInit(const GRState *state, const VarDecl *VD, - const LocationContext *LC) { - return state->makeWithStore(BindDeclInternal(state->getStore(), VD, LC, 0)); + const GRState *BindDeclWithNoInit(const GRState *state, const VarRegion *VR) { + return state->makeWithStore(BindDeclInternal(state->getStore(), VR, 0)); } - Store BindDeclInternal(Store store, const VarDecl *VD, - const LocationContext *LC, SVal *InitVal); + Store BindDeclInternal(Store store, const VarRegion *VR, SVal *InitVal); static inline BindingsTy GetBindings(Store store) { return BindingsTy(static_cast<const BindingsTy::TreeTy*>(store)); @@ -532,11 +530,11 @@ Store BasicStoreManager::getInitialStore(const LocationContext *InitLoc) { return St; } -Store BasicStoreManager::BindDeclInternal(Store store, const VarDecl* VD, - const LocationContext *LC, +Store BasicStoreManager::BindDeclInternal(Store store, const VarRegion* VR, SVal* InitVal) { BasicValueFactory& BasicVals = StateMgr.getBasicVals(); + const VarDecl *VD = VR->getDecl(); // BasicStore does not model arrays and structs. if (VD->getType()->isArrayType() || VD->getType()->isStructureType()) @@ -564,16 +562,16 @@ Store BasicStoreManager::BindDeclInternal(Store store, const VarDecl* VD, if (!InitVal) { QualType T = VD->getType(); if (Loc::IsLocType(T)) - store = BindInternal(store, getLoc(VD, LC), + store = BindInternal(store, loc::MemRegionVal(VR), loc::ConcreteInt(BasicVals.getValue(0, T))); else if (T->isIntegerType()) - store = BindInternal(store, getLoc(VD, LC), + store = BindInternal(store, loc::MemRegionVal(VR), nonloc::ConcreteInt(BasicVals.getValue(0, T))); else { // assert(0 && "ignore other types of variables"); } } else { - store = BindInternal(store, getLoc(VD, LC), *InitVal); + store = BindInternal(store, loc::MemRegionVal(VR), *InitVal); } } } else { @@ -581,7 +579,7 @@ Store BasicStoreManager::BindDeclInternal(Store store, const VarDecl* VD, QualType T = VD->getType(); if (ValMgr.getSymbolManager().canSymbolicate(T)) { SVal V = InitVal ? *InitVal : UndefinedVal(); - store = BindInternal(store, getLoc(VD, LC), V); + store = BindInternal(store, loc::MemRegionVal(VR), V); } } diff --git a/lib/Analysis/CFRefCount.cpp b/lib/Analysis/CFRefCount.cpp index c629ad1..03614e8 100644 --- a/lib/Analysis/CFRefCount.cpp +++ b/lib/Analysis/CFRefCount.cpp @@ -193,20 +193,6 @@ public: } // end anonymous namespace //===----------------------------------------------------------------------===// -// Selector creation functions. -//===----------------------------------------------------------------------===// - -static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) { - IdentifierInfo* II = &Ctx.Idents.get(name); - return Ctx.Selectors.getSelector(0, &II); -} - -static inline Selector GetUnarySelector(const char* name, ASTContext& Ctx) { - IdentifierInfo* II = &Ctx.Idents.get(name); - return Ctx.Selectors.getSelector(1, &II); -} - -//===----------------------------------------------------------------------===// // Type querying functions. //===----------------------------------------------------------------------===// @@ -1031,11 +1017,25 @@ RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD) { // Eventually this can be improved by recognizing that the pixel // buffer passed to CVPixelBufferCreateWithBytes is released via // a callback and doing full IPA to make sure this is done correctly. + // FIXME: This function has an out parameter that returns an + // allocated object. ScratchArgs = AF.Add(ScratchArgs, 7, StopTracking); S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); } break; + + case 29: + if (!memcmp(FName, "CGBitmapContextCreateWithData", 29)) { + // FIXES: <rdar://problem/7358899> + // Eventually this can be improved by recognizing that 'releaseInfo' + // passed to CGBitmapContextCreateWithData is released via + // a callback and doing full IPA to make sure this is done correctly. + ScratchArgs = AF.Add(ScratchArgs, 8, StopTracking); + S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), + DoNothing,DoNothing); + } + break; case 32: if (!memcmp(FName, "IOServiceAddMatchingNotification", 32)) { @@ -1899,7 +1899,7 @@ public: virtual ~CFRefCount() {} - void RegisterChecks(BugReporter &BR); + void RegisterChecks(GRExprEngine &Eng); virtual void RegisterPrinters(std::vector<GRState::Printer*>& Printers) { Printers.push_back(new BindingsPrinter()); @@ -2193,7 +2193,9 @@ namespace { }; } // end anonymous namespace -void CFRefCount::RegisterChecks(BugReporter& BR) { +void CFRefCount::RegisterChecks(GRExprEngine& Eng) { + BugReporter &BR = Eng.getBugReporter(); + useAfterRelease = new UseAfterRelease(this); BR.Register(useAfterRelease); diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index 89c1783..cd4697f 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/lib/Analysis/CMakeLists.txt @@ -3,6 +3,8 @@ set(LLVM_NO_RTTI 1) add_clang_library(clangAnalysis AnalysisContext.cpp AnalysisManager.cpp + AttrNonNullChecker.cpp + BadCallChecker.cpp BasicConstraintManager.cpp BasicObjCFoundationChecks.cpp BasicStore.cpp @@ -14,11 +16,12 @@ add_clang_library(clangAnalysis CallGraph.cpp CallInliner.cpp CheckDeadStores.cpp - CheckNSError.cpp CheckObjCDealloc.cpp CheckObjCInstMethSignature.cpp CheckObjCUnusedIVars.cpp CheckSecuritySyntaxOnly.cpp + DereferenceChecker.cpp + DivZeroChecker.cpp Environment.cpp ExplodedGraph.cpp GRBlockCounter.cpp @@ -28,6 +31,8 @@ add_clang_library(clangAnalysis GRState.cpp LiveVariables.cpp MemRegion.cpp + NSAutoreleasePoolChecker.cpp + NSErrorChecker.cpp PathDiagnostic.cpp RangeConstraintManager.cpp RegionStore.cpp @@ -37,8 +42,11 @@ add_clang_library(clangAnalysis SimpleSValuator.cpp Store.cpp SymbolManager.cpp + UndefinedArgChecker.cpp + UndefinedAssignmentChecker.cpp UninitializedValues.cpp ValueManager.cpp + VLASizeChecker.cpp ) add_dependencies(clangAnalysis ClangDiagnosticAnalysis) diff --git a/lib/Analysis/CallGraph.cpp b/lib/Analysis/CallGraph.cpp index ae8845d..17dc068 100644 --- a/lib/Analysis/CallGraph.cpp +++ b/lib/Analysis/CallGraph.cpp @@ -68,10 +68,8 @@ CallGraph::~CallGraph() { } } -void CallGraph::addTU(ASTUnit &AST) { - ASTContext &Ctx = AST.getASTContext(); +void CallGraph::addTU(ASTContext& Ctx) { DeclContext *DC = Ctx.getTranslationUnitDecl(); - for (DeclContext::decl_iterator I = DC->decls_begin(), E = DC->decls_end(); I != E; ++I) { diff --git a/lib/Analysis/CheckObjCUnusedIVars.cpp b/lib/Analysis/CheckObjCUnusedIVars.cpp index 1a900f8..2d9b531 100644 --- a/lib/Analysis/CheckObjCUnusedIVars.cpp +++ b/lib/Analysis/CheckObjCUnusedIVars.cpp @@ -62,6 +62,29 @@ static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl* D) { I->second = Used; } +static void Scan(IvarUsageMap& M, const ObjCContainerDecl* D) { + // Scan the methods for accesses. + for (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(), + E = D->instmeth_end(); I!=E; ++I) + Scan(M, (*I)->getBody()); + + if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) { + // Scan for @synthesized property methods that act as setters/getters + // to an ivar. + for (ObjCImplementationDecl::propimpl_iterator I = ID->propimpl_begin(), + E = ID->propimpl_end(); I!=E; ++I) + Scan(M, *I); + + // Scan the associated categories as well. + for (const ObjCCategoryDecl *CD = + ID->getClassInterface()->getCategoryList(); CD ; + CD = CD->getNextClassCategory()) { + if (const ObjCCategoryImplDecl *CID = CD->getImplementation()) + Scan(M, CID); + } + } +} + void clang::CheckObjCUnusedIvar(const ObjCImplementationDecl *D, BugReporter &BR) { @@ -88,16 +111,8 @@ void clang::CheckObjCUnusedIvar(const ObjCImplementationDecl *D, if (M.empty()) return; - // Now scan the methods for accesses. - for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), - E = D->instmeth_end(); I!=E; ++I) - Scan(M, (*I)->getBody()); - - // Scan for @synthesized property methods that act as setters/getters - // to an ivar. - for (ObjCImplementationDecl::propimpl_iterator I = D->propimpl_begin(), - E = D->propimpl_end(); I!=E; ++I) - Scan(M, *I); + // Now scan the implementation declaration. + Scan(M, D); // Find ivars that are unused. for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) diff --git a/lib/Analysis/DereferenceChecker.cpp b/lib/Analysis/DereferenceChecker.cpp new file mode 100644 index 0000000..33c85d5 --- /dev/null +++ b/lib/Analysis/DereferenceChecker.cpp @@ -0,0 +1,112 @@ +//== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines NullDerefChecker, a builtin check in GRExprEngine that performs +// checks for null pointers at loads and stores. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/Checkers/DereferenceChecker.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" + +using namespace clang; + +void *NullDerefChecker::getTag() { + static int x = 0; + return &x; +} + +ExplodedNode *NullDerefChecker::CheckLocation(const Stmt *S, ExplodedNode *Pred, + const GRState *state, SVal V, + GRExprEngine &Eng) { + Loc *LV = dyn_cast<Loc>(&V); + + // If the value is not a location, don't touch the node. + if (!LV) + return Pred; + + const GRState *NotNullState = state->Assume(*LV, true); + const GRState *NullState = state->Assume(*LV, false); + + GRStmtNodeBuilder &Builder = Eng.getBuilder(); + BugReporter &BR = Eng.getBugReporter(); + + // The explicit NULL case. + if (NullState) { + // Use the GDM to mark in the state what lval was null. + const SVal *PersistentLV = Eng.getBasicVals().getPersistentSVal(*LV); + NullState = NullState->set<GRState::NullDerefTag>(PersistentLV); + + ExplodedNode *N = Builder.generateNode(S, NullState, Pred, + ProgramPoint::PostNullCheckFailedKind); + if (N) { + N->markAsSink(); + + if (!NotNullState) { // Explicit null case. + if (!BT) + BT = new BuiltinBug(NULL, "Null dereference", + "Dereference of null pointer"); + + EnhancedBugReport *R = + new EnhancedBugReport(*BT, BT->getDescription().c_str(), N); + + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetDerefExpr(N)); + + BR.EmitReport(R); + + return 0; + } else // Implicit null case. + ImplicitNullDerefNodes.push_back(N); + } + } + + if (!NotNullState) + return 0; + + return Builder.generateNode(S, NotNullState, Pred, + ProgramPoint::PostLocationChecksSucceedKind); +} + + +void *UndefDerefChecker::getTag() { + static int x = 0; + return &x; +} + +ExplodedNode *UndefDerefChecker::CheckLocation(const Stmt *S, + ExplodedNode *Pred, + const GRState *state, SVal V, + GRExprEngine &Eng) { + GRStmtNodeBuilder &Builder = Eng.getBuilder(); + BugReporter &BR = Eng.getBugReporter(); + + if (V.isUndef()) { + ExplodedNode *N = Builder.generateNode(S, state, Pred, + ProgramPoint::PostUndefLocationCheckFailedKind); + if (N) { + N->markAsSink(); + + if (!BT) + BT = new BuiltinBug(0, "Undefined dereference", + "Dereference of undefined pointer value"); + + EnhancedBugReport *R = + new EnhancedBugReport(*BT, BT->getDescription().c_str(), N); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetDerefExpr(N)); + BR.EmitReport(R); + } + return 0; + } + + return Pred; +} + diff --git a/lib/Analysis/DivZeroChecker.cpp b/lib/Analysis/DivZeroChecker.cpp new file mode 100644 index 0000000..9c2359f --- /dev/null +++ b/lib/Analysis/DivZeroChecker.cpp @@ -0,0 +1,70 @@ +//== DivZeroChecker.cpp - Division by zero checker --------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines DivZeroChecker, a builtin check in GRExprEngine that performs +// checks for division by zeros. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/Checkers/DivZeroChecker.h" + +using namespace clang; + +void *DivZeroChecker::getTag() { + static int x; + return &x; +} + +void DivZeroChecker::PreVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + BinaryOperator::Opcode Op = B->getOpcode(); + if (Op != BinaryOperator::Div && + Op != BinaryOperator::Rem && + Op != BinaryOperator::DivAssign && + Op != BinaryOperator::RemAssign) + return; + + if (!B->getRHS()->getType()->isIntegerType() || + !B->getRHS()->getType()->isScalarType()) + return; + + SVal Denom = C.getState()->getSVal(B->getRHS()); + const DefinedSVal *DV = dyn_cast<DefinedSVal>(&Denom); + + // Divide-by-undefined handled in the generic checking for uses of + // undefined values. + if (!DV) + return; + + // Check for divide by zero. + ConstraintManager &CM = C.getConstraintManager(); + const GRState *stateNotZero, *stateZero; + llvm::tie(stateNotZero, stateZero) = CM.AssumeDual(C.getState(), *DV); + + if (stateZero && !stateNotZero) { + if (ExplodedNode *N = C.GenerateNode(B, stateZero, true)) { + if (!BT) + BT = new BuiltinBug(0, "Division by zero"); + + EnhancedBugReport *R = + new EnhancedBugReport(*BT, BT->getDescription().c_str(), N); + + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetDenomExpr(N)); + + C.EmitReport(R); + } + return; + } + + // If we get here, then the denom should not be zero. We abandon the implicit + // zero denom case for now. + if (stateNotZero != C.getState()) + C.addTransition(C.GenerateNode(B, stateNotZero)); +} diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp index ea0255d..c71882e 100644 --- a/lib/Analysis/GRExprEngine.cpp +++ b/lib/Analysis/GRExprEngine.cpp @@ -25,6 +25,7 @@ #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/StringSwitch.h" #ifndef NDEBUG #include "llvm/Support/GraphWriter.h" @@ -117,17 +118,18 @@ void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet Tmp; ExplodedNodeSet *PrevSet = &Src; - for (std::vector<Checker*>::iterator I = Checkers.begin(), E = Checkers.end(); - I != E; ++I) { - - ExplodedNodeSet *CurrSet = (I+1 == E) ? &Dst + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) + { + ExplodedNodeSet *CurrSet = (I+1 == E) ? &Dst : (PrevSet == &Tmp) ? &Src : &Tmp; + CurrSet->clear(); - Checker *checker = *I; + void *tag = I->first; + Checker *checker = I->second; for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); NI != NE; ++NI) - checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, isPrevisit); + checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, tag, isPrevisit); // Update which NodeSet is the current one. PrevSet = CurrSet; @@ -137,6 +139,41 @@ void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, // automatically. } +// FIXME: This is largely copy-paste from CheckerVisit(). Need to +// unify. +void GRExprEngine::CheckerVisitBind(Stmt *S, ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, + SVal location, SVal val, bool isPrevisit) { + + if (Checkers.empty()) { + Dst = Src; + return; + } + + ExplodedNodeSet Tmp; + ExplodedNodeSet *PrevSet = &Src; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) + { + ExplodedNodeSet *CurrSet = (I+1 == E) ? &Dst + : (PrevSet == &Tmp) ? &Src : &Tmp; + + CurrSet->clear(); + void *tag = I->first; + Checker *checker = I->second; + + for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); + NI != NE; ++NI) + checker->GR_VisitBind(*CurrSet, *Builder, *this, S, *NI, tag, location, + val, isPrevisit); + + // Update which NodeSet is the current one. + PrevSet = CurrSet; + } + + // Don't autotransition. The CheckerContext objects should do this + // automatically. +} //===----------------------------------------------------------------------===// // Engine construction and deletion. //===----------------------------------------------------------------------===// @@ -165,9 +202,8 @@ GRExprEngine::GRExprEngine(AnalysisManager &mgr) GRExprEngine::~GRExprEngine() { BR.FlushReports(); delete [] NSExceptionInstanceRaiseSelectors; - for (std::vector<Checker*>::iterator I=Checkers.begin(), E=Checkers.end(); - I!=E; ++I) - delete *I; + for (CheckersOrdered::iterator I=Checkers.begin(), E=Checkers.end(); I!=E;++I) + delete I->second; } //===----------------------------------------------------------------------===// @@ -177,7 +213,7 @@ GRExprEngine::~GRExprEngine() { void GRExprEngine::setTransferFunctions(GRTransferFuncs* tf) { StateMgr.TF = tf; - tf->RegisterChecks(getBugReporter()); + tf->RegisterChecks(*this); tf->RegisterPrinters(getStateManager().Printers); } @@ -369,11 +405,11 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { if (AMgr.shouldEagerlyAssume() && (B->isRelationalOp() || B->isEqualityOp())) { ExplodedNodeSet Tmp; - VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Tmp); + VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Tmp, false); EvalEagerlyAssume(Dst, Tmp, cast<Expr>(S)); } else - VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst); + VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst, false); break; } @@ -395,7 +431,7 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { } case Stmt::CompoundAssignOperatorClass: - VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst); + VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst, false); break; case Stmt::CompoundLiteralExprClass: @@ -409,7 +445,6 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { } case Stmt::DeclRefExprClass: - case Stmt::QualifiedDeclRefExprClass: VisitDeclRefExpr(cast<DeclRefExpr>(S), Pred, Dst, false); break; @@ -522,7 +557,6 @@ void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred, return; case Stmt::DeclRefExprClass: - case Stmt::QualifiedDeclRefExprClass: VisitDeclRefExpr(cast<DeclRefExpr>(Ex), Pred, Dst, true); return; @@ -565,6 +599,11 @@ void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred, return; } + case Stmt::BinaryOperatorClass: + case Stmt::CompoundAssignOperatorClass: + VisitBinaryOperator(cast<BinaryOperator>(Ex), Pred, Dst, true); + return; + default: // Arbitrary subexpressions can return aggregate temporaries that // can be used in a lvalue context. We need to enhance our support @@ -1078,8 +1117,7 @@ void GRExprEngine::VisitMemberExpr(MemberExpr* M, ExplodedNode* Pred, SVal L = state->getLValue(Field, state->getSVal(Base)); if (asLValue) - MakeNode(Dst, M, *I, state->BindExpr(M, L), - ProgramPoint::PostLValueKind); + MakeNode(Dst, M, *I, state->BindExpr(M, L), ProgramPoint::PostLValueKind); else EvalLoad(Dst, M, *I, state, L); } @@ -1087,30 +1125,52 @@ void GRExprEngine::VisitMemberExpr(MemberExpr* M, ExplodedNode* Pred, /// EvalBind - Handle the semantics of binding a value to a specific location. /// This method is used by EvalStore and (soon) VisitDeclStmt, and others. -void GRExprEngine::EvalBind(ExplodedNodeSet& Dst, Expr* Ex, ExplodedNode* Pred, - const GRState* state, SVal location, SVal Val) { +void GRExprEngine::EvalBind(ExplodedNodeSet& Dst, Stmt* Ex, ExplodedNode* Pred, + const GRState* state, SVal location, SVal Val, + bool atDeclInit) { + + + // Do a previsit of the bind. + ExplodedNodeSet CheckedSet, Src; + Src.Add(Pred); + CheckerVisitBind(Ex, CheckedSet, Src, location, Val, true); + + for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); + I!=E; ++I) { + + if (Pred != *I) + state = GetState(*I); + + const GRState* newState = 0; - const GRState* newState = 0; + if (atDeclInit) { + const VarRegion *VR = + cast<VarRegion>(cast<loc::MemRegionVal>(location).getRegion()); - if (location.isUnknown()) { - // We know that the new state will be the same as the old state since - // the location of the binding is "unknown". Consequently, there - // is no reason to just create a new node. - newState = state; - } - else { - // We are binding to a value other than 'unknown'. Perform the binding - // using the StoreManager. - newState = state->bindLoc(cast<Loc>(location), Val); - } + newState = state->bindDecl(VR, Val); + } + else { + if (location.isUnknown()) { + // We know that the new state will be the same as the old state since + // the location of the binding is "unknown". Consequently, there + // is no reason to just create a new node. + newState = state; + } + else { + // We are binding to a value other than 'unknown'. Perform the binding + // using the StoreManager. + newState = state->bindLoc(cast<Loc>(location), Val); + } + } - // The next thing to do is check if the GRTransferFuncs object wants to - // update the state based on the new binding. If the GRTransferFunc object - // doesn't do anything, just auto-propagate the current state. - GRStmtNodeBuilderRef BuilderRef(Dst, *Builder, *this, Pred, newState, Ex, - newState != state); + // The next thing to do is check if the GRTransferFuncs object wants to + // update the state based on the new binding. If the GRTransferFunc object + // doesn't do anything, just auto-propagate the current state. + GRStmtNodeBuilderRef BuilderRef(Dst, *Builder, *this, *I, newState, Ex, + newState != state); - getTF().EvalBind(BuilderRef, location, Val); + getTF().EvalBind(BuilderRef, location, Val); + } } /// EvalStore - Handle the semantics of a store via an assignment. @@ -1189,58 +1249,18 @@ ExplodedNode* GRExprEngine::EvalLocation(Stmt* Ex, ExplodedNode* Pred, SaveAndRestore<const void*> OldTag(Builder->Tag); Builder->Tag = tag; - // Check for loads/stores from/to undefined values. - if (location.isUndef()) { - ExplodedNode* N = - Builder->generateNode(Ex, state, Pred, - ProgramPoint::PostUndefLocationCheckFailedKind); - - if (N) { - N->markAsSink(); - UndefDeref.insert(N); - } - - return 0; - } - - // Check for loads/stores from/to unknown locations. Treat as No-Ops. - if (location.isUnknown()) + if (location.isUnknown() || Checkers.empty()) return Pred; - // During a load, one of two possible situations arise: - // (1) A crash, because the location (pointer) was NULL. - // (2) The location (pointer) is not NULL, and the dereference works. - // - // We add these assumptions. - - Loc LV = cast<Loc>(location); - - // "Assume" that the pointer is not NULL. - const GRState *StNotNull = state->Assume(LV, true); - - // "Assume" that the pointer is NULL. - const GRState *StNull = state->Assume(LV, false); - - if (StNull) { - // Use the Generic Data Map to mark in the state what lval was null. - const SVal* PersistentLV = getBasicVals().getPersistentSVal(LV); - StNull = StNull->set<GRState::NullDerefTag>(PersistentLV); - - // We don't use "MakeNode" here because the node will be a sink - // and we have no intention of processing it later. - ExplodedNode* NullNode = - Builder->generateNode(Ex, StNull, Pred, - ProgramPoint::PostNullCheckFailedKind); - - if (NullNode) { - NullNode->markAsSink(); - if (StNotNull) ImplicitNullDeref.insert(NullNode); - else ExplicitNullDeref.insert(NullNode); - } + + for (CheckersOrdered::iterator I=Checkers.begin(), E=Checkers.end(); I!=E;++I) + { + Pred = I->second->CheckLocation(Ex, Pred, state, location, *this); + if (!Pred) + break; } - - if (!StNotNull) - return NULL; + + return Pred; // FIXME: Temporarily disable out-of-bounds checking until we make // the logic reflect recent changes to CastRegion and friends. @@ -1283,10 +1303,6 @@ ExplodedNode* GRExprEngine::EvalLocation(Stmt* Ex, ExplodedNode* Pred, } } #endif - - // Generate a new node indicating the checks succeed. - return Builder->generateNode(Ex, StNotNull, Pred, - ProgramPoint::PostLocationChecksSucceedKind); } //===----------------------------------------------------------------------===// @@ -1445,75 +1461,30 @@ static void MarkNoReturnFunction(const FunctionDecl *FD, CallExpr *CE, // HACK: Some functions are not marked noreturn, and don't return. // Here are a few hardwired ones. If this takes too long, we can // potentially cache these results. - const char* s = FD->getIdentifier()->getNameStart(); - - switch (FD->getIdentifier()->getLength()) { - default: - break; - - case 4: - if (!memcmp(s, "exit", 4)) Builder->BuildSinks = true; - break; - - case 5: - if (!memcmp(s, "panic", 5)) Builder->BuildSinks = true; - else if (!memcmp(s, "error", 5)) { - if (CE->getNumArgs() > 0) { - SVal X = state->getSVal(*CE->arg_begin()); - // FIXME: use Assume to inspect the possible symbolic value of - // X. Also check the specific signature of error(). - nonloc::ConcreteInt* CI = dyn_cast<nonloc::ConcreteInt>(&X); - if (CI && CI->getValue() != 0) - Builder->BuildSinks = true; - } - } - break; - - case 6: - if (!memcmp(s, "Assert", 6)) { - Builder->BuildSinks = true; - break; - } - - // FIXME: This is just a wrapper around throwing an exception. - // Eventually inter-procedural analysis should handle this easily. - if (!memcmp(s, "ziperr", 6)) Builder->BuildSinks = true; - - break; - - case 7: - if (!memcmp(s, "assfail", 7)) Builder->BuildSinks = true; - break; - - case 8: - if (!memcmp(s ,"db_error", 8) || - !memcmp(s, "__assert", 8)) - Builder->BuildSinks = true; - break; - - case 12: - if (!memcmp(s, "__assert_rtn", 12)) Builder->BuildSinks = true; - break; - - case 13: - if (!memcmp(s, "__assert_fail", 13)) Builder->BuildSinks = true; - break; - - case 14: - if (!memcmp(s, "dtrace_assfail", 14) || - !memcmp(s, "yy_fatal_error", 14)) - Builder->BuildSinks = true; - break; - - case 26: - if (!memcmp(s, "_XCAssertionFailureHandler", 26) || - !memcmp(s, "_DTAssertionFailureHandler", 26) || - !memcmp(s, "_TSAssertionFailureHandler", 26)) - Builder->BuildSinks = true; - - break; - } - + using llvm::StringRef; + bool BuildSinks + = llvm::StringSwitch<bool>(StringRef(FD->getIdentifier()->getName())) + .Case("exit", true) + .Case("panic", true) + .Case("error", true) + .Case("Assert", true) + // FIXME: This is just a wrapper around throwing an exception. + // Eventually inter-procedural analysis should handle this easily. + .Case("ziperr", true) + .Case("assfail", true) + .Case("db_error", true) + .Case("__assert", true) + .Case("__assert_rtn", true) + .Case("__assert_fail", true) + .Case("dtrace_assfail", true) + .Case("yy_fatal_error", true) + .Case("_XCAssertionFailureHandler", true) + .Case("_DTAssertionFailureHandler", true) + .Case("_TSAssertionFailureHandler", true) + .Default(false); + + if (BuildSinks) + Builder->BuildSinks = true; } } @@ -2042,24 +2013,27 @@ void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME, } } + // Handle previsits checks. + ExplodedNodeSet Src, DstTmp; + Src.Add(Pred); + CheckerVisit(ME, DstTmp, Src, true); + // Check if we raise an exception. For now treat these as sinks. Eventually // we will want to handle exceptions properly. - SaveAndRestore<bool> OldSink(Builder->BuildSinks); - if (RaisesException) Builder->BuildSinks = true; // Dispatch to plug-in transfer function. - unsigned size = Dst.size(); SaveOr OldHasGen(Builder->HasGeneratedNode); - - EvalObjCMessageExpr(Dst, ME, Pred); + + for (ExplodedNodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end(); + DI!=DE; ++DI) + EvalObjCMessageExpr(Dst, ME, *DI); // Handle the case where no nodes where generated. Auto-generate that // contains the updated state if we aren't generating sinks. - if (!Builder->BuildSinks && Dst.size() == size && !Builder->HasGeneratedNode) MakeNode(Dst, ME, Pred, state); } @@ -2141,45 +2115,25 @@ void GRExprEngine::VisitDeclStmt(DeclStmt *DS, ExplodedNode *Pred, Tmp.Add(Pred); for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { - const GRState* state = GetState(*I); - unsigned Count = Builder->getCurrentBlockCount(); - - // Check if 'VD' is a VLA and if so check if has a non-zero size. - QualType T = getContext().getCanonicalType(VD->getType()); - if (VariableArrayType* VLA = dyn_cast<VariableArrayType>(T)) { - // FIXME: Handle multi-dimensional VLAs. - - Expr* SE = VLA->getSizeExpr(); - SVal Size_untested = state->getSVal(SE); - - if (Size_untested.isUndef()) { - if (ExplodedNode* N = Builder->generateNode(DS, state, Pred)) { - N->markAsSink(); - ExplicitBadSizedVLA.insert(N); - } - continue; - } - - DefinedOrUnknownSVal Size = cast<DefinedOrUnknownSVal>(Size_untested); - const GRState *zeroState = state->Assume(Size, false); - state = state->Assume(Size, true); + ExplodedNode *N = *I; + const GRState *state; + + for (CheckersOrdered::iterator CI = Checkers.begin(), CE = Checkers.end(); + CI != CE; ++CI) { + state = GetState(N); + N = CI->second->CheckType(getContext().getCanonicalType(VD->getType()), + N, state, DS, *this); + if (!N) + break; + } - if (zeroState) { - if (ExplodedNode* N = Builder->generateNode(DS, zeroState, Pred)) { - N->markAsSink(); - if (state) - ImplicitBadSizedVLA.insert(N); - else - ExplicitBadSizedVLA.insert(N); - } - } + if (!N) + continue; - if (!state) - continue; - } + state = GetState(N); // Decls without InitExpr are not initialized explicitly. - const LocationContext *LC = (*I)->getLocationContext(); + const LocationContext *LC = N->getLocationContext(); if (InitEx) { SVal InitVal = state->getSVal(InitEx); @@ -2189,20 +2143,15 @@ void GRExprEngine::VisitDeclStmt(DeclStmt *DS, ExplodedNode *Pred, // UnknownVal. if (InitVal.isUnknown() || !getConstraintManager().canReasonAbout(InitVal)) { - InitVal = ValMgr.getConjuredSymbolVal(NULL, InitEx, Count); + InitVal = ValMgr.getConjuredSymbolVal(NULL, InitEx, + Builder->getCurrentBlockCount()); } - - state = state->bindDecl(VD, LC, InitVal); - - // The next thing to do is check if the GRTransferFuncs object wants to - // update the state based on the new binding. If the GRTransferFunc - // object doesn't do anything, just auto-propagate the current state. - GRStmtNodeBuilderRef BuilderRef(Dst, *Builder, *this, *I, state, DS,true); - getTF().EvalBind(BuilderRef, loc::MemRegionVal(state->getRegion(VD, LC)), - InitVal); + + EvalBind(Dst, DS, *I, state, loc::MemRegionVal(state->getRegion(VD, LC)), + InitVal, true); } else { - state = state->bindDeclWithNoInit(VD, LC); + state = state->bindDeclWithNoInit(state->getRegion(VD, LC)); MakeNode(Dst, DS, *I, state); } } @@ -2752,7 +2701,7 @@ void GRExprEngine::VisitReturnStmt(ReturnStmt* S, ExplodedNode* Pred, void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, ExplodedNode* Pred, - ExplodedNodeSet& Dst) { + ExplodedNodeSet& Dst, bool asLValue) { ExplodedNodeSet Tmp1; Expr* LHS = B->getLHS()->IgnoreParens(); @@ -2798,10 +2747,12 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, unsigned Count = Builder->getCurrentBlockCount(); RightV = ValMgr.getConjuredSymbolVal(NULL, B->getRHS(), Count); } - + + SVal ExprVal = asLValue ? LeftV : RightV; + // Simulate the effects of a "store": bind the value of the RHS // to the L-Value represented by the LHS. - EvalStore(Dst, B, LHS, *I2, state->BindExpr(B, RightV), LeftV, RightV); + EvalStore(Dst, B, LHS, *I2, state->BindExpr(B, ExprVal), LeftV, RightV); continue; } @@ -2930,6 +2881,15 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, } //===----------------------------------------------------------------------===// +// Checker registration/lookup. +//===----------------------------------------------------------------------===// + +Checker *GRExprEngine::lookupChecker(void *tag) const { + CheckerMap::iterator I = CheckerM.find(tag); + return (I == CheckerM.end()) ? NULL : Checkers[I->second].second; +} + +//===----------------------------------------------------------------------===// // Visualization. //===----------------------------------------------------------------------===// @@ -2941,7 +2901,8 @@ namespace llvm { template<> struct VISIBILITY_HIDDEN DOTGraphTraits<ExplodedNode*> : public DefaultDOTGraphTraits { - + // FIXME: Since we do not cache error nodes in GRExprEngine now, this does not + // work. static std::string getNodeAttributes(const ExplodedNode* N, void*) { if (GraphPrintCheckerState->isImplicitNullDeref(N) || diff --git a/lib/Analysis/GRExprEngineInternalChecks.cpp b/lib/Analysis/GRExprEngineInternalChecks.cpp index da24192..695f0b0 100644 --- a/lib/Analysis/GRExprEngineInternalChecks.cpp +++ b/lib/Analysis/GRExprEngineInternalChecks.cpp @@ -15,6 +15,13 @@ #include "clang/Analysis/PathSensitive/BugReporter.h" #include "clang/Analysis/PathSensitive/GRExprEngine.h" #include "clang/Analysis/PathSensitive/CheckerVisitor.h" +#include "clang/Analysis/PathSensitive/Checkers/DereferenceChecker.h" +#include "clang/Analysis/PathSensitive/Checkers/DivZeroChecker.h" +#include "clang/Analysis/PathSensitive/Checkers/BadCallChecker.h" +#include "clang/Analysis/PathSensitive/Checkers/UndefinedArgChecker.h" +#include "clang/Analysis/PathSensitive/Checkers/UndefinedAssignmentChecker.h" +#include "clang/Analysis/PathSensitive/Checkers/AttrNonNullChecker.h" +#include "clang/Analysis/PathSensitive/Checkers/VLASizeChecker.h" #include "clang/Analysis/PathDiagnostic.h" #include "clang/Basic/SourceManager.h" #include "llvm/Support/Compiler.h" @@ -40,10 +47,8 @@ ExplodedNode* GetNode(GRExprEngine::undef_arg_iterator I) { //===----------------------------------------------------------------------===// // Bug Descriptions. //===----------------------------------------------------------------------===// - -namespace { - -class VISIBILITY_HIDDEN BuiltinBugReport : public RangedBugReport { +namespace clang { +class BuiltinBugReport : public RangedBugReport { public: BuiltinBugReport(BugType& bt, const char* desc, ExplodedNode *n) @@ -57,30 +62,10 @@ public: const ExplodedNode* N); }; -class VISIBILITY_HIDDEN BuiltinBug : public BugType { - GRExprEngine &Eng; -protected: - const std::string desc; -public: - BuiltinBug(GRExprEngine *eng, const char* n, const char* d) - : BugType(n, "Logic errors"), Eng(*eng), desc(d) {} - - BuiltinBug(GRExprEngine *eng, const char* n) - : BugType(n, "Logic errors"), Eng(*eng), desc(n) {} - - const std::string &getDescription() const { return desc; } - - virtual void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) {} - - void FlushReports(BugReporter& BR) { FlushReportsImpl(BR, Eng); } - - virtual void registerInitialVisitors(BugReporterContext& BRC, - const ExplodedNode* N, - BuiltinBugReport *R) {} - - template <typename ITER> void Emit(BugReporter& BR, ITER I, ITER E); -}; - +void BuiltinBugReport::registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode* N) { + static_cast<BuiltinBug&>(getBugType()).registerInitialVisitors(BRC, N, this); +} template <typename ITER> void BuiltinBug::Emit(BugReporter& BR, ITER I, ITER E) { @@ -88,27 +73,6 @@ void BuiltinBug::Emit(BugReporter& BR, ITER I, ITER E) { GetNode(I))); } -void BuiltinBugReport::registerInitialVisitors(BugReporterContext& BRC, - const ExplodedNode* N) { - static_cast<BuiltinBug&>(getBugType()).registerInitialVisitors(BRC, N, this); -} - -class VISIBILITY_HIDDEN NullDeref : public BuiltinBug { -public: - NullDeref(GRExprEngine* eng) - : BuiltinBug(eng,"Null dereference", "Dereference of null pointer") {} - - void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { - Emit(BR, Eng.null_derefs_begin(), Eng.null_derefs_end()); - } - - void registerInitialVisitors(BugReporterContext& BRC, - const ExplodedNode* N, - BuiltinBugReport *R) { - registerTrackNullOrUndefValue(BRC, GetDerefExpr(N), N); - } -}; - class VISIBILITY_HIDDEN NilReceiverStructRet : public BuiltinBug { public: NilReceiverStructRet(GRExprEngine* eng) : @@ -175,34 +139,6 @@ public: } }; -class VISIBILITY_HIDDEN UndefinedDeref : public BuiltinBug { -public: - UndefinedDeref(GRExprEngine* eng) - : BuiltinBug(eng,"Dereference of undefined pointer value") {} - - void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { - Emit(BR, Eng.undef_derefs_begin(), Eng.undef_derefs_end()); - } - - void registerInitialVisitors(BugReporterContext& BRC, - const ExplodedNode* N, - BuiltinBugReport *R) { - registerTrackNullOrUndefValue(BRC, GetDerefExpr(N), N); - } -}; - -class VISIBILITY_HIDDEN DivZero : public BuiltinBug { -public: - DivZero(GRExprEngine* eng = 0) - : BuiltinBug(eng,"Division by zero") {} - - void registerInitialVisitors(BugReporterContext& BRC, - const ExplodedNode* N, - BuiltinBugReport *R) { - registerTrackNullOrUndefValue(BRC, GetDenomExpr(N), N); - } -}; - class VISIBILITY_HIDDEN UndefResult : public BuiltinBug { public: UndefResult(GRExprEngine* eng) @@ -279,20 +215,6 @@ public: } }; -class VISIBILITY_HIDDEN BadCall : public BuiltinBug { -public: - BadCall(GRExprEngine *eng = 0) - : BuiltinBug(eng, "Invalid function call", - "Called function pointer is a null or undefined pointer value") {} - - void registerInitialVisitors(BugReporterContext& BRC, - const ExplodedNode* N, - BuiltinBugReport *R) { - registerTrackNullOrUndefValue(BRC, GetCalleeExpr(N), N); - } -}; - - class VISIBILITY_HIDDEN ArgReport : public BuiltinBugReport { const Stmt *Arg; public: @@ -528,276 +450,8 @@ public: } }; -class VISIBILITY_HIDDEN BadSizeVLA : public BuiltinBug { -public: - BadSizeVLA(GRExprEngine* eng) : - BuiltinBug(eng, "Bad variable-length array (VLA) size") {} - - void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) { - for (GRExprEngine::ErrorNodes::iterator - I = Eng.ExplicitBadSizedVLA.begin(), - E = Eng.ExplicitBadSizedVLA.end(); I!=E; ++I) { - - // Determine whether this was a 'zero-sized' VLA or a VLA with an - // undefined size. - ExplodedNode* N = *I; - PostStmt PS = cast<PostStmt>(N->getLocation()); - const DeclStmt *DS = cast<DeclStmt>(PS.getStmt()); - VarDecl* VD = cast<VarDecl>(*DS->decl_begin()); - QualType T = Eng.getContext().getCanonicalType(VD->getType()); - VariableArrayType* VT = cast<VariableArrayType>(T); - Expr* SizeExpr = VT->getSizeExpr(); - - std::string buf; - llvm::raw_string_ostream os(buf); - os << "The expression used to specify the number of elements in the " - "variable-length array (VLA) '" - << VD->getNameAsString() << "' evaluates to "; - - bool isUndefined = N->getState()->getSVal(SizeExpr).isUndef(); - - if (isUndefined) - os << "an undefined or garbage value."; - else - os << "0. VLAs with no elements have undefined behavior."; - - std::string shortBuf; - llvm::raw_string_ostream os_short(shortBuf); - os_short << "Variable-length array '" << VD->getNameAsString() << "' " - << (isUndefined ? "garbage value for array size" - : "has zero elements (undefined behavior)"); - - ArgReport *report = new ArgReport(*this, os_short.str().c_str(), - os.str().c_str(), N, SizeExpr); - - report->addRange(SizeExpr->getSourceRange()); - BR.EmitReport(report); - } - } - - void registerInitialVisitors(BugReporterContext& BRC, - const ExplodedNode* N, - BuiltinBugReport *R) { - registerTrackNullOrUndefValue(BRC, static_cast<ArgReport*>(R)->getArg(), - N); - } -}; - -//===----------------------------------------------------------------------===// -// __attribute__(nonnull) checking - -class VISIBILITY_HIDDEN CheckAttrNonNull : - public CheckerVisitor<CheckAttrNonNull> { - - BugType *BT; - -public: - CheckAttrNonNull() : BT(0) {} - ~CheckAttrNonNull() {} - - const void *getTag() { - static int x = 0; - return &x; - } - - void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { - const GRState *state = C.getState(); - const GRState *originalState = state; - - // Check if the callee has a 'nonnull' attribute. - SVal X = state->getSVal(CE->getCallee()); - - const FunctionDecl* FD = X.getAsFunctionDecl(); - if (!FD) - return; +} // end clang namespace - const NonNullAttr* Att = FD->getAttr<NonNullAttr>(); - if (!Att) - return; - - // Iterate through the arguments of CE and check them for null. - unsigned idx = 0; - - for (CallExpr::const_arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E; - ++I, ++idx) { - - if (!Att->isNonNull(idx)) - continue; - - const SVal &V = state->getSVal(*I); - const DefinedSVal *DV = dyn_cast<DefinedSVal>(&V); - - if (!DV) - continue; - - ConstraintManager &CM = C.getConstraintManager(); - const GRState *stateNotNull, *stateNull; - llvm::tie(stateNotNull, stateNull) = CM.AssumeDual(state, *DV); - - if (stateNull && !stateNotNull) { - // Generate an error node. Check for a null node in case - // we cache out. - if (ExplodedNode *errorNode = C.GenerateNode(CE, stateNull, true)) { - - // Lazily allocate the BugType object if it hasn't already been - // created. Ownership is transferred to the BugReporter object once - // the BugReport is passed to 'EmitWarning'. - if (!BT) - BT = new BugType("Argument with 'nonnull' attribute passed null", - "API"); - - EnhancedBugReport *R = - new EnhancedBugReport(*BT, - "Null pointer passed as an argument to a " - "'nonnull' parameter", errorNode); - - // Highlight the range of the argument that was null. - const Expr *arg = *I; - R->addRange(arg->getSourceRange()); - R->addVisitorCreator(registerTrackNullOrUndefValue, arg); - - // Emit the bug report. - C.EmitReport(R); - } - - // Always return. Either we cached out or we just emitted an error. - return; - } - - // If a pointer value passed the check we should assume that it is - // indeed not null from this point forward. - assert(stateNotNull); - state = stateNotNull; - } - - // If we reach here all of the arguments passed the nonnull check. - // If 'state' has been updated generated a new node. - if (state != originalState) - C.addTransition(C.GenerateNode(CE, state)); - } -}; -} // end anonymous namespace - -// Undefined arguments checking. -namespace { -class VISIBILITY_HIDDEN CheckUndefinedArg - : public CheckerVisitor<CheckUndefinedArg> { - - BadArg *BT; - -public: - CheckUndefinedArg() : BT(0) {} - ~CheckUndefinedArg() {} - - const void *getTag() { - static int x = 0; - return &x; - } - - void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); -}; - -void CheckUndefinedArg::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE){ - for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); - I != E; ++I) { - if (C.getState()->getSVal(*I).isUndef()) { - if (ExplodedNode *ErrorNode = C.GenerateNode(CE, true)) { - if (!BT) - BT = new BadArg(); - // Generate a report for this bug. - ArgReport *Report = new ArgReport(*BT, BT->getDescription().c_str(), - ErrorNode, *I); - Report->addRange((*I)->getSourceRange()); - C.EmitReport(Report); - } - } - } -} - -class VISIBILITY_HIDDEN CheckBadCall : public CheckerVisitor<CheckBadCall> { - BadCall *BT; - -public: - CheckBadCall() : BT(0) {} - ~CheckBadCall() {} - - const void *getTag() { - static int x = 0; - return &x; - } - - void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); -}; - -void CheckBadCall::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { - const Expr *Callee = CE->getCallee()->IgnoreParens(); - SVal L = C.getState()->getSVal(Callee); - - if (L.isUndef() || isa<loc::ConcreteInt>(L)) { - if (ExplodedNode *N = C.GenerateNode(CE, true)) { - if (!BT) - BT = new BadCall(); - C.EmitReport(new BuiltinBugReport(*BT, BT->getDescription().c_str(), N)); - } - } -} - -class VISIBILITY_HIDDEN CheckDivZero : public CheckerVisitor<CheckDivZero> { - DivZero *BT; -public: - CheckDivZero() : BT(0) {} - ~CheckDivZero() {} - - const void *getTag() { - static int x; - return &x; - } - - void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); -}; - -void CheckDivZero::PreVisitBinaryOperator(CheckerContext &C, - const BinaryOperator *B) { - BinaryOperator::Opcode Op = B->getOpcode(); - if (Op != BinaryOperator::Div && - Op != BinaryOperator::Rem && - Op != BinaryOperator::DivAssign && - Op != BinaryOperator::RemAssign) - return; - - if (!B->getRHS()->getType()->isIntegerType() || - !B->getRHS()->getType()->isScalarType()) - return; - - SVal Denom = C.getState()->getSVal(B->getRHS()); - const DefinedSVal *DV = dyn_cast<DefinedSVal>(&Denom); - - // Divide-by-undefined handled in the generic checking for uses of - // undefined values. - if (!DV) - return; - - // Check for divide by zero. - ConstraintManager &CM = C.getConstraintManager(); - const GRState *stateNotZero, *stateZero; - llvm::tie(stateNotZero, stateZero) = CM.AssumeDual(C.getState(), *DV); - - if (stateZero && !stateNotZero) { - if (ExplodedNode *N = C.GenerateNode(B, stateZero, true)) { - if (!BT) - BT = new DivZero(); - - C.EmitReport(new BuiltinBugReport(*BT, BT->getDescription().c_str(), N)); - } - return; - } - - // If we get here, then the denom should not be zero. We abandon the implicit - // zero denom case for now. - if (stateNotZero != C.getState()) - C.addTransition(C.GenerateNode(B, stateNotZero)); -} -} //===----------------------------------------------------------------------===// // Check registration. //===----------------------------------------------------------------------===// @@ -808,8 +462,6 @@ void GRExprEngine::RegisterInternalChecks() { // create BugReports on-the-fly but instead wait until GRExprEngine finishes // analyzing a function. Generation of BugReport objects is done via a call // to 'FlushReports' from BugReporter. - BR.Register(new NullDeref(this)); - BR.Register(new UndefinedDeref(this)); BR.Register(new UndefBranch(this)); BR.Register(new UndefResult(this)); BR.Register(new RetStack(this)); @@ -817,7 +469,6 @@ void GRExprEngine::RegisterInternalChecks() { BR.Register(new BadMsgExprArg(this)); BR.Register(new BadReceiver(this)); BR.Register(new OutOfBoundMemoryAccess(this)); - BR.Register(new BadSizeVLA(this)); BR.Register(new NilReceiverStructRet(this)); BR.Register(new NilReceiverLargerThanVoidPtrRet(this)); @@ -826,8 +477,13 @@ void GRExprEngine::RegisterInternalChecks() { // their associated BugType will get registered with the BugReporter // automatically. Note that the check itself is owned by the GRExprEngine // object. - registerCheck(new CheckAttrNonNull()); - registerCheck(new CheckUndefinedArg()); - registerCheck(new CheckBadCall()); - registerCheck(new CheckDivZero()); + registerCheck(new AttrNonNullChecker()); + registerCheck(new UndefinedArgChecker()); + registerCheck(new UndefinedAssignmentChecker()); + registerCheck(new BadCallChecker()); + registerCheck(new DivZeroChecker()); + registerCheck(new UndefDerefChecker()); + registerCheck(new NullDerefChecker()); + registerCheck(new UndefSizedVLAChecker()); + registerCheck(new ZeroSizedVLAChecker()); } diff --git a/lib/Analysis/NSAutoreleasePoolChecker.cpp b/lib/Analysis/NSAutoreleasePoolChecker.cpp new file mode 100644 index 0000000..e0a8d0d --- /dev/null +++ b/lib/Analysis/NSAutoreleasePoolChecker.cpp @@ -0,0 +1,84 @@ +//=- NSAutoreleasePoolChecker.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 a NSAutoreleasePoolChecker, a small checker that warns +// about subpar uses of NSAutoreleasePool. Note that while the check itself +// (in it's current form) could be written as a flow-insensitive check, in +// can be potentially enhanced in the future with flow-sensitive information. +// It is also a good example of the CheckerVisitor interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/BugReporter.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" +#include "BasicObjCFoundationChecks.h" +#include "llvm/Support/Compiler.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Decl.h" + +using namespace clang; + +namespace { +class VISIBILITY_HIDDEN NSAutoreleasePoolChecker + : public CheckerVisitor<NSAutoreleasePoolChecker> { + + Selector releaseS; + +public: + NSAutoreleasePoolChecker(Selector release_s) : releaseS(release_s) {} + + static void *getTag() { + static int x = 0; + return &x; + } + + void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); +}; + +} // end anonymous namespace + + +void clang::RegisterNSAutoreleasePoolChecks(GRExprEngine &Eng) { + ASTContext &Ctx = Eng.getContext(); + if (Ctx.getLangOptions().getGCMode() != LangOptions::NonGC) { + Eng.registerCheck(new NSAutoreleasePoolChecker(GetNullarySelector("release", + Ctx))); + } +} + +void +NSAutoreleasePoolChecker::PreVisitObjCMessageExpr(CheckerContext &C, + const ObjCMessageExpr *ME) { + + const Expr *receiver = ME->getReceiver(); + if (!receiver) + return; + + // FIXME: Enhance with value-tracking information instead of consulting + // the type of the expression. + const ObjCObjectPointerType* PT = + receiver->getType()->getAs<ObjCObjectPointerType>(); + const ObjCInterfaceDecl* OD = PT->getInterfaceDecl(); + if (!OD) + return; + if (!OD->getIdentifier()->getName().equals("NSAutoreleasePool")) + return; + + // Sending 'release' message? + if (ME->getSelector() != releaseS) + return; + + SourceRange R = ME->getSourceRange(); + + C.getBugReporter().EmitBasicReport("Use -drain instead of -release", + "API Upgrade (Apple)", + "Use -drain instead of -release when using NSAutoreleasePool " + "and garbage collection", ME->getLocStart(), &R, 1); +} diff --git a/lib/Analysis/CheckNSError.cpp b/lib/Analysis/NSErrorChecker.cpp index 8086da5..307686f 100644 --- a/lib/Analysis/CheckNSError.cpp +++ b/lib/Analysis/NSErrorChecker.cpp @@ -1,4 +1,4 @@ -//=- CheckNSError.cpp - Coding conventions for uses of NSError ---*- C++ -*-==// +//=- NSErrorCheckerer.cpp - Coding conventions for uses of NSError -*- C++ -*-==// // // The LLVM Compiler Infrastructure // @@ -18,6 +18,7 @@ #include "clang/Analysis/LocalCheckers.h" #include "clang/Analysis/PathSensitive/BugReporter.h" #include "clang/Analysis/PathSensitive/GRExprEngine.h" +#include "clang/Analysis/PathSensitive/Checkers/DereferenceChecker.h" #include "BasicObjCFoundationChecks.h" #include "llvm/Support/Compiler.h" #include "clang/AST/DeclObjC.h" @@ -27,7 +28,7 @@ using namespace clang; namespace { -class VISIBILITY_HIDDEN NSErrorCheck : public BugType { +class VISIBILITY_HIDDEN NSErrorChecker : public BugType { const Decl &CodeDecl; const bool isNSErrorWarning; IdentifierInfo * const II; @@ -48,7 +49,7 @@ class VISIBILITY_HIDDEN NSErrorCheck : public BugType { void EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl); public: - NSErrorCheck(const Decl &D, bool isNSError, GRExprEngine& eng) + NSErrorChecker(const Decl &D, bool isNSError, GRExprEngine& eng) : BugType(isNSError ? "NSError** null dereference" : "CFErrorRef* null dereference", "Coding conventions (Apple)"), @@ -64,11 +65,11 @@ public: void clang::RegisterNSErrorChecks(BugReporter& BR, GRExprEngine &Eng, const Decl &D) { - BR.Register(new NSErrorCheck(D, true, Eng)); - BR.Register(new NSErrorCheck(D, false, Eng)); + BR.Register(new NSErrorChecker(D, true, Eng)); + BR.Register(new NSErrorChecker(D, false, Eng)); } -void NSErrorCheck::FlushReports(BugReporter& BR) { +void NSErrorChecker::FlushReports(BugReporter& BR) { // Get the analysis engine and the exploded analysis graph. ExplodedGraph& G = Eng.getGraph(); @@ -99,7 +100,7 @@ void NSErrorCheck::FlushReports(BugReporter& BR) { } } -void NSErrorCheck::EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl) { +void NSErrorChecker::EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl) { std::string sbuf; llvm::raw_string_ostream os(sbuf); @@ -121,7 +122,7 @@ void NSErrorCheck::EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl) { } void -NSErrorCheck::CheckSignature(const ObjCMethodDecl& M, QualType& ResultTy, +NSErrorChecker::CheckSignature(const ObjCMethodDecl& M, QualType& ResultTy, llvm::SmallVectorImpl<VarDecl*>& ErrorParams) { ResultTy = M.getResultType(); @@ -140,7 +141,7 @@ NSErrorCheck::CheckSignature(const ObjCMethodDecl& M, QualType& ResultTy, } void -NSErrorCheck::CheckSignature(const FunctionDecl& F, QualType& ResultTy, +NSErrorChecker::CheckSignature(const FunctionDecl& F, QualType& ResultTy, llvm::SmallVectorImpl<VarDecl*>& ErrorParams) { ResultTy = F.getResultType(); @@ -159,7 +160,7 @@ NSErrorCheck::CheckSignature(const FunctionDecl& F, QualType& ResultTy, } -bool NSErrorCheck::CheckNSErrorArgument(QualType ArgTy) { +bool NSErrorChecker::CheckNSErrorArgument(QualType ArgTy) { const PointerType* PPT = ArgTy->getAs<PointerType>(); if (!PPT) @@ -180,7 +181,7 @@ bool NSErrorCheck::CheckNSErrorArgument(QualType ArgTy) { return false; } -bool NSErrorCheck::CheckCFErrorArgument(QualType ArgTy) { +bool NSErrorChecker::CheckCFErrorArgument(QualType ArgTy) { const PointerType* PPT = ArgTy->getAs<PointerType>(); if (!PPT) return false; @@ -191,7 +192,7 @@ bool NSErrorCheck::CheckCFErrorArgument(QualType ArgTy) { return TT->getDecl()->getIdentifier() == II; } -void NSErrorCheck::CheckParamDeref(const VarDecl *Param, +void NSErrorChecker::CheckParamDeref(const VarDecl *Param, const LocationContext *LC, const GRState *rootState, BugReporter& BR) { @@ -208,8 +209,10 @@ void NSErrorCheck::CheckParamDeref(const VarDecl *Param, return; // Iterate over the implicit-null dereferences. - for (GRExprEngine::null_deref_iterator I=Eng.implicit_null_derefs_begin(), - E=Eng.implicit_null_derefs_end(); I!=E; ++I) { + NullDerefChecker *Checker = Eng.getChecker<NullDerefChecker>(); + assert(Checker && "NullDerefChecker not exist."); + for (NullDerefChecker::iterator I = Checker->implicit_nodes_begin(), + E = Checker->implicit_nodes_end(); I != E; ++I) { const GRState *state = (*I)->getState(); const SVal* X = state->get<GRState::NullDerefTag>(); diff --git a/lib/Analysis/RegionStore.cpp b/lib/Analysis/RegionStore.cpp index 780772a..dbf8c42 100644 --- a/lib/Analysis/RegionStore.cpp +++ b/lib/Analysis/RegionStore.cpp @@ -275,11 +275,11 @@ public: const GRState *BindCompoundLiteral(const GRState *state, const CompoundLiteralExpr* CL, SVal V); - const GRState *BindDecl(const GRState *ST, const VarDecl *VD, - const LocationContext *LC, SVal InitVal); + const GRState *BindDecl(const GRState *ST, const VarRegion *VR, + SVal InitVal); - const GRState *BindDeclWithNoInit(const GRState *state, const VarDecl*, - const LocationContext *) { + const GRState *BindDeclWithNoInit(const GRState *state, + const VarRegion *) { return state; } @@ -1409,12 +1409,10 @@ const GRState *RegionStoreManager::Bind(const GRState *state, Loc L, SVal V) { } const GRState *RegionStoreManager::BindDecl(const GRState *ST, - const VarDecl *VD, - const LocationContext *LC, + const VarRegion *VR, SVal InitVal) { - QualType T = VD->getType(); - VarRegion* VR = MRMgr.getVarRegion(VD, LC); + QualType T = VR->getDecl()->getType(); if (T->isArrayType()) return BindArray(ST, VR, InitVal); @@ -1630,6 +1628,8 @@ void RegionStoreManager::RemoveDeadBindings(GRState &state, Stmt* Loc, // Process the "intermediate" roots to find if they are referenced by // real roots. llvm::SmallVector<RBDNode, 10> WorkList; + llvm::SmallVector<RBDNode, 10> Postponed; + llvm::DenseSet<const MemRegion*> IntermediateVisited; while (!IntermediateRoots.empty()) { @@ -1647,8 +1647,11 @@ void RegionStoreManager::RemoveDeadBindings(GRState &state, Stmt* Loc, } if (const SymbolicRegion* SR = dyn_cast<SymbolicRegion>(R)) { - if (SymReaper.isLive(SR->getSymbol())) - WorkList.push_back(std::make_pair(&state, SR)); + llvm::SmallVectorImpl<RBDNode> &Q = + SymReaper.isLive(SR->getSymbol()) ? WorkList : Postponed; + + Q.push_back(std::make_pair(&state, SR)); + continue; } @@ -1667,6 +1670,7 @@ void RegionStoreManager::RemoveDeadBindings(GRState &state, Stmt* Loc, llvm::DenseSet<RBDNode> Visited; +tryAgain: while (!WorkList.empty()) { RBDNode N = WorkList.back(); WorkList.pop_back(); @@ -1740,6 +1744,21 @@ void RegionStoreManager::RemoveDeadBindings(GRState &state, Stmt* Loc, } } + // See if any postponed SymbolicRegions are actually live now, after + // having done a scan. + for (llvm::SmallVectorImpl<RBDNode>::iterator I = Postponed.begin(), + E = Postponed.end() ; I != E ; ++I) { + if (const SymbolicRegion *SR = cast_or_null<SymbolicRegion>(I->second)) { + if (SymReaper.isLive(SR->getSymbol())) { + WorkList.push_back(*I); + I->second = NULL; + } + } + } + + if (!WorkList.empty()) + goto tryAgain; + // We have now scanned the store, marking reachable regions and symbols // as live. We now remove all the regions that are dead from the store // as well as update DSymbols with the set symbols that are now dead. diff --git a/lib/Analysis/UndefinedArgChecker.cpp b/lib/Analysis/UndefinedArgChecker.cpp new file mode 100644 index 0000000..a229f55 --- /dev/null +++ b/lib/Analysis/UndefinedArgChecker.cpp @@ -0,0 +1,43 @@ +//===--- UndefinedArgChecker.h - Undefined arguments checker ----*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines BadCallChecker, a builtin check in GRExprEngine that performs +// checks for undefined arguments. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/Checkers/UndefinedArgChecker.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" + +using namespace clang; + +void *UndefinedArgChecker::getTag() { + static int x = 0; + return &x; +} + +void UndefinedArgChecker::PreVisitCallExpr(CheckerContext &C, + const CallExpr *CE){ + for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); + I != E; ++I) { + if (C.getState()->getSVal(*I).isUndef()) { + if (ExplodedNode *N = C.GenerateNode(CE, true)) { + if (!BT) + BT = new BugType("Pass-by-value argument in function call is " + "undefined", "Logic error"); + // Generate a report for this bug. + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName().c_str(), + N); + R->addRange((*I)->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, *I); + C.EmitReport(R); + } + } + } +} diff --git a/lib/Analysis/UndefinedAssignmentChecker.cpp b/lib/Analysis/UndefinedAssignmentChecker.cpp new file mode 100644 index 0000000..c5b2401 --- /dev/null +++ b/lib/Analysis/UndefinedAssignmentChecker.cpp @@ -0,0 +1,61 @@ +//===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UndefinedAssginmentChecker, a builtin check in GRExprEngine that +// checks for assigning undefined values. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/Checkers/UndefinedAssignmentChecker.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" + +using namespace clang; + +void *UndefinedAssignmentChecker::getTag() { + static int x = 0; + return &x; +} + +void UndefinedAssignmentChecker::PreVisitBind(CheckerContext &C, + const Stmt *S, + SVal location, + SVal val) { + if (!val.isUndef()) + return; + + ExplodedNode *N = C.GenerateNode(S, true); + + if (!N) + return; + + if (!BT) + BT = new BugType("Assigned value is garbage or undefined", + "Logic error"); + + // Generate a report for this bug. + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName().c_str(), N); + const Expr *ex = 0; + + // FIXME: This check needs to be done on the expression doing the + // assignment, not the "store" expression. + if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) + ex = B->getRHS(); + else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) { + const VarDecl* VD = dyn_cast<VarDecl>(DS->getSingleDecl()); + ex = VD->getInit(); + } + + if (ex) { + R->addRange(ex->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, ex); + } + + C.EmitReport(R); +} + diff --git a/lib/Analysis/VLASizeChecker.cpp b/lib/Analysis/VLASizeChecker.cpp new file mode 100644 index 0000000..76e4477 --- /dev/null +++ b/lib/Analysis/VLASizeChecker.cpp @@ -0,0 +1,102 @@ +//=== VLASizeChecker.cpp - Undefined dereference checker --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines two VLASizeCheckers, a builtin check in GRExprEngine that +// performs checks for declaration of VLA of undefined or zero size. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/Checkers/VLASizeChecker.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" +#include "clang/Analysis/PathSensitive/BugReporter.h" + +using namespace clang; + +void *UndefSizedVLAChecker::getTag() { + static int x = 0; + return &x; +} + +ExplodedNode *UndefSizedVLAChecker::CheckType(QualType T, ExplodedNode *Pred, + const GRState *state, + Stmt *S, GRExprEngine &Eng) { + GRStmtNodeBuilder &Builder = Eng.getBuilder(); + BugReporter &BR = Eng.getBugReporter(); + + if (VariableArrayType* VLA = dyn_cast<VariableArrayType>(T)) { + // FIXME: Handle multi-dimensional VLAs. + Expr* SE = VLA->getSizeExpr(); + SVal Size_untested = state->getSVal(SE); + + if (Size_untested.isUndef()) { + if (ExplodedNode* N = Builder.generateNode(S, state, Pred)) { + N->markAsSink(); + if (!BT) + BT = new BugType("Declare variable-length array (VLA) of undefined " + "size", "Logic error"); + + EnhancedBugReport *R = + new EnhancedBugReport(*BT, BT->getName().c_str(), N); + R->addRange(SE->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); + BR.EmitReport(R); + } + return 0; + } + } + return Pred; +} + +void *ZeroSizedVLAChecker::getTag() { + static int x; + return &x; +} + +ExplodedNode *ZeroSizedVLAChecker::CheckType(QualType T, ExplodedNode *Pred, + const GRState *state, Stmt *S, + GRExprEngine &Eng) { + GRStmtNodeBuilder &Builder = Eng.getBuilder(); + BugReporter &BR = Eng.getBugReporter(); + + if (VariableArrayType* VLA = dyn_cast<VariableArrayType>(T)) { + // FIXME: Handle multi-dimensional VLAs. + Expr* SE = VLA->getSizeExpr(); + SVal Size_untested = state->getSVal(SE); + + DefinedOrUnknownSVal *Size = dyn_cast<DefinedOrUnknownSVal>(&Size_untested); + // Undefined size is checked in another checker. + if (!Size) + return Pred; + + const GRState *zeroState = state->Assume(*Size, false); + state = state->Assume(*Size, true); + + if (zeroState && !state) { + if (ExplodedNode* N = Builder.generateNode(S, zeroState, Pred)) { + N->markAsSink(); + if (!BT) + BT = new BugType("Declare variable-length array (VLA) of zero size", + "Logic error"); + + EnhancedBugReport *R = + new EnhancedBugReport(*BT, BT->getName().c_str(), N); + R->addRange(SE->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); + BR.EmitReport(R); + } + } + if (!state) + return 0; + + return Builder.generateNode(S, state, Pred); + } + else + return Pred; +} + |