diff options
author | dim <dim@FreeBSD.org> | 2010-09-17 15:54:40 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2010-09-17 15:54:40 +0000 |
commit | 36c49e3f258dced101949edabd72e9bc3f1dedc4 (patch) | |
tree | 0bbe07708f7571f8b5291f6d7b96c102b7c99dee /lib/Checker | |
parent | fc84956ac8b7cd244ef30e7a4d4d38a58dec5904 (diff) | |
download | FreeBSD-src-36c49e3f258dced101949edabd72e9bc3f1dedc4.zip FreeBSD-src-36c49e3f258dced101949edabd72e9bc3f1dedc4.tar.gz |
Vendor import of clang r114020 (from the release_28 branch):
http://llvm.org/svn/llvm-project/cfe/branches/release_28@114020
Approved by: rpaulo (mentor)
Diffstat (limited to 'lib/Checker')
57 files changed, 3348 insertions, 1512 deletions
diff --git a/lib/Checker/AdjustedReturnValueChecker.cpp b/lib/Checker/AdjustedReturnValueChecker.cpp index b92f2e7..0ed04fb 100644 --- a/lib/Checker/AdjustedReturnValueChecker.cpp +++ b/lib/Checker/AdjustedReturnValueChecker.cpp @@ -70,8 +70,7 @@ void AdjustedReturnValueChecker::PostVisitCallExpr(CheckerContext &C, } else if (const BlockDataRegion *BD = dyn_cast<BlockDataRegion>(callee)) { const BlockTextRegion *BR = BD->getCodeRegion(); - const BlockPointerType *BT = - BR->getLocationType(C.getASTContext())->getAs<BlockPointerType>(); + const BlockPointerType *BT=BR->getLocationType()->getAs<BlockPointerType>(); const FunctionType *FT = BT->getPointeeType()->getAs<FunctionType>(); actualResultTy = FT->getResultType(); } diff --git a/lib/Checker/AggExprVisitor.cpp b/lib/Checker/AggExprVisitor.cpp index 343afec..6d472f4 100644 --- a/lib/Checker/AggExprVisitor.cpp +++ b/lib/Checker/AggExprVisitor.cpp @@ -18,6 +18,13 @@ using namespace clang; namespace { +/// AggExprVisitor is designed after AggExprEmitter of the CodeGen module. It +/// is used for evaluating exprs of C++ object type. Evaluating such exprs +/// requires a destination pointer pointing to the object being evaluated +/// into. Passing such a pointer around would pollute the Visit* interface of +/// GRExprEngine. AggExprVisitor encapsulates code that goes through various +/// cast and construct exprs (and others), and at the final point, dispatches +/// back to the GRExprEngine to let the real evaluation logic happen. class AggExprVisitor : public StmtVisitor<AggExprVisitor> { SVal DestPtr; ExplodedNode *Pred; @@ -38,8 +45,8 @@ void AggExprVisitor::VisitCastExpr(CastExpr *E) { switch (E->getCastKind()) { default: assert(0 && "Unhandled cast kind"); - case CastExpr::CK_NoOp: - case CastExpr::CK_ConstructorConversion: + case CK_NoOp: + case CK_ConstructorConversion: Visit(E->getSubExpr()); break; } diff --git a/lib/Checker/AnalysisConsumer.cpp b/lib/Checker/AnalysisConsumer.cpp index 524f37e..ad5ccb50 100644 --- a/lib/Checker/AnalysisConsumer.cpp +++ b/lib/Checker/AnalysisConsumer.cpp @@ -29,6 +29,7 @@ #include "clang/Checker/PathSensitive/GRTransferFuncs.h" #include "clang/Checker/PathDiagnosticClients.h" #include "GRExprEngineExperimentalChecks.h" +#include "GRExprEngineInternalChecks.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Frontend/AnalyzerOptions.h" @@ -173,10 +174,12 @@ public: 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.TrimGraph, Opts.InlineCall, + Opts.UnoptimizedCFG)); } virtual void HandleTranslationUnit(ASTContext &C); @@ -341,7 +344,10 @@ static void ActionGRExprEngine(AnalysisConsumer &C, AnalysisManager& mgr, if (C.Opts.EnableExperimentalChecks) RegisterExperimentalChecks(Eng); - if (C.Opts.EnableIdempotentOperationChecker) + // 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); // Set the graph auditor. @@ -352,7 +358,7 @@ static void ActionGRExprEngine(AnalysisConsumer &C, AnalysisManager& mgr, } // Execute the worklist algorithm. - Eng.ExecuteWorkList(mgr.getStackFrame(D), mgr.getMaxNodes()); + Eng.ExecuteWorkList(mgr.getStackFrame(D, 0), mgr.getMaxNodes()); // Release the auditor (if any) so that it doesn't monitor the graph // created BugReporter. diff --git a/lib/Checker/AnalysisManager.cpp b/lib/Checker/AnalysisManager.cpp new file mode 100644 index 0000000..339cdab --- /dev/null +++ b/lib/Checker/AnalysisManager.cpp @@ -0,0 +1,31 @@ +//===-- AnalysisManager.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/AnalysisManager.h" +#include "clang/Index/Entity.h" +#include "clang/Index/Indexer.h" + +using namespace clang; + +const AnalysisContext * +AnalysisManager::getAnalysisContextInAnotherTU(const Decl *D) { + idx::Entity Ent = idx::Entity::get(const_cast<Decl *>(D), + Idxer->getProgram()); + FunctionDecl *FuncDef; + idx::TranslationUnit *TU; + llvm::tie(FuncDef, TU) = Idxer->getDefinitionFor(Ent); + + if (FuncDef == 0) + return 0; + + // This AnalysisContext wraps function definition in another translation unit. + // But it is still owned by the AnalysisManager associated with the current + // translation unit. + return AnaCtxMgr.getContext(FuncDef, TU); +} diff --git a/lib/Checker/ArrayBoundChecker.cpp b/lib/Checker/ArrayBoundChecker.cpp index 746b3f9..98345bd 100644 --- a/lib/Checker/ArrayBoundChecker.cpp +++ b/lib/Checker/ArrayBoundChecker.cpp @@ -58,7 +58,7 @@ void ArrayBoundChecker::VisitLocation(CheckerContext &C, const Stmt *S, SVal l){ // Get the size of the array. DefinedOrUnknownSVal NumElements = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), - ER->getValueType(C.getASTContext())); + ER->getValueType()); const GRState *StInBound = state->AssumeInBound(Idx, NumElements, true); const GRState *StOutBound = state->AssumeInBound(Idx, NumElements, false); diff --git a/lib/Checker/BasicObjCFoundationChecks.cpp b/lib/Checker/BasicObjCFoundationChecks.cpp index ecb2d1c..3c1a6d1 100644 --- a/lib/Checker/BasicObjCFoundationChecks.cpp +++ b/lib/Checker/BasicObjCFoundationChecks.cpp @@ -73,9 +73,6 @@ class BasicObjCFoundationChecks : public GRSimpleAPICheck { bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix); bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME); - void Warn(ExplodedNode* N, const Expr* E, const std::string& s); - void WarnNilArg(ExplodedNode* N, const Expr* E); - bool CheckNilArg(ExplodedNode* N, unsigned Arg); public: @@ -358,7 +355,7 @@ bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){ if (!R) return false; - QualType T = Ctx.getCanonicalType(R->getValueType(Ctx)); + QualType T = Ctx.getCanonicalType(R->getValueType()); // FIXME: If the pointee isn't an integer type, should we flag a warning? // People can do weird stuff with pointers. diff --git a/lib/Checker/BasicStore.cpp b/lib/Checker/BasicStore.cpp index 62c8d9c..f82e1b2 100644 --- a/lib/Checker/BasicStore.cpp +++ b/lib/Checker/BasicStore.cpp @@ -52,7 +52,7 @@ public: Store InvalidateRegions(Store store, const MemRegion * const *Begin, const MemRegion * const *End, const Expr *E, unsigned Count, InvalidatedSymbols *IS, - bool invalidateGlobals); + bool invalidateGlobals, InvalidatedRegions *Regions); Store scanForIvars(Stmt *B, const Decl* SelfDecl, const MemRegion *SelfRegion, Store St); @@ -61,11 +61,6 @@ public: Store Remove(Store St, Loc loc); Store getInitialStore(const LocationContext *InitLoc); - // FIXME: Investigate what is using this. This method should be removed. - virtual Loc getLoc(const VarDecl* VD, const LocationContext *LC) { - return ValMgr.makeLoc(MRMgr.getVarRegion(VD, LC)); - } - Store BindCompoundLiteral(Store store, const CompoundLiteralExpr*, const LocationContext*, SVal val) { return store; @@ -77,9 +72,8 @@ public: /// RemoveDeadBindings - Scans a BasicStore of 'state' for dead values. /// It updatees the GRState object in place with the values removed. - const GRState *RemoveDeadBindings(GRState &state, - const StackFrameContext *LCtx, - SymbolReaper& SymReaper, + Store RemoveDeadBindings(Store store, const StackFrameContext *LCtx, + SymbolReaper& SymReaper, llvm::SmallVectorImpl<const MemRegion*>& RegionRoots); void iterBindings(Store store, BindingsHandler& f); @@ -103,8 +97,6 @@ public: private: SVal LazyRetrieve(Store store, const TypedRegion *R); - - ASTContext& getContext() { return StateMgr.getContext(); } }; } // end anonymous namespace @@ -228,17 +220,15 @@ Store BasicStoreManager::Bind(Store store, Loc loc, SVal V) { return VBFactory.Add(B, R, V).getRoot(); } - ASTContext &C = StateMgr.getContext(); - // Special case: handle store of pointer values (Loc) to pointers via // a cast to intXX_t*, void*, etc. This is needed to handle // OSCompareAndSwap32Barrier/OSCompareAndSwap64Barrier. if (isa<Loc>(V) || isa<nonloc::LocAsInteger>(V)) if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { // FIXME: Should check for index 0. - QualType T = ER->getLocationType(C); + QualType T = ER->getLocationType(); - if (isHigherOrderRawPtr(T, C)) + if (isHigherOrderRawPtr(T, Ctx)) R = ER->getSuperRegion(); } @@ -249,7 +239,7 @@ Store BasicStoreManager::Bind(Store store, Loc loc, SVal V) { // Do not bind to arrays. We need to explicitly check for this so that // we do not encounter any weirdness of trying to load/store from arrays. - if (TyR->isBoundable() && TyR->getValueType(C)->isArrayType()) + if (TyR->isBoundable() && TyR->getValueType()->isArrayType()) return store; if (nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(&V)) { @@ -259,7 +249,7 @@ Store BasicStoreManager::Bind(Store store, Loc loc, SVal V) { // a pointer. We may wish to flag a type error here if the types // are incompatible. This may also cause lots of breakage // elsewhere. Food for thought. - if (TyR->isBoundable() && Loc::IsLocType(TyR->getValueType(C))) + if (TyR->isBoundable() && Loc::IsLocType(TyR->getValueType())) V = X->getLoc(); } @@ -285,12 +275,11 @@ Store BasicStoreManager::Remove(Store store, Loc loc) { } } -const GRState *BasicStoreManager::RemoveDeadBindings(GRState &state, +Store BasicStoreManager::RemoveDeadBindings(Store store, const StackFrameContext *LCtx, SymbolReaper& SymReaper, llvm::SmallVectorImpl<const MemRegion*>& RegionRoots) { - Store store = state.getStore(); BindingsTy B = GetBindings(store); typedef SVal::symbol_iterator symbol_iterator; @@ -365,8 +354,7 @@ const GRState *BasicStoreManager::RemoveDeadBindings(GRState &state, } } - state.setStore(store); - return StateMgr.getPersistentState(state); + return store; } Store BasicStoreManager::scanForIvars(Stmt *B, const Decl* SelfDecl, @@ -406,10 +394,10 @@ Store BasicStoreManager::getInitialStore(const LocationContext *InitLoc) { Store St = VBFactory.GetEmptyMap().getRoot(); for (LVDataTy::decl_iterator I=D.begin_decl(), E=D.end_decl(); I != E; ++I) { - NamedDecl* ND = const_cast<NamedDecl*>(I->first); + const NamedDecl* ND = I->first; // Handle implicit parameters. - if (ImplicitParamDecl* PD = dyn_cast<ImplicitParamDecl>(ND)) { + if (const ImplicitParamDecl* PD = dyn_cast<ImplicitParamDecl>(ND)) { const Decl& CD = *InitLoc->getDecl(); if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CD)) { if (MD->getSelfDecl() == PD) { @@ -449,11 +437,11 @@ Store BasicStoreManager::BindDeclInternal(Store store, const VarRegion* VR, // will not be called more than once. // Static global variables should not be visited here. - assert(!(VD->getStorageClass() == VarDecl::Static && + assert(!(VD->getStorageClass() == SC_Static && VD->isFileVarDecl())); // Process static variables. - if (VD->getStorageClass() == VarDecl::Static) { + if (VD->getStorageClass() == SC_Static) { // C99: 6.7.8 Initialization // If an object that has static storage duration is not initialized // explicitly, then: @@ -465,12 +453,9 @@ Store BasicStoreManager::BindDeclInternal(Store store, const VarRegion* VR, if (Loc::IsLocType(T)) store = Bind(store, loc::MemRegionVal(VR), loc::ConcreteInt(BasicVals.getValue(0, T))); - else if (T->isIntegerType()) + else if (T->isIntegerType() && T->isScalarType()) store = Bind(store, loc::MemRegionVal(VR), nonloc::ConcreteInt(BasicVals.getValue(0, T))); - else { - // assert(0 && "ignore other types of variables"); - } } else { store = Bind(store, loc::MemRegionVal(VR), *InitVal); } @@ -478,7 +463,8 @@ Store BasicStoreManager::BindDeclInternal(Store store, const VarRegion* VR, } else { // Process local scalar variables. QualType T = VD->getType(); - if (ValMgr.getSymbolManager().canSymbolicate(T)) { + // BasicStore only supports scalars. + if (T->isScalarType() && ValMgr.getSymbolManager().canSymbolicate(T)) { SVal V = InitVal ? *InitVal : UndefinedVal(); store = Bind(store, loc::MemRegionVal(VR), V); } @@ -523,11 +509,12 @@ StoreManager::BindingsHandler::~BindingsHandler() {} Store BasicStoreManager::InvalidateRegions(Store store, - const MemRegion * const *I, - const MemRegion * const *End, - const Expr *E, unsigned Count, - InvalidatedSymbols *IS, - bool invalidateGlobals) { + const MemRegion * const *I, + const MemRegion * const *End, + const Expr *E, unsigned Count, + InvalidatedSymbols *IS, + bool invalidateGlobals, + InvalidatedRegions *Regions) { if (invalidateGlobals) { BindingsTy B = GetBindings(store); for (BindingsTy::iterator I=B.begin(), End=B.end(); I != End; ++I) { @@ -545,6 +532,8 @@ Store BasicStoreManager::InvalidateRegions(Store store, continue; } store = InvalidateRegion(store, *I, E, Count, IS); + if (Regions) + Regions->push_back(R); } // FIXME: This is copy-and-paste from RegionStore.cpp. @@ -558,6 +547,8 @@ Store BasicStoreManager::InvalidateRegions(Store store, Count); store = Bind(store, loc::MemRegionVal(GS), V); + if (Regions) + Regions->push_back(GS); } return store; @@ -582,7 +573,7 @@ Store BasicStoreManager::InvalidateRegion(Store store, } } - QualType T = cast<TypedRegion>(R)->getValueType(R->getContext()); + QualType T = cast<TypedRegion>(R)->getValueType(); SVal V = ValMgr.getConjuredSymbolVal(R, E, T, Count); return Bind(store, loc::MemRegionVal(R), V); } diff --git a/lib/Checker/BasicValueFactory.cpp b/lib/Checker/BasicValueFactory.cpp index 246beea..4c9b109 100644 --- a/lib/Checker/BasicValueFactory.cpp +++ b/lib/Checker/BasicValueFactory.cpp @@ -149,22 +149,22 @@ BasicValueFactory::EvaluateAPSInt(BinaryOperator::Opcode Op, default: assert (false && "Invalid Opcode."); - case BinaryOperator::Mul: + case BO_Mul: return &getValue( V1 * V2 ); - case BinaryOperator::Div: + case BO_Div: return &getValue( V1 / V2 ); - case BinaryOperator::Rem: + case BO_Rem: return &getValue( V1 % V2 ); - case BinaryOperator::Add: + case BO_Add: return &getValue( V1 + V2 ); - case BinaryOperator::Sub: + case BO_Sub: return &getValue( V1 - V2 ); - case BinaryOperator::Shl: { + case BO_Shl: { // FIXME: This logic should probably go higher up, where we can // test these conditions symbolically. @@ -182,7 +182,7 @@ BasicValueFactory::EvaluateAPSInt(BinaryOperator::Opcode Op, return &getValue( V1.operator<<( (unsigned) Amt )); } - case BinaryOperator::Shr: { + case BO_Shr: { // FIXME: This logic should probably go higher up, where we can // test these conditions symbolically. @@ -200,33 +200,33 @@ BasicValueFactory::EvaluateAPSInt(BinaryOperator::Opcode Op, return &getValue( V1.operator>>( (unsigned) Amt )); } - case BinaryOperator::LT: + case BO_LT: return &getTruthValue( V1 < V2 ); - case BinaryOperator::GT: + case BO_GT: return &getTruthValue( V1 > V2 ); - case BinaryOperator::LE: + case BO_LE: return &getTruthValue( V1 <= V2 ); - case BinaryOperator::GE: + case BO_GE: return &getTruthValue( V1 >= V2 ); - case BinaryOperator::EQ: + case BO_EQ: return &getTruthValue( V1 == V2 ); - case BinaryOperator::NE: + case BO_NE: return &getTruthValue( V1 != V2 ); // Note: LAnd, LOr, Comma are handled specially by higher-level logic. - case BinaryOperator::And: + case BO_And: return &getValue( V1 & V2 ); - case BinaryOperator::Or: + case BO_Or: return &getValue( V1 | V2 ); - case BinaryOperator::Xor: + case BO_Xor: return &getValue( V1 ^ V2 ); } } diff --git a/lib/Checker/BugReporter.cpp b/lib/Checker/BugReporter.cpp index 0422d80..bffbd52 100644 --- a/lib/Checker/BugReporter.cpp +++ b/lib/Checker/BugReporter.cpp @@ -94,8 +94,8 @@ static const Stmt* GetNextStmt(const ExplodedNode* N) { case Stmt::ChooseExprClass: case Stmt::ConditionalOperatorClass: continue; case Stmt::BinaryOperatorClass: { - BinaryOperator::Opcode Op = cast<BinaryOperator>(S)->getOpcode(); - if (Op == BinaryOperator::LAnd || Op == BinaryOperator::LOr) + BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode(); + if (Op == BO_LAnd || Op == BO_LOr) continue; break; } @@ -177,18 +177,9 @@ public: } virtual NodeMapClosure& getNodeResolver() { return NMC; } - BugReport& getReport() { return *R; } PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S); - PathDiagnosticLocation - getEnclosingStmtLocation(const PathDiagnosticLocation &L) { - if (const Stmt *S = L.asStmt()) - return getEnclosingStmtLocation(S); - - return L; - } - PathDiagnosticClient::PathGenerationScheme getGenerationScheme() const { return PDC ? PDC->getGenerationScheme() : PathDiagnosticClient::Extensive; } @@ -541,9 +532,9 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) { - CFGBlock* Src = BE->getSrc(); - CFGBlock* Dst = BE->getDst(); - Stmt* T = Src->getTerminator(); + const CFGBlock* Src = BE->getSrc(); + const CFGBlock* Dst = BE->getDst(); + const Stmt* T = Src->getTerminator(); if (!T) continue; @@ -577,7 +568,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, std::string sbuf; llvm::raw_string_ostream os(sbuf); - if (Stmt* S = Dst->getLabel()) { + if (const Stmt* S = Dst->getLabel()) { PathDiagnosticLocation End(S, SMgr); switch (S->getStmtClass()) { @@ -593,17 +584,17 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, case Stmt::CaseStmtClass: { os << "Control jumps to 'case "; - CaseStmt* Case = cast<CaseStmt>(S); - Expr* LHS = Case->getLHS()->IgnoreParenCasts(); + const CaseStmt* Case = cast<CaseStmt>(S); + const Expr* LHS = Case->getLHS()->IgnoreParenCasts(); // Determine if it is an enum. bool GetRawInt = true; - if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(LHS)) { + if (const DeclRefExpr* DR = dyn_cast<DeclRefExpr>(LHS)) { // FIXME: Maybe this should be an assertion. Are there cases // were it is not an EnumConstantDecl? - EnumConstantDecl* D = - dyn_cast<EnumConstantDecl>(DR->getDecl()); + const EnumConstantDecl* D = + dyn_cast<EnumConstantDecl>(DR->getDecl()); if (D) { GetRawInt = false; @@ -668,12 +659,12 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (!PDB.supportsLogicalOpControlFlow()) break; - BinaryOperator *B = cast<BinaryOperator>(T); + const BinaryOperator *B = cast<BinaryOperator>(T); std::string sbuf; llvm::raw_string_ostream os(sbuf); os << "Left side of '"; - if (B->getOpcode() == BinaryOperator::LAnd) { + if (B->getOpcode() == BO_LAnd) { os << "&&" << "' is "; if (*(Src->succ_begin()+1) == Dst) { @@ -692,7 +683,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, } } else { - assert(B->getOpcode() == BinaryOperator::LOr); + assert(B->getOpcode() == BO_LOr); os << "||" << "' is "; if (*(Src->succ_begin()+1) == Dst) { @@ -902,8 +893,6 @@ class EdgeBuilder { CLocs.pop_back(); } - PathDiagnosticLocation IgnoreParens(const PathDiagnosticLocation &L); - public: EdgeBuilder(PathDiagnostic &pd, PathDiagnosticBuilder &pdb) : PD(pd), PDB(pdb) { @@ -935,10 +924,6 @@ public: void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false); - void addEdge(const Stmt *S, bool alwaysAdd = false) { - addEdge(PathDiagnosticLocation(S, PDB.getSourceManager()), alwaysAdd); - } - void rawAddEdge(PathDiagnosticLocation NewLoc); void addContext(const Stmt *S); @@ -1006,14 +991,6 @@ bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container, SM.getInstantiationColumnNumber(ContainerREnd))); } -PathDiagnosticLocation -EdgeBuilder::IgnoreParens(const PathDiagnosticLocation &L) { - if (const Expr* E = dyn_cast_or_null<Expr>(L.asStmt())) - return PathDiagnosticLocation(E->IgnoreParenCasts(), - PDB.getSourceManager()); - return L; -} - void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { if (!PrevLoc.isValid()) { PrevLoc = NewLoc; diff --git a/lib/Checker/BugReporterVisitors.cpp b/lib/Checker/BugReporterVisitors.cpp index 776e12b..91cf349 100644 --- a/lib/Checker/BugReporterVisitors.cpp +++ b/lib/Checker/BugReporterVisitors.cpp @@ -31,7 +31,7 @@ const Stmt *clang::bugreporter::GetDerefExpr(const ExplodedNode *N) { const Stmt *S = N->getLocationAs<PostStmt>()->getStmt(); if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) { - if (U->getOpcode() == UnaryOperator::Deref) + if (U->getOpcode() == UO_Deref) return U->getSubExpr()->IgnoreParenCasts(); } else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) { @@ -143,10 +143,9 @@ public: if (isa<loc::ConcreteInt>(V)) { bool b = false; - ASTContext &C = BRC.getASTContext(); if (R->isBoundable()) { if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { - if (TR->getValueType(C)->isObjCObjectPointerType()) { + if (TR->getValueType()->isObjCObjectPointerType()) { os << "initialized to nil"; b = true; } @@ -174,10 +173,9 @@ public: if (os.str().empty()) { if (isa<loc::ConcreteInt>(V)) { bool b = false; - ASTContext &C = BRC.getASTContext(); if (R->isBoundable()) { if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { - if (TR->getValueType(C)->isObjCObjectPointerType()) { + if (TR->getValueType()->isObjCObjectPointerType()) { os << "nil object reference stored to "; b = true; } @@ -209,7 +207,7 @@ public: ProgramPoint P = N->getLocation(); if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { - CFGBlock *BSrc = BE->getSrc(); + const CFGBlock *BSrc = BE->getSrc(); S = BSrc->getTerminatorCondition(); } else if (PostStmt *PS = dyn_cast<PostStmt>(&P)) { @@ -282,7 +280,7 @@ public: ProgramPoint P = N->getLocation(); if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { - CFGBlock *BSrc = BE->getSrc(); + const CFGBlock *BSrc = BE->getSrc(); S = BSrc->getTerminatorCondition(); } else if (PostStmt *PS = dyn_cast<PostStmt>(&P)) { @@ -421,3 +419,40 @@ public: void clang::bugreporter::registerNilReceiverVisitor(BugReporterContext &BRC) { BRC.addVisitor(new NilReceiverVisitor()); } + +// Registers every VarDecl inside a Stmt with a last store vistor. +void clang::bugreporter::registerVarDeclsLastStore(BugReporterContext &BRC, + const void *stmt, + const ExplodedNode *N) { + const Stmt *S = static_cast<const Stmt *>(stmt); + + std::deque<const Stmt *> WorkList; + + WorkList.push_back(S); + + while (!WorkList.empty()) { + const Stmt *Head = WorkList.front(); + WorkList.pop_front(); + + GRStateManager &StateMgr = BRC.getStateManager(); + const GRState *state = N->getState(); + + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Head)) { + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + const VarRegion *R = + StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); + + // What did we load? + SVal V = state->getSVal(S); + + if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V)) { + ::registerFindLastStore(BRC, R, V); + } + } + } + + for (Stmt::const_child_iterator I = Head->child_begin(); + I != Head->child_end(); ++I) + WorkList.push_back(*I); + } +} diff --git a/lib/Checker/CFRefCount.cpp b/lib/Checker/CFRefCount.cpp index 3c74cd8..6fa48b2 100644 --- a/lib/Checker/CFRefCount.cpp +++ b/lib/Checker/CFRefCount.cpp @@ -82,8 +82,7 @@ public: static const ObjCMethodDecl* ResolveToInterfaceMethodDecl(const ObjCMethodDecl *MD) { - ObjCInterfaceDecl *ID = - const_cast<ObjCInterfaceDecl*>(MD->getClassInterface()); + const ObjCInterfaceDecl *ID = MD->getClassInterface(); return MD->isInstanceMethod() ? ID->lookupInstanceMethod(MD->getSelector()) @@ -93,11 +92,11 @@ ResolveToInterfaceMethodDecl(const ObjCMethodDecl *MD) { namespace { class GenericNodeBuilder { GRStmtNodeBuilder *SNB; - Stmt *S; + const Stmt *S; const void *tag; GREndPathNodeBuilder *ENB; public: - GenericNodeBuilder(GRStmtNodeBuilder &snb, Stmt *s, + GenericNodeBuilder(GRStmtNodeBuilder &snb, const Stmt *s, const void *t) : SNB(&snb), S(s), tag(t), ENB(0) {} @@ -195,12 +194,6 @@ public: static RetEffect MakeNoRet() { return RetEffect(NoRet); } - - void Profile(llvm::FoldingSetNodeID& ID) const { - ID.AddInteger((unsigned)K); - ID.AddInteger((unsigned)O); - ID.AddInteger(index); - } }; //===----------------------------------------------------------------------===// @@ -239,9 +232,6 @@ private: RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t) : kind(k), okind(o), Cnt(cnt), ACnt(acnt), T(t) {} - RefVal(Kind k, unsigned cnt = 0) - : kind(k), okind(RetEffect::AnyObj), Cnt(cnt), ACnt(0) {} - public: Kind getKind() const { return kind; } @@ -256,12 +246,6 @@ public: QualType getType() const { return T; } - // Useful predicates. - - static bool isError(Kind k) { return k >= ERROR_START; } - - static bool isLeak(Kind k) { return k >= ERROR_LEAK_START; } - bool isOwned() const { return getKind() == Owned; } @@ -278,11 +262,6 @@ public: return getKind() == ReturnedNotOwned; } - bool isNonLeakError() const { - Kind k = getKind(); - return isError(k) && !isLeak(k); - } - static RefVal makeOwned(RetEffect::ObjKind o, QualType t, unsigned Count = 1) { return RefVal(Owned, o, Count, 0, t); @@ -474,11 +453,6 @@ public: DefaultArgEffect = E; } - /// setArg - Set the argument effect on the argument specified by idx. - void setArgEffect(ArgEffects::Factory& AF, unsigned idx, ArgEffect E) { - Args = AF.Add(Args, idx, E); - } - /// getRetEffect - Returns the effect on the return value of the call. RetEffect getRetEffect() const { return Ret; } @@ -492,28 +466,6 @@ public: /// getReceiverEffect - Returns the effect on the receiver of the call. /// This is only meaningful if the summary applies to an ObjCMessageExpr*. ArgEffect getReceiverEffect() const { return Receiver; } - - /// setReceiverEffect - Set the effect on the receiver of the call. - void setReceiverEffect(ArgEffect E) { Receiver = E; } - - typedef ArgEffects::iterator ExprIterator; - - ExprIterator begin_args() const { return Args.begin(); } - ExprIterator end_args() const { return Args.end(); } - - static void Profile(llvm::FoldingSetNodeID& ID, ArgEffects A, - RetEffect RetEff, ArgEffect DefaultEff, - ArgEffect ReceiverEff, bool EndPath) { - ID.Add(A); - ID.Add(RetEff); - ID.AddInteger((unsigned) DefaultEff); - ID.AddInteger((unsigned) ReceiverEff); - ID.AddInteger((unsigned) EndPath); - } - - void Profile(llvm::FoldingSetNodeID& ID) const { - Profile(ID, Args, Ret, DefaultArgEffect, Receiver, EndPath); - } }; } // end anonymous namespace @@ -618,11 +570,6 @@ public: return Summ; } - - RetainSummary* find(Expr* Receiver, Selector S) { - return find(getReceiverDecl(Receiver), S); - } - RetainSummary* find(IdentifierInfo* II, Selector S) { // FIXME: Class method lookup. Right now we dont' have a good way // of going between IdentifierInfo* and the class hierarchy. @@ -634,47 +581,6 @@ public: return I == M.end() ? NULL : I->second; } - const ObjCInterfaceDecl* getReceiverDecl(Expr* E) { - if (const ObjCObjectPointerType* PT = - E->getType()->getAs<ObjCObjectPointerType>()) - return PT->getInterfaceDecl(); - - return NULL; - } - - RetainSummary*& operator[](ObjCMessageExpr* ME) { - - Selector S = ME->getSelector(); - - const ObjCInterfaceDecl* OD = 0; - bool IsInstanceMessage = false; - switch (ME->getReceiverKind()) { - case ObjCMessageExpr::Instance: - OD = getReceiverDecl(ME->getInstanceReceiver()); - IsInstanceMessage = true; - break; - - case ObjCMessageExpr::SuperInstance: - IsInstanceMessage = true; - OD = ME->getSuperType()->getAs<ObjCObjectPointerType>() - ->getInterfaceDecl(); - break; - - case ObjCMessageExpr::Class: - OD = ME->getClassReceiver()->getAs<ObjCObjectType>()->getInterface(); - break; - - case ObjCMessageExpr::SuperClass: - OD = ME->getSuperType()->getAs<ObjCObjectType>()->getInterface(); - break; - } - - if (IsInstanceMessage) - return OD ? M[ObjCSummaryKey(OD->getIdentifier(), S)] : M[S]; - - return M[ObjCSummaryKey(OD->getIdentifier(), S)]; - } - RetainSummary*& operator[](ObjCSummaryKey K) { return M[K]; } @@ -696,7 +602,7 @@ class RetainSummaryManager { // Typedefs. //==-----------------------------------------------------------------==// - typedef llvm::DenseMap<FunctionDecl*, RetainSummary*> + typedef llvm::DenseMap<const FunctionDecl*, RetainSummary*> FuncSummariesTy; typedef ObjCSummaryCache ObjCMethodSummariesTy; @@ -766,9 +672,10 @@ public: RetainSummary* getUnarySummary(const FunctionType* FT, UnaryFuncKind func); - RetainSummary* getCFSummaryCreateRule(FunctionDecl* FD); - RetainSummary* getCFSummaryGetRule(FunctionDecl* FD); - RetainSummary* getCFCreateGetRuleSummary(FunctionDecl* FD, StringRef FName); + RetainSummary* getCFSummaryCreateRule(const FunctionDecl* FD); + RetainSummary* getCFSummaryGetRule(const FunctionDecl* FD); + RetainSummary* getCFCreateGetRuleSummary(const FunctionDecl* FD, + StringRef FName); RetainSummary* getPersistentSummary(ArgEffects AE, RetEffect RetEff, ArgEffect ReceiverEff = DoNothing, @@ -796,12 +703,6 @@ public: void InitializeClassMethodSummaries(); void InitializeMethodSummaries(); private: - - void addClsMethSummary(IdentifierInfo* ClsII, Selector S, - RetainSummary* Summ) { - ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; - } - void addNSObjectClsMethSummary(Selector S, RetainSummary *Summ) { ObjCClassMethodSummaries[S] = Summ; } @@ -892,7 +793,7 @@ public: ~RetainSummaryManager(); - RetainSummary* getSummary(FunctionDecl* FD); + RetainSummary* getSummary(const FunctionDecl* FD); RetainSummary *getInstanceMethodSummary(const ObjCMessageExpr *ME, const GRState *state, @@ -999,15 +900,15 @@ RetainSummaryManager::getPersistentSummary(ArgEffects AE, RetEffect RetEff, // Summary creation for functions (largely uses of Core Foundation). //===----------------------------------------------------------------------===// -static bool isRetain(FunctionDecl* FD, StringRef FName) { +static bool isRetain(const FunctionDecl* FD, StringRef FName) { return FName.endswith("Retain"); } -static bool isRelease(FunctionDecl* FD, StringRef FName) { +static bool isRelease(const FunctionDecl* FD, StringRef FName) { return FName.endswith("Release"); } -RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD) { +RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) { // Look up a summary in our cache of FunctionDecls -> Summaries. FuncSummariesTy::iterator I = FuncSummaries.find(FD); if (I != FuncSummaries.end()) @@ -1201,7 +1102,7 @@ RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD) { } RetainSummary* -RetainSummaryManager::getCFCreateGetRuleSummary(FunctionDecl* FD, +RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl* FD, StringRef FName) { if (FName.find("Create") != StringRef::npos || @@ -1250,7 +1151,8 @@ RetainSummaryManager::getUnarySummary(const FunctionType* FT, } } -RetainSummary* RetainSummaryManager::getCFSummaryCreateRule(FunctionDecl* FD) { +RetainSummary* +RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl* FD) { assert (ScratchArgs.isEmpty()); if (FD->getIdentifier() == CFDictionaryCreateII) { @@ -1261,7 +1163,8 @@ RetainSummary* RetainSummaryManager::getCFSummaryCreateRule(FunctionDecl* FD) { return getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true)); } -RetainSummary* RetainSummaryManager::getCFSummaryGetRule(FunctionDecl* FD) { +RetainSummary* +RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl* FD) { assert (ScratchArgs.isEmpty()); return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF), DoNothing, DoNothing); @@ -1767,7 +1670,7 @@ private: void ProcessNonLeakError(ExplodedNodeSet& Dst, GRStmtNodeBuilder& Builder, - Expr* NodeExpr, SourceRange ErrorRange, + const Expr* NodeExpr, SourceRange ErrorRange, ExplodedNode* Pred, const GRState* St, RefVal::Kind hasErr, SymbolRef Sym); @@ -1810,33 +1713,26 @@ public: void EvalSummary(ExplodedNodeSet& Dst, GRExprEngine& Eng, GRStmtNodeBuilder& Builder, - Expr* Ex, + const Expr* Ex, InstanceReceiver Receiver, const RetainSummary& Summ, const MemRegion *Callee, - ExprIterator arg_beg, ExprIterator arg_end, + ConstExprIterator arg_beg, ConstExprIterator arg_end, ExplodedNode* Pred, const GRState *state); virtual void EvalCall(ExplodedNodeSet& Dst, GRExprEngine& Eng, GRStmtNodeBuilder& Builder, - CallExpr* CE, SVal L, + const CallExpr* CE, SVal L, ExplodedNode* Pred); virtual void EvalObjCMessageExpr(ExplodedNodeSet& Dst, GRExprEngine& Engine, GRStmtNodeBuilder& Builder, - ObjCMessageExpr* ME, + const ObjCMessageExpr* ME, ExplodedNode* Pred, const GRState *state); - - bool EvalObjCMessageExprAux(ExplodedNodeSet& Dst, - GRExprEngine& Engine, - GRStmtNodeBuilder& Builder, - ObjCMessageExpr* ME, - ExplodedNode* Pred); - // Stores. virtual void EvalBind(GRStmtNodeBuilderRef& B, SVal location, SVal val); @@ -1861,7 +1757,7 @@ public: virtual void EvalReturn(ExplodedNodeSet& Dst, GRExprEngine& Engine, GRStmtNodeBuilder& Builder, - ReturnStmt* S, + const ReturnStmt* S, ExplodedNode* Pred); // Assumptions. @@ -1934,7 +1830,6 @@ namespace { public: CFRefCount& getTF() { return TF; } - const CFRefCount& getTF() const { return TF; } // FIXME: Eventually remove. virtual const char* getDescription() const = 0; @@ -2049,9 +1944,6 @@ namespace { CFRefBug& getBugType() { return (CFRefBug&) RangedBugReport::getBugType(); } - const CFRefBug& getBugType() const { - return (const CFRefBug&) RangedBugReport::getBugType(); - } virtual void getRanges(const SourceRange*& beg, const SourceRange*& end) { if (!getBugType().isLeak()) @@ -2605,11 +2497,12 @@ static QualType GetReturnType(const Expr* RetE, ASTContext& Ctx) { void CFRefCount::EvalSummary(ExplodedNodeSet& Dst, GRExprEngine& Eng, GRStmtNodeBuilder& Builder, - Expr* Ex, + const Expr* Ex, InstanceReceiver Receiver, const RetainSummary& Summ, const MemRegion *Callee, - ExprIterator arg_beg, ExprIterator arg_end, + ConstExprIterator arg_beg, + ConstExprIterator arg_end, ExplodedNode* Pred, const GRState *state) { // Evaluate the effect of the arguments. @@ -2620,19 +2513,25 @@ void CFRefCount::EvalSummary(ExplodedNodeSet& Dst, llvm::SmallVector<const MemRegion*, 10> RegionsToInvalidate; - for (ExprIterator I = arg_beg; I != arg_end; ++I, ++idx) { + // HACK: Symbols that have ref-count state that are referenced directly + // (not as structure or array elements, or via bindings) by an argument + // should not have their ref-count state stripped after we have + // done an invalidation pass. + llvm::DenseSet<SymbolRef> WhitelistedSymbols; + + for (ConstExprIterator I = arg_beg; I != arg_end; ++I, ++idx) { SVal V = state->getSValAsScalarOrLoc(*I); SymbolRef Sym = V.getAsLocSymbol(); if (Sym) if (RefBindings::data_type* T = state->get<RefBindings>(Sym)) { + WhitelistedSymbols.insert(Sym); state = Update(state, Sym, *T, Summ.getArg(idx), hasErr); if (hasErr) { ErrorRange = (*I)->getSourceRange(); ErrorSym = Sym; break; } - continue; } tryAgain: @@ -2703,22 +2602,22 @@ void CFRefCount::EvalSummary(ExplodedNodeSet& Dst, // expression (the context) and the expression itself. This should // disambiguate conjured symbols. unsigned Count = Builder.getCurrentBlockCount(); - StoreManager& StoreMgr = Eng.getStateManager().getStoreManager(); StoreManager::InvalidatedSymbols IS; - Store store = state->getStore(); // NOTE: Even if RegionsToInvalidate is empty, we must still invalidate // global variables. - store = StoreMgr.InvalidateRegions(store, RegionsToInvalidate.data(), - RegionsToInvalidate.data() + - RegionsToInvalidate.size(), - Ex, Count, &IS, - /* invalidateGlobals = */ true); + state = state->InvalidateRegions(RegionsToInvalidate.data(), + RegionsToInvalidate.data() + + RegionsToInvalidate.size(), + Ex, Count, &IS, + /* invalidateGlobals = */ true); - state = state->makeWithStore(store); for (StoreManager::InvalidatedSymbols::iterator I = IS.begin(), E = IS.end(); I!=E; ++I) { - // Remove any existing reference-count binding. + SymbolRef sym = *I; + if (WhitelistedSymbols.count(sym)) + continue; + // Remove any existing reference-count binding. state = state->remove<RefBindings>(*I); } @@ -2860,7 +2759,7 @@ void CFRefCount::EvalSummary(ExplodedNodeSet& Dst, void CFRefCount::EvalCall(ExplodedNodeSet& Dst, GRExprEngine& Eng, GRStmtNodeBuilder& Builder, - CallExpr* CE, SVal L, + const CallExpr* CE, SVal L, ExplodedNode* Pred) { RetainSummary *Summ = 0; @@ -2874,7 +2773,7 @@ void CFRefCount::EvalCall(ExplodedNodeSet& Dst, else { const FunctionDecl* FD = L.getAsFunctionDecl(); Summ = !FD ? Summaries.getDefaultSummary() : - Summaries.getSummary(const_cast<FunctionDecl*>(FD)); + Summaries.getSummary(FD); } assert(Summ); @@ -2885,7 +2784,7 @@ void CFRefCount::EvalCall(ExplodedNodeSet& Dst, void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet& Dst, GRExprEngine& Eng, GRStmtNodeBuilder& Builder, - ObjCMessageExpr* ME, + const ObjCMessageExpr* ME, ExplodedNode* Pred, const GRState *state) { RetainSummary *Summ = @@ -2956,10 +2855,10 @@ void CFRefCount::EvalBind(GRStmtNodeBuilderRef& B, SVal location, SVal val) { void CFRefCount::EvalReturn(ExplodedNodeSet& Dst, GRExprEngine& Eng, GRStmtNodeBuilder& Builder, - ReturnStmt* S, + const ReturnStmt* S, ExplodedNode* Pred) { - Expr* RetE = S->getRetValue(); + const Expr* RetE = S->getRetValue(); if (!RetE) return; @@ -3404,7 +3303,7 @@ void CFRefCount::EvalDeadSymbols(ExplodedNodeSet& Dst, ExplodedNode* Pred, const GRState* state, SymbolReaper& SymReaper) { - Stmt *S = Builder.getStmt(); + const Stmt *S = Builder.getStmt(); RefBindings B = state->get<RefBindings>(); // Update counts from autorelease pools @@ -3454,7 +3353,8 @@ void CFRefCount::EvalDeadSymbols(ExplodedNodeSet& Dst, void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst, GRStmtNodeBuilder& Builder, - Expr* NodeExpr, SourceRange ErrorRange, + const Expr* NodeExpr, + SourceRange ErrorRange, ExplodedNode* Pred, const GRState* St, RefVal::Kind hasErr, SymbolRef Sym) { diff --git a/lib/Checker/CMakeLists.txt b/lib/Checker/CMakeLists.txt index 259346a..5b54f0d 100644 --- a/lib/Checker/CMakeLists.txt +++ b/lib/Checker/CMakeLists.txt @@ -4,6 +4,7 @@ add_clang_library(clangChecker AdjustedReturnValueChecker.cpp AggExprVisitor.cpp AnalysisConsumer.cpp + AnalysisManager.cpp ArrayBoundChecker.cpp AttrNonNullChecker.cpp BasicConstraintManager.cpp @@ -15,7 +16,6 @@ add_clang_library(clangChecker BuiltinFunctionChecker.cpp CFRefCount.cpp CallAndMessageChecker.cpp - CallInliner.cpp CastSizeChecker.cpp CastToStructChecker.cpp CheckDeadStores.cpp @@ -24,6 +24,7 @@ add_clang_library(clangChecker CheckSecuritySyntaxOnly.cpp CheckSizeofPointer.cpp Checker.cpp + CheckerHelpers.cpp CocoaConventions.cpp CStringChecker.cpp DereferenceChecker.cpp @@ -74,6 +75,7 @@ add_clang_library(clangChecker UndefinedArraySubscriptChecker.cpp UndefinedAssignmentChecker.cpp UnixAPIChecker.cpp + UnreachableCodeChecker.cpp VLASizeChecker.cpp ValueManager.cpp ) diff --git a/lib/Checker/CStringChecker.cpp b/lib/Checker/CStringChecker.cpp index a92d409..9ea572f 100644 --- a/lib/Checker/CStringChecker.cpp +++ b/lib/Checker/CStringChecker.cpp @@ -15,19 +15,30 @@ #include "GRExprEngineExperimentalChecks.h" #include "clang/Checker/BugReporter/BugType.h" #include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Checker/PathSensitive/GRStateTrait.h" #include "llvm/ADT/StringSwitch.h" using namespace clang; namespace { class CStringChecker : public CheckerVisitor<CStringChecker> { - BugType *BT_Null, *BT_Bounds, *BT_Overlap; + BugType *BT_Null, *BT_Bounds, *BT_BoundsWrite, *BT_Overlap, *BT_NotCString; public: CStringChecker() - : BT_Null(0), BT_Bounds(0), BT_Overlap(0) {} + : BT_Null(0), BT_Bounds(0), BT_BoundsWrite(0), BT_Overlap(0), BT_NotCString(0) + {} static void *getTag() { static int tag; return &tag; } bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); + void PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS); + void MarkLiveSymbols(const GRState *state, SymbolReaper &SR); + void EvalDeadSymbols(CheckerContext &C, SymbolReaper &SR); + bool WantsRegionChangeUpdate(const GRState *state); + + const GRState *EvalRegionChanges(const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End, + bool*); typedef void (CStringChecker::*FnCheck)(CheckerContext &, const CallExpr *); @@ -40,26 +51,61 @@ public: void EvalMemcmp(CheckerContext &C, const CallExpr *CE); + void EvalStrlen(CheckerContext &C, const CallExpr *CE); + + void EvalStrcpy(CheckerContext &C, const CallExpr *CE); + void EvalStpcpy(CheckerContext &C, const CallExpr *CE); + void EvalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool ReturnEnd); + // Utility methods std::pair<const GRState*, const GRState*> AssumeZero(CheckerContext &C, const GRState *state, SVal V, QualType Ty); + const GRState *SetCStringLength(const GRState *state, const MemRegion *MR, + SVal StrLen); + SVal GetCStringLengthForRegion(CheckerContext &C, const GRState *&state, + const Expr *Ex, const MemRegion *MR); + SVal GetCStringLength(CheckerContext &C, const GRState *&state, + const Expr *Ex, SVal Buf); + + const GRState *InvalidateBuffer(CheckerContext &C, const GRState *state, + const Expr *Ex, SVal V); + + bool SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx, + const MemRegion *MR); + + // Re-usable checks const GRState *CheckNonNull(CheckerContext &C, const GRState *state, const Expr *S, SVal l); const GRState *CheckLocation(CheckerContext &C, const GRState *state, - const Expr *S, SVal l); + const Expr *S, SVal l, + bool IsDestination = false); const GRState *CheckBufferAccess(CheckerContext &C, const GRState *state, const Expr *Size, const Expr *FirstBuf, - const Expr *SecondBuf = NULL); + const Expr *SecondBuf = NULL, + bool FirstIsDestination = false); const GRState *CheckOverlap(CheckerContext &C, const GRState *state, const Expr *Size, const Expr *First, const Expr *Second); void EmitOverlapBug(CheckerContext &C, const GRState *state, const Stmt *First, const Stmt *Second); }; + +class CStringLength { +public: + typedef llvm::ImmutableMap<const MemRegion *, SVal> EntryMap; +}; } //end anonymous namespace +namespace clang { + template <> + struct GRStateTrait<CStringLength> + : public GRStatePartialTrait<CStringLength::EntryMap> { + static void *GDMIndex() { return CStringChecker::getTag(); } + }; +} + void clang::RegisterCStringChecker(GRExprEngine &Eng) { Eng.registerCheck(new CStringChecker()); } @@ -122,7 +168,8 @@ const GRState *CStringChecker::CheckNonNull(CheckerContext &C, // FIXME: This was originally copied from ArrayBoundChecker.cpp. Refactor? const GRState *CStringChecker::CheckLocation(CheckerContext &C, const GRState *state, - const Expr *S, SVal l) { + const Expr *S, SVal l, + bool IsDestination) { // If a previous check has failed, propagate the failure. if (!state) return NULL; @@ -136,7 +183,7 @@ const GRState *CStringChecker::CheckLocation(CheckerContext &C, if (!ER) return state; - assert(ER->getValueType(C.getASTContext()) == C.getASTContext().CharTy && + assert(ER->getValueType() == C.getASTContext().CharTy && "CheckLocation should only be called with char* ElementRegions"); // Get the size of the array. @@ -155,17 +202,26 @@ const GRState *CStringChecker::CheckLocation(CheckerContext &C, if (!N) return NULL; - if (!BT_Bounds) - BT_Bounds = new BuiltinBug("Out-of-bound array access", - "Byte string function accesses out-of-bound array element " - "(buffer overflow)"); + BuiltinBug *BT; + if (IsDestination) { + if (!BT_BoundsWrite) { + BT_BoundsWrite = new BuiltinBug("Out-of-bound array access", + "Byte string function overflows destination buffer"); + } + BT = static_cast<BuiltinBug*>(BT_BoundsWrite); + } else { + if (!BT_Bounds) { + BT_Bounds = new BuiltinBug("Out-of-bound array access", + "Byte string function accesses out-of-bound array element"); + } + BT = static_cast<BuiltinBug*>(BT_Bounds); + } // FIXME: It would be nice to eventually make this diagnostic more clear, // e.g., by referencing the original declaration or by saying *why* this // reference is outside the range. // Generate a report for this bug. - BuiltinBug *BT = static_cast<BuiltinBug*>(BT_Bounds); RangedBugReport *report = new RangedBugReport(*BT, BT->getDescription(), N); report->addRange(S->getSourceRange()); @@ -182,7 +238,8 @@ const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C, const GRState *state, const Expr *Size, const Expr *FirstBuf, - const Expr *SecondBuf) { + const Expr *SecondBuf, + bool FirstIsDestination) { // If a previous check has failed, propagate the failure. if (!state) return NULL; @@ -191,7 +248,7 @@ const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C, SValuator &SV = VM.getSValuator(); ASTContext &Ctx = C.getASTContext(); - QualType SizeTy = Ctx.getSizeType(); + QualType SizeTy = Size->getType(); QualType PtrTy = Ctx.getPointerType(Ctx.CharTy); // Check that the first buffer is non-null. @@ -208,18 +265,20 @@ const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C, // Compute the offset of the last element to be accessed: size-1. NonLoc One = cast<NonLoc>(VM.makeIntVal(1, SizeTy)); - NonLoc LastOffset = cast<NonLoc>(SV.EvalBinOpNN(state, BinaryOperator::Sub, + NonLoc LastOffset = cast<NonLoc>(SV.EvalBinOpNN(state, BO_Sub, *Length, One, SizeTy)); // Check that the first buffer is sufficently long. - Loc BufStart = cast<Loc>(SV.EvalCast(BufVal, PtrTy, FirstBuf->getType())); - SVal BufEnd - = SV.EvalBinOpLN(state, BinaryOperator::Add, BufStart, LastOffset, PtrTy); - state = CheckLocation(C, state, FirstBuf, BufEnd); + SVal BufStart = SV.EvalCast(BufVal, PtrTy, FirstBuf->getType()); + if (Loc *BufLoc = dyn_cast<Loc>(&BufStart)) { + SVal BufEnd = SV.EvalBinOpLN(state, BO_Add, *BufLoc, + LastOffset, PtrTy); + state = CheckLocation(C, state, FirstBuf, BufEnd, FirstIsDestination); - // If the buffer isn't large enough, abort. - if (!state) - return NULL; + // If the buffer isn't large enough, abort. + if (!state) + return NULL; + } // If there's a second buffer, check it as well. if (SecondBuf) { @@ -228,10 +287,12 @@ const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C, if (!state) return NULL; - BufStart = cast<Loc>(SV.EvalCast(BufVal, PtrTy, SecondBuf->getType())); - BufEnd - = SV.EvalBinOpLN(state, BinaryOperator::Add, BufStart, LastOffset, PtrTy); - state = CheckLocation(C, state, SecondBuf, BufEnd); + BufStart = SV.EvalCast(BufVal, PtrTy, SecondBuf->getType()); + if (Loc *BufLoc = dyn_cast<Loc>(&BufStart)) { + SVal BufEnd = SV.EvalBinOpLN(state, BO_Add, *BufLoc, + LastOffset, PtrTy); + state = CheckLocation(C, state, SecondBuf, BufEnd); + } } // Large enough or not, return this state! @@ -284,7 +345,7 @@ const GRState *CStringChecker::CheckOverlap(CheckerContext &C, // Which value comes first? QualType CmpTy = Ctx.IntTy; - SVal Reverse = SV.EvalBinOpLL(state, BinaryOperator::GT, + SVal Reverse = SV.EvalBinOpLL(state, BO_GT, *FirstLoc, *SecondLoc, CmpTy); DefinedOrUnknownSVal *ReverseTest = dyn_cast<DefinedOrUnknownSVal>(&Reverse); if (!ReverseTest) @@ -324,14 +385,14 @@ const GRState *CStringChecker::CheckOverlap(CheckerContext &C, return state; // Compute the end of the first buffer. Bail out if THAT fails. - SVal FirstEnd = SV.EvalBinOpLN(state, BinaryOperator::Add, + SVal FirstEnd = SV.EvalBinOpLN(state, BO_Add, *FirstStartLoc, *Length, CharPtrTy); Loc *FirstEndLoc = dyn_cast<Loc>(&FirstEnd); if (!FirstEndLoc) return state; // Is the end of the first buffer past the start of the second buffer? - SVal Overlap = SV.EvalBinOpLL(state, BinaryOperator::GT, + SVal Overlap = SV.EvalBinOpLL(state, BO_GT, *FirstEndLoc, *SecondLoc, CmpTy); DefinedOrUnknownSVal *OverlapTest = dyn_cast<DefinedOrUnknownSVal>(&Overlap); if (!OverlapTest) @@ -369,6 +430,222 @@ void CStringChecker::EmitOverlapBug(CheckerContext &C, const GRState *state, C.EmitReport(report); } +const GRState *CStringChecker::SetCStringLength(const GRState *state, + const MemRegion *MR, + SVal StrLen) { + assert(!StrLen.isUndef() && "Attempt to set an undefined string length"); + if (StrLen.isUnknown()) + return state; + + MR = MR->StripCasts(); + + switch (MR->getKind()) { + case MemRegion::StringRegionKind: + // FIXME: This can happen if we strcpy() into a string region. This is + // undefined [C99 6.4.5p6], but we should still warn about it. + return state; + + case MemRegion::SymbolicRegionKind: + case MemRegion::AllocaRegionKind: + case MemRegion::VarRegionKind: + case MemRegion::FieldRegionKind: + case MemRegion::ObjCIvarRegionKind: + return state->set<CStringLength>(MR, StrLen); + + case MemRegion::ElementRegionKind: + // FIXME: Handle element regions by upper-bounding the parent region's + // string length. + return state; + + default: + // Other regions (mostly non-data) can't have a reliable C string length. + // For now, just ignore the change. + // FIXME: These are rare but not impossible. We should output some kind of + // warning for things like strcpy((char[]){'a', 0}, "b"); + return state; + } +} + +SVal CStringChecker::GetCStringLengthForRegion(CheckerContext &C, + const GRState *&state, + const Expr *Ex, + const MemRegion *MR) { + // If there's a recorded length, go ahead and return it. + const SVal *Recorded = state->get<CStringLength>(MR); + if (Recorded) + return *Recorded; + + // Otherwise, get a new symbol and update the state. + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + ValueManager &ValMgr = C.getValueManager(); + QualType SizeTy = ValMgr.getContext().getSizeType(); + SVal Strlen = ValMgr.getMetadataSymbolVal(getTag(), MR, Ex, SizeTy, Count); + + state = state->set<CStringLength>(MR, Strlen); + return Strlen; +} + +SVal CStringChecker::GetCStringLength(CheckerContext &C, const GRState *&state, + const Expr *Ex, SVal Buf) { + const MemRegion *MR = Buf.getAsRegion(); + if (!MR) { + // If we can't get a region, see if it's something we /know/ isn't a + // C string. In the context of locations, the only time we can issue such + // a warning is for labels. + if (loc::GotoLabel *Label = dyn_cast<loc::GotoLabel>(&Buf)) { + if (ExplodedNode *N = C.GenerateNode(state)) { + if (!BT_NotCString) + BT_NotCString = new BuiltinBug("API", + "Argument is not a null-terminated string."); + + llvm::SmallString<120> buf; + llvm::raw_svector_ostream os(buf); + os << "Argument to byte string function is the address of the label '" + << Label->getLabel()->getID()->getName() + << "', which is not a null-terminated string"; + + // Generate a report for this bug. + EnhancedBugReport *report = new EnhancedBugReport(*BT_NotCString, + os.str(), N); + + report->addRange(Ex->getSourceRange()); + C.EmitReport(report); + } + + return UndefinedVal(); + } + + // If it's not a region and not a label, give up. + return UnknownVal(); + } + + // If we have a region, strip casts from it and see if we can figure out + // its length. For anything we can't figure out, just return UnknownVal. + MR = MR->StripCasts(); + + switch (MR->getKind()) { + case MemRegion::StringRegionKind: { + // Modifying the contents of string regions is undefined [C99 6.4.5p6], + // so we can assume that the byte length is the correct C string length. + ValueManager &ValMgr = C.getValueManager(); + QualType SizeTy = ValMgr.getContext().getSizeType(); + const StringLiteral *Str = cast<StringRegion>(MR)->getStringLiteral(); + return ValMgr.makeIntVal(Str->getByteLength(), SizeTy); + } + case MemRegion::SymbolicRegionKind: + case MemRegion::AllocaRegionKind: + case MemRegion::VarRegionKind: + case MemRegion::FieldRegionKind: + case MemRegion::ObjCIvarRegionKind: + return GetCStringLengthForRegion(C, state, Ex, MR); + case MemRegion::CompoundLiteralRegionKind: + // FIXME: Can we track this? Is it necessary? + return UnknownVal(); + case MemRegion::ElementRegionKind: + // FIXME: How can we handle this? It's not good enough to subtract the + // offset from the base string length; consider "123\x00567" and &a[5]. + return UnknownVal(); + default: + // Other regions (mostly non-data) can't have a reliable C string length. + // In this case, an error is emitted and UndefinedVal is returned. + // The caller should always be prepared to handle this case. + if (ExplodedNode *N = C.GenerateNode(state)) { + if (!BT_NotCString) + BT_NotCString = new BuiltinBug("API", + "Argument is not a null-terminated string."); + + llvm::SmallString<120> buf; + llvm::raw_svector_ostream os(buf); + + os << "Argument to byte string function is "; + + if (SummarizeRegion(os, C.getASTContext(), MR)) + os << ", which is not a null-terminated string"; + else + os << "not a null-terminated string"; + + // Generate a report for this bug. + EnhancedBugReport *report = new EnhancedBugReport(*BT_NotCString, + os.str(), N); + + report->addRange(Ex->getSourceRange()); + C.EmitReport(report); + } + + return UndefinedVal(); + } +} + +const GRState *CStringChecker::InvalidateBuffer(CheckerContext &C, + const GRState *state, + const Expr *E, SVal V) { + Loc *L = dyn_cast<Loc>(&V); + if (!L) + return state; + + // FIXME: This is a simplified version of what's in CFRefCount.cpp -- it makes + // some assumptions about the value that CFRefCount can't. Even so, it should + // probably be refactored. + if (loc::MemRegionVal* MR = dyn_cast<loc::MemRegionVal>(L)) { + const MemRegion *R = MR->getRegion()->StripCasts(); + + // Are we dealing with an ElementRegion? If so, we should be invalidating + // the super-region. + if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { + R = ER->getSuperRegion(); + // FIXME: What about layers of ElementRegions? + } + + // Invalidate this region. + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + return state->InvalidateRegion(R, E, Count, NULL); + } + + // If we have a non-region value by chance, just remove the binding. + // FIXME: is this necessary or correct? This handles the non-Region + // cases. Is it ever valid to store to these? + return state->unbindLoc(*L); +} + +bool CStringChecker::SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx, + const MemRegion *MR) { + const TypedRegion *TR = dyn_cast<TypedRegion>(MR); + if (!TR) + return false; + + switch (TR->getKind()) { + case MemRegion::FunctionTextRegionKind: { + const FunctionDecl *FD = cast<FunctionTextRegion>(TR)->getDecl(); + if (FD) + os << "the address of the function '" << FD << "'"; + else + os << "the address of a function"; + return true; + } + case MemRegion::BlockTextRegionKind: + os << "block text"; + return true; + case MemRegion::BlockDataRegionKind: + os << "a block"; + return true; + case MemRegion::CXXThisRegionKind: + case MemRegion::CXXObjectRegionKind: + os << "a C++ object of type " << TR->getValueType().getAsString(); + return true; + case MemRegion::VarRegionKind: + os << "a variable of type" << TR->getValueType().getAsString(); + return true; + case MemRegion::FieldRegionKind: + os << "a field of type " << TR->getValueType().getAsString(); + return true; + case MemRegion::ObjCIvarRegionKind: + os << "an instance variable of type " << TR->getValueType().getAsString(); + return true; + default: + return false; + } +} + //===----------------------------------------------------------------------===// // Evaluation of individual function calls. //===----------------------------------------------------------------------===// @@ -390,11 +667,20 @@ void CStringChecker::EvalCopyCommon(CheckerContext &C, const GRState *state, // If the size can be nonzero, we have to check the other arguments. if (StNonZeroSize) { state = StNonZeroSize; - state = CheckBufferAccess(C, state, Size, Dest, Source); + state = CheckBufferAccess(C, state, Size, Dest, Source, + /* FirstIsDst = */ true); if (Restricted) state = CheckOverlap(C, state, Size, Dest, Source); - if (state) + + if (state) { + // Invalidate the destination. + // FIXME: Even if we can't perfectly model the copy, we should see if we + // can use LazyCompoundVals to copy the source values into the destination. + // This would probably remove any existing bindings past the end of the + // copied region, but that's still an improvement over blank invalidation. + state = InvalidateBuffer(C, state, Dest, state->getSVal(Dest)); C.addTransition(state); + } } } @@ -481,7 +767,7 @@ void CStringChecker::EvalMemcmp(CheckerContext &C, const CallExpr *CE) { if (state) { // The return value is the comparison result, which we don't know. unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); - SVal CmpV = ValMgr.getConjuredSymbolVal(NULL, CE, CE->getType(), Count); + SVal CmpV = ValMgr.getConjuredSymbolVal(NULL, CE, Count); state = state->BindExpr(CE, CmpV); C.addTransition(state); } @@ -489,8 +775,123 @@ void CStringChecker::EvalMemcmp(CheckerContext &C, const CallExpr *CE) { } } +void CStringChecker::EvalStrlen(CheckerContext &C, const CallExpr *CE) { + // size_t strlen(const char *s); + const GRState *state = C.getState(); + const Expr *Arg = CE->getArg(0); + SVal ArgVal = state->getSVal(Arg); + + // Check that the argument is non-null. + state = CheckNonNull(C, state, Arg, ArgVal); + + if (state) { + SVal StrLen = GetCStringLength(C, state, Arg, ArgVal); + + // If the argument isn't a valid C string, there's no valid state to + // transition to. + if (StrLen.isUndef()) + return; + + // If GetCStringLength couldn't figure out the length, conjure a return + // value, so it can be used in constraints, at least. + if (StrLen.isUnknown()) { + ValueManager &ValMgr = C.getValueManager(); + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + StrLen = ValMgr.getConjuredSymbolVal(NULL, CE, Count); + } + + // Bind the return value. + state = state->BindExpr(CE, StrLen); + C.addTransition(state); + } +} + +void CStringChecker::EvalStrcpy(CheckerContext &C, const CallExpr *CE) { + // char *strcpy(char *restrict dst, const char *restrict src); + EvalStrcpyCommon(C, CE, /* ReturnEnd = */ false); +} + +void CStringChecker::EvalStpcpy(CheckerContext &C, const CallExpr *CE) { + // char *stpcpy(char *restrict dst, const char *restrict src); + EvalStrcpyCommon(C, CE, /* ReturnEnd = */ true); +} + +void CStringChecker::EvalStrcpyCommon(CheckerContext &C, const CallExpr *CE, + bool ReturnEnd) { + const GRState *state = C.getState(); + + // Check that the destination is non-null + const Expr *Dst = CE->getArg(0); + SVal DstVal = state->getSVal(Dst); + + state = CheckNonNull(C, state, Dst, DstVal); + if (!state) + return; + + // Check that the source is non-null. + const Expr *Src = CE->getArg(1); + SVal SrcVal = state->getSVal(Src); + + state = CheckNonNull(C, state, Src, SrcVal); + if (!state) + return; + + // Get the string length of the source. + SVal StrLen = GetCStringLength(C, state, Src, SrcVal); + + // If the source isn't a valid C string, give up. + if (StrLen.isUndef()) + return; + + SVal Result = (ReturnEnd ? UnknownVal() : DstVal); + + // If the destination is a MemRegion, try to check for a buffer overflow and + // record the new string length. + if (loc::MemRegionVal *DstRegVal = dyn_cast<loc::MemRegionVal>(&DstVal)) { + // If the length is known, we can check for an overflow. + if (NonLoc *KnownStrLen = dyn_cast<NonLoc>(&StrLen)) { + SValuator &SV = C.getSValuator(); + + SVal LastElement = SV.EvalBinOpLN(state, BO_Add, + *DstRegVal, *KnownStrLen, + Dst->getType()); + + state = CheckLocation(C, state, Dst, LastElement, /* IsDst = */ true); + if (!state) + return; + + // If this is a stpcpy-style copy, the last element is the return value. + if (ReturnEnd) + Result = LastElement; + } + + // Invalidate the destination. This must happen before we set the C string + // length because invalidation will clear the length. + // FIXME: Even if we can't perfectly model the copy, we should see if we + // can use LazyCompoundVals to copy the source values into the destination. + // This would probably remove any existing bindings past the end of the + // string, but that's still an improvement over blank invalidation. + state = InvalidateBuffer(C, state, Dst, *DstRegVal); + + // Set the C string length of the destination. + state = SetCStringLength(state, DstRegVal->getRegion(), StrLen); + } + + // If this is a stpcpy-style copy, but we were unable to check for a buffer + // overflow, we still need a result. Conjure a return value. + if (ReturnEnd && Result.isUnknown()) { + ValueManager &ValMgr = C.getValueManager(); + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + StrLen = ValMgr.getConjuredSymbolVal(NULL, CE, Count); + } + + // Set the return value. + state = state->BindExpr(CE, Result); + C.addTransition(state); +} + //===----------------------------------------------------------------------===// -// The driver method. +// The driver method, and other Checker callbacks. //===----------------------------------------------------------------------===// bool CStringChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { @@ -512,6 +913,9 @@ bool CStringChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { .Cases("memcpy", "__memcpy_chk", &CStringChecker::EvalMemcpy) .Cases("memcmp", "bcmp", &CStringChecker::EvalMemcmp) .Cases("memmove", "__memmove_chk", &CStringChecker::EvalMemmove) + .Cases("strcpy", "__strcpy_chk", &CStringChecker::EvalStrcpy) + .Cases("stpcpy", "__stpcpy_chk", &CStringChecker::EvalStpcpy) + .Case("strlen", &CStringChecker::EvalStrlen) .Case("bcopy", &CStringChecker::EvalBcopy) .Default(NULL); @@ -523,3 +927,129 @@ bool CStringChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { (this->*EvalFunction)(C, CE); return true; } + +void CStringChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { + // Record string length for char a[] = "abc"; + const GRState *state = C.getState(); + + for (DeclStmt::const_decl_iterator I = DS->decl_begin(), E = DS->decl_end(); + I != E; ++I) { + const VarDecl *D = dyn_cast<VarDecl>(*I); + if (!D) + continue; + + // FIXME: Handle array fields of structs. + if (!D->getType()->isArrayType()) + continue; + + const Expr *Init = D->getInit(); + if (!Init) + continue; + if (!isa<StringLiteral>(Init)) + continue; + + Loc VarLoc = state->getLValue(D, C.getPredecessor()->getLocationContext()); + const MemRegion *MR = VarLoc.getAsRegion(); + if (!MR) + continue; + + SVal StrVal = state->getSVal(Init); + assert(StrVal.isValid() && "Initializer string is unknown or undefined"); + DefinedOrUnknownSVal StrLen + = cast<DefinedOrUnknownSVal>(GetCStringLength(C, state, Init, StrVal)); + + state = state->set<CStringLength>(MR, StrLen); + } + + C.addTransition(state); +} + +bool CStringChecker::WantsRegionChangeUpdate(const GRState *state) { + CStringLength::EntryMap Entries = state->get<CStringLength>(); + return !Entries.isEmpty(); +} + +const GRState *CStringChecker::EvalRegionChanges(const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End, + bool *) { + CStringLength::EntryMap Entries = state->get<CStringLength>(); + if (Entries.isEmpty()) + return state; + + llvm::SmallPtrSet<const MemRegion *, 8> Invalidated; + llvm::SmallPtrSet<const MemRegion *, 32> SuperRegions; + + // First build sets for the changed regions and their super-regions. + for ( ; Begin != End; ++Begin) { + const MemRegion *MR = *Begin; + Invalidated.insert(MR); + + SuperRegions.insert(MR); + while (const SubRegion *SR = dyn_cast<SubRegion>(MR)) { + MR = SR->getSuperRegion(); + SuperRegions.insert(MR); + } + } + + CStringLength::EntryMap::Factory &F = state->get_context<CStringLength>(); + + // Then loop over the entries in the current state. + for (CStringLength::EntryMap::iterator I = Entries.begin(), + E = Entries.end(); I != E; ++I) { + const MemRegion *MR = I.getKey(); + + // Is this entry for a super-region of a changed region? + if (SuperRegions.count(MR)) { + Entries = F.Remove(Entries, MR); + continue; + } + + // Is this entry for a sub-region of a changed region? + const MemRegion *Super = MR; + while (const SubRegion *SR = dyn_cast<SubRegion>(Super)) { + Super = SR->getSuperRegion(); + if (Invalidated.count(Super)) { + Entries = F.Remove(Entries, MR); + break; + } + } + } + + return state->set<CStringLength>(Entries); +} + +void CStringChecker::MarkLiveSymbols(const GRState *state, SymbolReaper &SR) { + // Mark all symbols in our string length map as valid. + CStringLength::EntryMap Entries = state->get<CStringLength>(); + + for (CStringLength::EntryMap::iterator I = Entries.begin(), E = Entries.end(); + I != E; ++I) { + SVal Len = I.getData(); + if (SymbolRef Sym = Len.getAsSymbol()) + SR.markInUse(Sym); + } +} + +void CStringChecker::EvalDeadSymbols(CheckerContext &C, SymbolReaper &SR) { + if (!SR.hasDeadSymbols()) + return; + + const GRState *state = C.getState(); + CStringLength::EntryMap Entries = state->get<CStringLength>(); + if (Entries.isEmpty()) + return; + + CStringLength::EntryMap::Factory &F = state->get_context<CStringLength>(); + for (CStringLength::EntryMap::iterator I = Entries.begin(), E = Entries.end(); + I != E; ++I) { + SVal Len = I.getData(); + if (SymbolRef Sym = Len.getAsSymbol()) { + if (SR.isDead(Sym)) + Entries = F.Remove(Entries, I.getKey()); + } + } + + state = state->set<CStringLength>(Entries); + C.GenerateNode(state); +} diff --git a/lib/Checker/CallAndMessageChecker.cpp b/lib/Checker/CallAndMessageChecker.cpp index c619d75..3c9ddce 100644 --- a/lib/Checker/CallAndMessageChecker.cpp +++ b/lib/Checker/CallAndMessageChecker.cpp @@ -114,7 +114,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, : C(c), StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {} bool Find(const TypedRegion *R) { - QualType T = R->getValueType(C); + QualType T = R->getValueType(); if (const RecordType *RT = T->getAsStructureType()) { const RecordDecl *RD = RT->getDecl()->getDefinition(); assert(RD && "Referred record has no definition"); diff --git a/lib/Checker/CallInliner.cpp b/lib/Checker/CallInliner.cpp deleted file mode 100644 index c47e06c..0000000 --- a/lib/Checker/CallInliner.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//===--- CallInliner.cpp - Transfer function that inlines callee ----------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the callee inlining transfer function. -// -//===----------------------------------------------------------------------===// - -#include "clang/Checker/PathSensitive/CheckerVisitor.h" -#include "clang/Checker/PathSensitive/GRState.h" -#include "clang/Checker/Checkers/LocalCheckers.h" - -using namespace clang; - -namespace { -class CallInliner : public Checker { -public: - static void *getTag() { - static int x; - return &x; - } - - virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); -}; -} - -void clang::RegisterCallInliner(GRExprEngine &Eng) { - Eng.registerCheck(new CallInliner()); -} - -bool CallInliner::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { - const GRState *state = C.getState(); - const Expr *Callee = CE->getCallee(); - SVal L = state->getSVal(Callee); - - const FunctionDecl *FD = L.getAsFunctionDecl(); - if (!FD) - return false; - - if (!FD->hasBody(FD)) - return false; - - // Now we have the definition of the callee, create a CallEnter node. - CallEnter Loc(CE, FD, C.getPredecessor()->getLocationContext()); - C.addTransition(state, Loc); - - return true; -} - diff --git a/lib/Checker/CastSizeChecker.cpp b/lib/Checker/CastSizeChecker.cpp index a502c10..6676fe5 100644 --- a/lib/Checker/CastSizeChecker.cpp +++ b/lib/Checker/CastSizeChecker.cpp @@ -44,6 +44,10 @@ void CastSizeChecker::PreVisitCastExpr(CheckerContext &C, const CastExpr *CE) { QualType ToPointeeTy = ToPTy->getPointeeType(); + // Only perform the check if 'ToPointeeTy' is a complete type. + if (ToPointeeTy->isIncompleteType()) + return; + const GRState *state = C.getState(); const MemRegion *R = state->getSVal(E).getAsRegion(); if (R == 0) diff --git a/lib/Checker/CheckDeadStores.cpp b/lib/Checker/CheckDeadStores.cpp index d6ea187..3896100 100644 --- a/lib/Checker/CheckDeadStores.cpp +++ b/lib/Checker/CheckDeadStores.cpp @@ -217,7 +217,7 @@ public: // If x is EVER assigned a new value later, don't issue // a warning. This is because such initialization can be // due to defensive programming. - if (E->isConstantInitializer(Ctx)) + if (E->isConstantInitializer(Ctx, false)) return; if (DeclRefExpr *DRE=dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) @@ -268,7 +268,7 @@ public: // Check for '&'. Any VarDecl whose value has its address-taken we // treat as escaped. Expr* E = U->getSubExpr()->IgnoreParenCasts(); - if (U->getOpcode() == UnaryOperator::AddrOf) + if (U->getOpcode() == UO_AddrOf) if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(E)) if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl())) { Escaped.insert(VD); diff --git a/lib/Checker/CheckSecuritySyntaxOnly.cpp b/lib/Checker/CheckSecuritySyntaxOnly.cpp index af85c2f..9a2ac45 100644 --- a/lib/Checker/CheckSecuritySyntaxOnly.cpp +++ b/lib/Checker/CheckSecuritySyntaxOnly.cpp @@ -41,8 +41,8 @@ class WalkAST : public StmtVisitor<WalkAST> { public: WalkAST(BugReporter &br) : BR(br), - II_gets(0), II_getpw(0), II_mktemp(0), - II_rand(), II_random(0), II_setid(), + II_gets(0), II_getpw(0), II_mktemp(0), + II_rand(), II_random(0), II_setid(), CheckRand(isArc4RandomAvailable(BR.getContext())) {} // Statement visitor methods. @@ -131,7 +131,7 @@ GetIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) { if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) { if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() || - B->getOpcode() == BinaryOperator::Comma)) + B->getOpcode() == BO_Comma)) return NULL; if (const DeclRefExpr *lhs = GetIncrementedVar(B->getLHS(), x, y)) @@ -217,7 +217,7 @@ void WalkAST::CheckLoopConditionForFloat(const ForStmt *FS) { llvm::SmallString<256> sbuf; llvm::raw_svector_ostream os(sbuf); - os << "Variable '" << drCond->getDecl()->getNameAsCString() + os << "Variable '" << drCond->getDecl()->getName() << "' with floating point type '" << drCond->getType().getAsString() << "' should not be used as a loop counter"; @@ -332,10 +332,10 @@ void WalkAST::CheckCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { // Issue a waring. SourceRange R = CE->getCallee()->getSourceRange(); BR.EmitBasicReport("Potential insecure temporary file in call 'mktemp'", - "Security", - "Call to function 'mktemp' is insecure as it always " - "creates or uses insecure temporary file. Use 'mkstemp' instead", - CE->getLocStart(), &R, 1); + "Security", + "Call to function 'mktemp' is insecure as it always " + "creates or uses insecure temporary file. Use 'mkstemp' instead", + CE->getLocStart(), &R, 1); } //===----------------------------------------------------------------------===// diff --git a/lib/Checker/CheckerHelpers.cpp b/lib/Checker/CheckerHelpers.cpp new file mode 100644 index 0000000..ece6943 --- /dev/null +++ b/lib/Checker/CheckerHelpers.cpp @@ -0,0 +1,80 @@ +//===---- CheckerHelpers.cpp - Helper functions for checkers ----*- 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 several static functions for use in checkers. +// +//===----------------------------------------------------------------------===// + +#include "clang/Checker/PathSensitive/CheckerHelpers.h" +#include "clang/AST/Expr.h" + +// Recursively find any substatements containing macros +bool clang::containsMacro(const Stmt *S) { + if (S->getLocStart().isMacroID()) + return true; + + if (S->getLocEnd().isMacroID()) + return true; + + for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); + ++I) + if (const Stmt *child = *I) + if (containsMacro(child)) + return true; + + return false; +} + +// Recursively find any substatements containing enum constants +bool clang::containsEnum(const Stmt *S) { + const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S); + + if (DR && isa<EnumConstantDecl>(DR->getDecl())) + return true; + + for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); + ++I) + if (const Stmt *child = *I) + if (containsEnum(child)) + return true; + + return false; +} + +// Recursively find any substatements containing static vars +bool clang::containsStaticLocal(const Stmt *S) { + const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S); + + if (DR) + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) + if (VD->isStaticLocal()) + return true; + + for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); + ++I) + if (const Stmt *child = *I) + if (containsStaticLocal(child)) + return true; + + return false; +} + +// Recursively find any substatements containing __builtin_offsetof +bool clang::containsBuiltinOffsetOf(const Stmt *S) { + if (isa<OffsetOfExpr>(S)) + return true; + + for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); + ++I) + if (const Stmt *child = *I) + if (containsBuiltinOffsetOf(child)) + return true; + + return false; +} diff --git a/lib/Checker/CocoaConventions.cpp b/lib/Checker/CocoaConventions.cpp index 3ba887c..b446a04 100644 --- a/lib/Checker/CocoaConventions.cpp +++ b/lib/Checker/CocoaConventions.cpp @@ -173,9 +173,10 @@ bool cocoa::isCocoaObjectRef(QualType Ty) { if (!PT) return true; - // We assume that id<..>, id, and "Class" all represent tracked objects. + // We assume that id<..>, id, Class, and Class<..> all represent tracked + // objects. if (PT->isObjCIdType() || PT->isObjCQualifiedIdType() || - PT->isObjCClassType()) + PT->isObjCClassType() || PT->isObjCQualifiedClassType()) return true; // Does the interface subclass NSObject? diff --git a/lib/Checker/DivZeroChecker.cpp b/lib/Checker/DivZeroChecker.cpp index e09a871..32e2a17 100644 --- a/lib/Checker/DivZeroChecker.cpp +++ b/lib/Checker/DivZeroChecker.cpp @@ -40,10 +40,10 @@ void *DivZeroChecker::getTag() { 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) + if (Op != BO_Div && + Op != BO_Rem && + Op != BO_DivAssign && + Op != BO_RemAssign) return; if (!B->getRHS()->getType()->isIntegerType() || diff --git a/lib/Checker/Environment.cpp b/lib/Checker/Environment.cpp index 48152ce..02291f4 100644 --- a/lib/Checker/Environment.cpp +++ b/lib/Checker/Environment.cpp @@ -80,7 +80,7 @@ SVal Environment::GetSVal(const Stmt *E, ValueManager& ValMgr) const { return LookupExpr(E); } -Environment EnvironmentManager::BindExpr(Environment Env, const Stmt *S, +Environment EnvironmentManager::bindExpr(Environment Env, const Stmt *S, SVal V, bool Invalidate) { assert(S); @@ -94,6 +94,16 @@ Environment EnvironmentManager::BindExpr(Environment Env, const Stmt *S, return Environment(F.Add(Env.ExprBindings, S, V)); } +static inline const Stmt *MakeLocation(const Stmt *S) { + return (const Stmt*) (((uintptr_t) S) | 0x1); +} + +Environment EnvironmentManager::bindExprAndLocation(Environment Env, + const Stmt *S, + SVal location, SVal V) { + return Environment(F.Add(F.Add(Env.ExprBindings, MakeLocation(S), V), S, V)); +} + namespace { class MarkLiveCallback : public SymbolVisitor { SymbolReaper &SymReaper; @@ -115,6 +125,12 @@ static bool isBlockExprInCallers(const Stmt *E, const LocationContext *LC) { return false; } +// In addition to mapping from Stmt * - > SVals in the Environment, we also +// maintain a mapping from Stmt * -> SVals (locations) that were used during +// a load and store. +static inline bool IsLocation(const Stmt *S) { + return (bool) (((uintptr_t) S) & 0x1); +} // RemoveDeadBindings: // - Remove subexpression bindings. @@ -123,7 +139,6 @@ static bool isBlockExprInCallers(const Stmt *E, const LocationContext *LC) { // - Mark their reachable symbols live in SymbolReaper, // see ScanReachableSymbols. // - Mark the region in DRoots if the binding is a loc::MemRegionVal. - Environment EnvironmentManager::RemoveDeadBindings(Environment Env, SymbolReaper &SymReaper, @@ -136,12 +151,25 @@ EnvironmentManager::RemoveDeadBindings(Environment Env, // individually removing all the subexpression bindings (which will greatly // outnumber block-level expression bindings). Environment NewEnv = getInitialEnvironment(); + + llvm::SmallVector<std::pair<const Stmt*, SVal>, 10> deferredLocations; // Iterate over the block-expr bindings. for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { const Stmt *BlkExpr = I.getKey(); + + // For recorded locations (used when evaluating loads and stores), we + // consider them live only when their associated normal expression is + // also live. + // NOTE: This assumes that loads/stores that evaluated to UnknownVal + // still have an entry in the map. + if (IsLocation(BlkExpr)) { + deferredLocations.push_back(std::make_pair(BlkExpr, I.getData())); + continue; + } + const SVal &X = I.getData(); // Block-level expressions in callers are assumed always live. @@ -186,6 +214,15 @@ EnvironmentManager::RemoveDeadBindings(Environment Env, if (X.isUndef() && cast<UndefinedVal>(X).getData()) NewEnv.ExprBindings = F.Add(NewEnv.ExprBindings, BlkExpr, X); } + + // Go through he deferred locations and add them to the new environment if + // the correspond Stmt* is in the map as well. + for (llvm::SmallVectorImpl<std::pair<const Stmt*, SVal> >::iterator + I = deferredLocations.begin(), E = deferredLocations.end(); I != E; ++I) { + const Stmt *S = (Stmt*) (((uintptr_t) I->first) & (uintptr_t) ~0x1); + if (NewEnv.ExprBindings.lookup(S)) + NewEnv.ExprBindings = F.Add(NewEnv.ExprBindings, I->first, I->second); + } return NewEnv; } diff --git a/lib/Checker/FixedAddressChecker.cpp b/lib/Checker/FixedAddressChecker.cpp index 4fce45b..29a3c3a 100644 --- a/lib/Checker/FixedAddressChecker.cpp +++ b/lib/Checker/FixedAddressChecker.cpp @@ -40,7 +40,7 @@ void FixedAddressChecker::PreVisitBinaryOperator(CheckerContext &C, // Using a fixed address is not portable because that address will probably // not be valid in all environments or platforms. - if (B->getOpcode() != BinaryOperator::Assign) + if (B->getOpcode() != BO_Assign) return; QualType T = B->getType(); diff --git a/lib/Checker/FlatStore.cpp b/lib/Checker/FlatStore.cpp index 64575b3c9..21fa422 100644 --- a/lib/Checker/FlatStore.cpp +++ b/lib/Checker/FlatStore.cpp @@ -44,11 +44,10 @@ public: } SVal ArrayToPointer(Loc Array); - const GRState *RemoveDeadBindings(GRState &state, - const StackFrameContext *LCtx, + Store RemoveDeadBindings(Store store, const StackFrameContext *LCtx, SymbolReaper& SymReaper, llvm::SmallVectorImpl<const MemRegion*>& RegionRoots){ - return StateMgr.getPersistentState(state); + return store; } Store BindDecl(Store store, const VarRegion *VR, SVal initVal); @@ -57,13 +56,10 @@ public: typedef llvm::DenseSet<SymbolRef> InvalidatedSymbols; - Store InvalidateRegion(Store store, const MemRegion *R, const Expr *E, - unsigned Count, InvalidatedSymbols *IS); - Store InvalidateRegions(Store store, const MemRegion * const *I, const MemRegion * const *E, const Expr *Ex, unsigned Count, InvalidatedSymbols *IS, - bool invalidateGlobals); + bool invalidateGlobals, InvalidatedRegions *Regions); void print(Store store, llvm::raw_ostream& Out, const char* nl, const char *sep); @@ -74,7 +70,14 @@ private: return RegionBindings(static_cast<const RegionBindings::TreeTy*>(store)); } - Interval RegionToInterval(const MemRegion *R); + class RegionInterval { + public: + const MemRegion *R; + Interval I; + RegionInterval(const MemRegion *r, int64_t s, int64_t e) : R(r), I(s, e){} + }; + + RegionInterval RegionToInterval(const MemRegion *R); SVal RetrieveRegionWithNoBinding(const MemRegion *R, QualType T); }; @@ -86,11 +89,15 @@ StoreManager *clang::CreateFlatStoreManager(GRStateManager &StMgr) { SVal FlatStoreManager::Retrieve(Store store, Loc L, QualType T) { const MemRegion *R = cast<loc::MemRegionVal>(L).getRegion(); - Interval I = RegionToInterval(R); + RegionInterval RI = RegionToInterval(R); + // FIXME: FlatStore should handle regions with unknown intervals. + if (!RI.R) + return UnknownVal(); + RegionBindings B = getRegionBindings(store); - const BindingVal *BV = B.lookup(R); + const BindingVal *BV = B.lookup(RI.R); if (BV) { - const SVal *V = BVFactory.Lookup(*BV, I); + const SVal *V = BVFactory.Lookup(*BV, RI.I); if (V) return *V; else @@ -116,9 +123,12 @@ Store FlatStoreManager::Bind(Store store, Loc L, SVal val) { if (V) BV = *V; - Interval I = RegionToInterval(R); - BV = BVFactory.Add(BV, I, val); - B = RBFactory.Add(B, R, BV); + RegionInterval RI = RegionToInterval(R); + // FIXME: FlatStore should handle regions with unknown intervals. + if (!RI.R) + return B.getRoot(); + BV = BVFactory.Add(BV, RI.I, val); + B = RBFactory.Add(B, RI.R, BV); return B.getRoot(); } @@ -139,7 +149,7 @@ SVal FlatStoreManager::ArrayToPointer(Loc Array) { Store FlatStoreManager::BindDecl(Store store, const VarRegion *VR, SVal initVal) { - return store; + return Bind(store, ValMgr.makeLoc(VR), initVal); } Store FlatStoreManager::BindDeclWithNoInit(Store store, const VarRegion *VR) { @@ -147,18 +157,12 @@ Store FlatStoreManager::BindDeclWithNoInit(Store store, const VarRegion *VR) { } Store FlatStoreManager::InvalidateRegions(Store store, - const MemRegion * const *I, - const MemRegion * const *E, - const Expr *Ex, unsigned Count, - InvalidatedSymbols *IS, - bool invalidateGlobals) { - assert(false && "Not implemented"); - return store; -} - -Store FlatStoreManager::InvalidateRegion(Store store, const MemRegion *R, - const Expr *E, unsigned Count, - InvalidatedSymbols *IS) { + const MemRegion * const *I, + const MemRegion * const *E, + const Expr *Ex, unsigned Count, + InvalidatedSymbols *IS, + bool invalidateGlobals, + InvalidatedRegions *Regions) { assert(false && "Not implemented"); return store; } @@ -170,15 +174,29 @@ void FlatStoreManager::print(Store store, llvm::raw_ostream& Out, void FlatStoreManager::iterBindings(Store store, BindingsHandler& f) { } -Interval FlatStoreManager::RegionToInterval(const MemRegion *R) { +FlatStoreManager::RegionInterval +FlatStoreManager::RegionToInterval(const MemRegion *R) { switch (R->getKind()) { case MemRegion::VarRegionKind: { - QualType T = cast<VarRegion>(R)->getValueType(Ctx); - uint64_t Size = Ctx.getTypeSize(T); - return Interval(0, Size-1); + QualType T = cast<VarRegion>(R)->getValueType(); + int64_t Size = Ctx.getTypeSize(T); + return RegionInterval(R, 0, Size-1); } + + case MemRegion::ElementRegionKind: + case MemRegion::FieldRegionKind: { + RegionOffset Offset = R->getAsOffset(); + // We cannot compute offset for all regions, for example, elements + // with symbolic offsets. + if (!Offset.getRegion()) + return RegionInterval(0, 0, 0); + int64_t Start = Offset.getOffset(); + int64_t Size = Ctx.getTypeSize(cast<TypedRegion>(R)->getValueType()); + return RegionInterval(Offset.getRegion(), Start, Start+Size); + } + default: llvm_unreachable("Region kind unhandled."); - return Interval(0, 0); + return RegionInterval(0, 0, 0); } } diff --git a/lib/Checker/GRCXXExprEngine.cpp b/lib/Checker/GRCXXExprEngine.cpp index 18e112c..a49989b 100644 --- a/lib/Checker/GRCXXExprEngine.cpp +++ b/lib/Checker/GRCXXExprEngine.cpp @@ -17,7 +17,7 @@ using namespace clang; -void GRExprEngine::EvalArguments(ExprIterator AI, ExprIterator AE, +void GRExprEngine::EvalArguments(ConstExprIterator AI, ConstExprIterator AE, const FunctionProtoType *FnType, ExplodedNode *Pred, ExplodedNodeSet &Dst) { llvm::SmallVector<CallExprWLItem, 20> WorkList; @@ -55,7 +55,7 @@ const CXXThisRegion *GRExprEngine::getCXXThisRegion(const CXXMethodDecl *D, return ValMgr.getRegionManager().getCXXThisRegion(PT, SFC); } -void GRExprEngine::CreateCXXTemporaryObject(Expr *Ex, ExplodedNode *Pred, +void GRExprEngine::CreateCXXTemporaryObject(const Expr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst) { ExplodedNodeSet Tmp; Visit(Ex, Pred, Tmp); @@ -94,9 +94,7 @@ void GRExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, SVal Dest, // Evaluate other arguments. ExplodedNodeSet ArgsEvaluated; const FunctionProtoType *FnType = CD->getType()->getAs<FunctionProtoType>(); - EvalArguments(const_cast<CXXConstructExpr*>(E)->arg_begin(), - const_cast<CXXConstructExpr*>(E)->arg_end(), - FnType, Pred, ArgsEvaluated); + EvalArguments(E->arg_begin(), E->arg_end(), FnType, Pred, ArgsEvaluated); // The callee stack frame context used to create the 'this' parameter region. const StackFrameContext *SFC = AMgr.getStackFrame(CD, Pred->getLocationContext(), @@ -104,11 +102,12 @@ void GRExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, SVal Dest, const CXXThisRegion *ThisR = getCXXThisRegion(E->getConstructor(), SFC); - CallEnter Loc(E, CD, Pred->getLocationContext()); + CallEnter Loc(E, SFC->getAnalysisContext(), Pred->getLocationContext()); for (ExplodedNodeSet::iterator NI = ArgsEvaluated.begin(), NE = ArgsEvaluated.end(); NI != NE; ++NI) { const GRState *state = GetState(*NI); - // Setup 'this' region. + // Setup 'this' region, so that the ctor is evaluated on the object pointed + // by 'Dest'. state = state->bindLoc(loc::MemRegionVal(ThisR), Dest); ExplodedNode *N = Builder->generateNode(Loc, state, Pred); if (N) @@ -126,9 +125,7 @@ void GRExprEngine::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE, // Evaluate explicit arguments with a worklist. ExplodedNodeSet ArgsEvaluated; - EvalArguments(const_cast<CXXMemberCallExpr*>(MCE)->arg_begin(), - const_cast<CXXMemberCallExpr*>(MCE)->arg_end(), - FnType, Pred, ArgsEvaluated); + EvalArguments(MCE->arg_begin(), MCE->arg_end(), FnType, Pred, ArgsEvaluated); // Evaluate the implicit object argument. ExplodedNodeSet AllArgsEvaluated; @@ -157,7 +154,7 @@ void GRExprEngine::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE, Builder->getBlock(), Builder->getIndex()); const CXXThisRegion *ThisR = getCXXThisRegion(MD, SFC); - CallEnter Loc(MCE, MD, Pred->getLocationContext()); + CallEnter Loc(MCE, SFC->getAnalysisContext(), Pred->getLocationContext()); for (ExplodedNodeSet::iterator I = AllArgsEvaluated.begin(), E = AllArgsEvaluated.end(); I != E; ++I) { // Set up 'this' region. @@ -169,7 +166,7 @@ void GRExprEngine::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE, } } -void GRExprEngine::VisitCXXNewExpr(CXXNewExpr *CNE, ExplodedNode *Pred, +void GRExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { if (CNE->isArray()) { // FIXME: allocating an array has not been handled. @@ -177,7 +174,7 @@ void GRExprEngine::VisitCXXNewExpr(CXXNewExpr *CNE, ExplodedNode *Pred, } unsigned Count = Builder->getCurrentBlockCount(); - DefinedOrUnknownSVal SymVal = getValueManager().getConjuredSymbolVal(NULL,CNE, + DefinedOrUnknownSVal SymVal = getValueManager().getConjuredSymbolVal(NULL,CNE, CNE->getType(), Count); const MemRegion *NewReg = cast<loc::MemRegionVal>(SymVal).getRegion(); @@ -201,10 +198,7 @@ void GRExprEngine::VisitCXXNewExpr(CXXNewExpr *CNE, ExplodedNode *Pred, const GRState *state = GetState(*I); if (ObjTy->isRecordType()) { - Store store = state->getStore(); - StoreManager::InvalidatedSymbols IS; - store = getStoreManager().InvalidateRegion(store, EleReg, CNE, Count, &IS); - state = state->makeWithStore(store); + state = state->InvalidateRegion(EleReg, CNE, Count); } else { if (CNE->hasInitializer()) { SVal V = state->getSVal(*CNE->constructor_arg_begin()); @@ -220,8 +214,8 @@ void GRExprEngine::VisitCXXNewExpr(CXXNewExpr *CNE, ExplodedNode *Pred, } } -void GRExprEngine::VisitCXXDeleteExpr(CXXDeleteExpr *CDE, ExplodedNode *Pred, - ExplodedNodeSet &Dst) { +void GRExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, + ExplodedNode *Pred,ExplodedNodeSet &Dst) { // Should do more checking. ExplodedNodeSet ArgEvaluated; Visit(CDE->getArgument(), Pred, ArgEvaluated); @@ -232,7 +226,7 @@ void GRExprEngine::VisitCXXDeleteExpr(CXXDeleteExpr *CDE, ExplodedNode *Pred, } } -void GRExprEngine::VisitCXXThisExpr(CXXThisExpr *TE, ExplodedNode *Pred, +void GRExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { // Get the this object region from StoreManager. const MemRegion *R = diff --git a/lib/Checker/GRCoreEngine.cpp b/lib/Checker/GRCoreEngine.cpp index a816186..5125f36 100644 --- a/lib/Checker/GRCoreEngine.cpp +++ b/lib/Checker/GRCoreEngine.cpp @@ -12,8 +12,10 @@ // //===----------------------------------------------------------------------===// +#include "clang/Checker/PathSensitive/AnalysisManager.h" #include "clang/Checker/PathSensitive/GRCoreEngine.h" #include "clang/Checker/PathSensitive/GRExprEngine.h" +#include "clang/Index/TranslationUnit.h" #include "clang/AST/Expr.h" #include "llvm/Support/Casting.h" #include "llvm/ADT/DenseMap.h" @@ -24,6 +26,12 @@ using llvm::cast; using llvm::isa; using namespace clang; +// This should be removed in the future. +namespace clang { +GRTransferFuncs* MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled, + const LangOptions& lopts); +} + //===----------------------------------------------------------------------===// // Worklist classes for exploration of reachable states. //===----------------------------------------------------------------------===// @@ -118,47 +126,15 @@ GRWorkList* GRWorkList::MakeBFSBlockDFSContents() { //===----------------------------------------------------------------------===// // Core analysis engine. //===----------------------------------------------------------------------===// -void GRCoreEngine::ProcessEndPath(GREndPathNodeBuilder& Builder) { - SubEngine.ProcessEndPath(Builder); -} - -void GRCoreEngine::ProcessStmt(CFGElement E, GRStmtNodeBuilder& Builder) { - SubEngine.ProcessStmt(E, Builder); -} - -bool GRCoreEngine::ProcessBlockEntrance(CFGBlock* Blk, const ExplodedNode *Pred, - GRBlockCounter BC) { - return SubEngine.ProcessBlockEntrance(Blk, Pred, BC); -} - -void GRCoreEngine::ProcessBranch(Stmt* Condition, Stmt* Terminator, - GRBranchNodeBuilder& Builder) { - SubEngine.ProcessBranch(Condition, Terminator, Builder); -} - -void GRCoreEngine::ProcessIndirectGoto(GRIndirectGotoNodeBuilder& Builder) { - SubEngine.ProcessIndirectGoto(Builder); -} - -void GRCoreEngine::ProcessSwitch(GRSwitchNodeBuilder& Builder) { - SubEngine.ProcessSwitch(Builder); -} - -void GRCoreEngine::ProcessCallEnter(GRCallEnterNodeBuilder &Builder) { - SubEngine.ProcessCallEnter(Builder); -} - -void GRCoreEngine::ProcessCallExit(GRCallExitNodeBuilder &Builder) { - SubEngine.ProcessCallExit(Builder); -} /// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. -bool GRCoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps) { +bool GRCoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, + const GRState *InitState) { if (G->num_roots() == 0) { // Initialize the analysis by constructing // the root if none exists. - CFGBlock* Entry = &(L->getCFG()->getEntry()); + const CFGBlock* Entry = &(L->getCFG()->getEntry()); assert (Entry->empty() && "Entry block must be empty."); @@ -167,7 +143,7 @@ bool GRCoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps) { "Entry block must have 1 successor."); // Get the solitary successor. - CFGBlock* Succ = *(Entry->succ_begin()); + const CFGBlock* Succ = *(Entry->succ_begin()); // Construct an edge representing the // starting location in the function. @@ -176,8 +152,11 @@ bool GRCoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps) { // Set the current block counter to being empty. WList->setBlockCounter(BCounterFactory.GetEmptyCounter()); - // Generate the root. - GenerateNode(StartLoc, getInitialState(L), 0); + if (!InitState) + // Generate the root. + GenerateNode(StartLoc, getInitialState(L), 0); + else + GenerateNode(StartLoc, InitState, 0); } while (Steps && WList->hasWork()) { @@ -221,14 +200,25 @@ bool GRCoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps) { } } - SubEngine.ProcessEndWorklist(WList->hasWork() || BlockAborted); + SubEngine.ProcessEndWorklist(hasWorkRemaining()); return WList->hasWork(); } +void GRCoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, + unsigned Steps, + const GRState *InitState, + ExplodedNodeSet &Dst) { + ExecuteWorkList(L, Steps, InitState); + for (llvm::SmallVectorImpl<ExplodedNode*>::iterator I = G->EndNodes.begin(), + E = G->EndNodes.end(); I != E; ++I) { + Dst.Add(*I); + } +} + void GRCoreEngine::HandleCallEnter(const CallEnter &L, const CFGBlock *Block, unsigned Index, ExplodedNode *Pred) { - GRCallEnterNodeBuilder Builder(*this, Pred, L.getCallExpr(), L.getCallee(), - Block, Index); + GRCallEnterNodeBuilder Builder(*this, Pred, L.getCallExpr(), + L.getCalleeContext(), Block, Index); ProcessCallEnter(Builder); } @@ -239,7 +229,7 @@ void GRCoreEngine::HandleCallExit(const CallExit &L, ExplodedNode *Pred) { void GRCoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) { - CFGBlock* Blk = L.getDst(); + const CFGBlock* Blk = L.getDst(); // Check if we are entering the EXIT block. if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { @@ -260,8 +250,9 @@ void GRCoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) { if (ProcessBlockEntrance(Blk, Pred, WList->getBlockCounter())) GenerateNode(BlockEntrance(Blk, Pred->getLocationContext()), Pred->State, Pred); - else - BlockAborted = true; + else { + blocksAborted.push_back(std::make_pair(L, Pred)); + } } void GRCoreEngine::HandleBlockEntrance(const BlockEntrance& L, @@ -284,9 +275,9 @@ void GRCoreEngine::HandleBlockEntrance(const BlockEntrance& L, HandleBlockExit(L.getBlock(), Pred); } -void GRCoreEngine::HandleBlockExit(CFGBlock * B, ExplodedNode* Pred) { +void GRCoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode* Pred) { - if (Stmt* Term = B->getTerminator()) { + if (const Stmt* Term = B->getTerminator()) { switch (Term->getStmtClass()) { default: assert(false && "Analysis for this terminator not implemented."); @@ -372,8 +363,8 @@ void GRCoreEngine::HandleBlockExit(CFGBlock * B, ExplodedNode* Pred) { Pred->State, Pred); } -void GRCoreEngine::HandleBranch(Stmt* Cond, Stmt* Term, CFGBlock * B, - ExplodedNode* Pred) { +void GRCoreEngine::HandleBranch(const Stmt* Cond, const Stmt* Term, + const CFGBlock * B, ExplodedNode* Pred) { assert (B->succ_size() == 2); GRBranchNodeBuilder Builder(B, *(B->succ_begin()), *(B->succ_begin()+1), @@ -382,7 +373,7 @@ void GRCoreEngine::HandleBranch(Stmt* Cond, Stmt* Term, CFGBlock * B, ProcessBranch(Cond, Term, Builder); } -void GRCoreEngine::HandlePostStmt(const PostStmt& L, CFGBlock* B, +void GRCoreEngine::HandlePostStmt(const PostStmt& L, const CFGBlock* B, unsigned StmtIdx, ExplodedNode* Pred) { assert (!B->empty()); @@ -415,7 +406,7 @@ void GRCoreEngine::GenerateNode(const ProgramPoint& Loc, if (IsNew) WList->Enqueue(Node); } -GRStmtNodeBuilder::GRStmtNodeBuilder(CFGBlock* b, unsigned idx, +GRStmtNodeBuilder::GRStmtNodeBuilder(const CFGBlock* b, unsigned idx, ExplodedNode* N, GRCoreEngine* e, GRStateManager &mgr) : Eng(*e), B(*b), Idx(idx), Pred(N), Mgr(mgr), Auditor(0), @@ -438,7 +429,7 @@ void GRStmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) { if (isa<CallEnter>(N->getLocation())) { // Still use the index of the CallExpr. It's needed to create the callee // StackFrameContext. - Eng.WList->Enqueue(N, B, Idx); + Eng.WList->Enqueue(N, &B, Idx); return; } @@ -447,7 +438,7 @@ void GRStmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) { if (Loc == N->getLocation()) { // Note: 'N' should be a fresh node because otherwise it shouldn't be // a member of Deferred. - Eng.WList->Enqueue(N, B, Idx+1); + Eng.WList->Enqueue(N, &B, Idx+1); return; } @@ -456,10 +447,10 @@ void GRStmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) { Succ->addPredecessor(N, *Eng.G); if (IsNew) - Eng.WList->Enqueue(Succ, B, Idx+1); + Eng.WList->Enqueue(Succ, &B, Idx+1); } -ExplodedNode* GRStmtNodeBuilder::MakeNode(ExplodedNodeSet& Dst, Stmt* S, +ExplodedNode* GRStmtNodeBuilder::MakeNode(ExplodedNodeSet& Dst, const Stmt* S, ExplodedNode* Pred, const GRState* St, ProgramPoint::Kind K) { const GRState* PredState = GetState(Pred); @@ -692,6 +683,59 @@ void GREndPathNodeBuilder::GenerateCallExitNode(const GRState *state) { void GRCallEnterNodeBuilder::GenerateNode(const GRState *state, const LocationContext *LocCtx) { + // Check if the callee is in the same translation unit. + if (CalleeCtx->getTranslationUnit() != + Pred->getLocationContext()->getTranslationUnit()) { + // Create a new engine. We must be careful that the new engine should not + // reference data structures owned by the old engine. + + AnalysisManager &OldMgr = Eng.SubEngine.getAnalysisManager(); + + // Get the callee's translation unit. + idx::TranslationUnit *TU = CalleeCtx->getTranslationUnit(); + + // Create a new AnalysisManager with components of the callee's + // TranslationUnit. + // The Diagnostic is actually shared when we create ASTUnits from AST files. + AnalysisManager AMgr(TU->getASTContext(), TU->getDiagnostic(), + OldMgr.getLangOptions(), + OldMgr.getPathDiagnosticClient(), + OldMgr.getStoreManagerCreator(), + OldMgr.getConstraintManagerCreator(), + OldMgr.getIndexer(), + OldMgr.getMaxNodes(), OldMgr.getMaxLoop(), + OldMgr.shouldVisualizeGraphviz(), + OldMgr.shouldVisualizeUbigraph(), + OldMgr.shouldPurgeDead(), + OldMgr.shouldEagerlyAssume(), + OldMgr.shouldTrimGraph(), + OldMgr.shouldInlineCall(), + OldMgr.getAnalysisContextManager().getUseUnoptimizedCFG()); + llvm::OwningPtr<GRTransferFuncs> TF(MakeCFRefCountTF(AMgr.getASTContext(), + /* GCEnabled */ false, + AMgr.getLangOptions())); + // Create the new engine. + GRExprEngine NewEng(AMgr, TF.take()); + + // Create the new LocationContext. + AnalysisContext *NewAnaCtx = AMgr.getAnalysisContext(CalleeCtx->getDecl(), + CalleeCtx->getTranslationUnit()); + const StackFrameContext *OldLocCtx = cast<StackFrameContext>(LocCtx); + const StackFrameContext *NewLocCtx = AMgr.getStackFrame(NewAnaCtx, + OldLocCtx->getParent(), + OldLocCtx->getCallSite(), + OldLocCtx->getCallSiteBlock(), + OldLocCtx->getIndex()); + + // Now create an initial state for the new engine. + const GRState *NewState = NewEng.getStateManager().MarshalState(state, + NewLocCtx); + ExplodedNodeSet ReturnNodes; + NewEng.ExecuteWorkListWithInitialState(NewLocCtx, AMgr.getMaxNodes(), + NewState, ReturnNodes); + return; + } + // Get the callee entry block. const CFGBlock *Entry = &(LocCtx->getCFG()->getEntry()); assert(Entry->empty()); @@ -721,6 +765,6 @@ void GRCallExitNodeBuilder::GenerateNode(const GRState *state) { ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); Node->addPredecessor(const_cast<ExplodedNode*>(Pred), *Eng.G); if (isNew) - Eng.WList->Enqueue(Node, *const_cast<CFGBlock*>(LocCtx->getCallSiteBlock()), + Eng.WList->Enqueue(Node, LocCtx->getCallSiteBlock(), LocCtx->getIndex() + 1); } diff --git a/lib/Checker/GRExprEngine.cpp b/lib/Checker/GRExprEngine.cpp index 07fee9d..feb826e 100644 --- a/lib/Checker/GRExprEngine.cpp +++ b/lib/Checker/GRExprEngine.cpp @@ -169,17 +169,18 @@ public: // Checker worklist routines. //===----------------------------------------------------------------------===// -void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, - ExplodedNodeSet &Src, bool isPrevisit) { +void GRExprEngine::CheckerVisit(const Stmt *S, ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, CallbackKind Kind) { // Determine if we already have a cached 'CheckersOrdered' vector - // specifically tailored for the provided <Stmt kind, isPrevisit>. This + // specifically tailored for the provided <CallbackKind, Stmt kind>. This // can reduce the number of checkers actually called. CheckersOrdered *CO = &Checkers; llvm::OwningPtr<CheckersOrdered> NewCO; - - const std::pair<unsigned, unsigned> &K = - std::make_pair((unsigned)S->getStmtClass(), isPrevisit ? 1U : 0U); + + // The cache key is made up of the and the callback kind (pre- or post-visit) + // and the statement kind. + CallbackTag K = GetCallbackTag(Kind, S->getStmtClass()); CheckersOrdered *& CO_Ref = COCache[K]; @@ -204,7 +205,10 @@ void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet *PrevSet = &Src; unsigned checkersEvaluated = 0; - for (CheckersOrdered::iterator I=CO->begin(), E=CO->end(); I!=E; ++I){ + for (CheckersOrdered::iterator I=CO->begin(), E=CO->end(); I!=E; ++I) { + // If all nodes are sunk, bail out early. + if (PrevSet->empty()) + break; ExplodedNodeSet *CurrSet = 0; if (I+1 == E) CurrSet = &Dst; @@ -219,8 +223,8 @@ void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); NI != NE; ++NI) { - checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, tag, isPrevisit, - respondsToCallback); + checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, tag, + Kind == PreVisitStmtCallback, respondsToCallback); } @@ -235,7 +239,9 @@ void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, // If we built NewCO, check if we called all the checkers. This is important // so that we know that we accurately determined the entire set of checkers - // that responds to this callback. + // that responds to this callback. Note that 'checkersEvaluated' might + // not be the same as Checkers.size() if one of the Checkers generates + // a sink node. if (NewCO.get() && checkersEvaluated == Checkers.size()) CO_Ref = NewCO.take(); @@ -300,10 +306,9 @@ bool GRExprEngine::CheckerEvalCall(const CallExpr *CE, // FIXME: This is largely copy-paste from CheckerVisit(). Need to // unify. -void GRExprEngine::CheckerVisitBind(const Stmt *AssignE, const Stmt *StoreE, - ExplodedNodeSet &Dst, - ExplodedNodeSet &Src, - SVal location, SVal val, bool isPrevisit) { +void GRExprEngine::CheckerVisitBind(const Stmt *StoreE, ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, SVal location, + SVal val, bool isPrevisit) { if (Checkers.empty()) { Dst.insert(Src); @@ -328,7 +333,7 @@ void GRExprEngine::CheckerVisitBind(const Stmt *AssignE, const Stmt *StoreE, for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); NI != NE; ++NI) - checker->GR_VisitBind(*CurrSet, *Builder, *this, AssignE, StoreE, + checker->GR_VisitBind(*CurrSet, *Builder, *this, StoreE, *NI, tag, location, val, isPrevisit); // Update which NodeSet is the current one. @@ -457,7 +462,7 @@ const GRState* GRExprEngine::getInitialState(const LocationContext *InitLoc) { break; SVal V = state->getSVal(loc::MemRegionVal(R)); - SVal Constraint_untested = EvalBinOp(state, BinaryOperator::GT, V, + SVal Constraint_untested = EvalBinOp(state, BO_GT, V, ValMgr.makeZeroVal(T), getContext().IntTy); @@ -499,29 +504,135 @@ const GRState* GRExprEngine::getInitialState(const LocationContext *InitLoc) { /// logic for handling assumptions on symbolic values. const GRState *GRExprEngine::ProcessAssume(const GRState *state, SVal cond, bool assumption) { - for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); - I != E; ++I) { + // Determine if we already have a cached 'CheckersOrdered' vector + // specifically tailored for processing assumptions. This + // can reduce the number of checkers actually called. + CheckersOrdered *CO = &Checkers; + llvm::OwningPtr<CheckersOrdered> NewCO; - if (!state) - return NULL; + CallbackTag K = GetCallbackTag(ProcessAssumeCallback); + CheckersOrdered *& CO_Ref = COCache[K]; + + if (!CO_Ref) { + // If we have no previously cached CheckersOrdered vector for this + // statement kind, then create one. + NewCO.reset(new CheckersOrdered); + } + else { + // Use the already cached set. + CO = CO_Ref; + } + + if (!CO->empty()) { + // Let the checkers have a crack at the assume before the transfer functions + // get their turn. + for (CheckersOrdered::iterator I = CO->begin(), E = CO->end(); I!=E; ++I) { - state = I->second->EvalAssume(state, cond, assumption); + // If any checker declares the state infeasible (or if it starts that + // way), bail out. + if (!state) + return NULL; + + Checker *C = I->second; + bool respondsToCallback = true; + + state = C->EvalAssume(state, cond, assumption, &respondsToCallback); + + // Check if we're building the cache of checkers that care about Assumes. + if (NewCO.get() && respondsToCallback) + NewCO->push_back(*I); + } + + // If we got through all the checkers, and we built a list of those that + // care about Assumes, save it. + if (NewCO.get()) + CO_Ref = NewCO.take(); } + // If the state is infeasible at this point, bail out. if (!state) return NULL; return TF->EvalAssume(state, cond, assumption); } +bool GRExprEngine::WantsRegionChangeUpdate(const GRState* state) { + CallbackTag K = GetCallbackTag(EvalRegionChangesCallback); + CheckersOrdered *CO = COCache[K]; + + if (!CO) + CO = &Checkers; + + for (CheckersOrdered::iterator I = CO->begin(), E = CO->end(); I != E; ++I) { + Checker *C = I->second; + if (C->WantsRegionChangeUpdate(state)) + return true; + } + + return false; +} + +const GRState * +GRExprEngine::ProcessRegionChanges(const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End) { + // FIXME: Most of this method is copy-pasted from ProcessAssume. + + // Determine if we already have a cached 'CheckersOrdered' vector + // specifically tailored for processing region changes. This + // can reduce the number of checkers actually called. + CheckersOrdered *CO = &Checkers; + llvm::OwningPtr<CheckersOrdered> NewCO; + + CallbackTag K = GetCallbackTag(EvalRegionChangesCallback); + CheckersOrdered *& CO_Ref = COCache[K]; + + if (!CO_Ref) { + // If we have no previously cached CheckersOrdered vector for this + // callback, then create one. + NewCO.reset(new CheckersOrdered); + } + else { + // Use the already cached set. + CO = CO_Ref; + } + + // If there are no checkers, just return the state as is. + if (CO->empty()) + return state; + + for (CheckersOrdered::iterator I = CO->begin(), E = CO->end(); I != E; ++I) { + // If any checker declares the state infeasible (or if it starts that way), + // bail out. + if (!state) + return NULL; + + Checker *C = I->second; + bool respondsToCallback = true; + + state = C->EvalRegionChanges(state, Begin, End, &respondsToCallback); + + // See if we're building a cache of checkers that care about region changes. + if (NewCO.get() && respondsToCallback) + NewCO->push_back(*I); + } + + // If we got through all the checkers, and we built a list of those that + // care about region changes, save it. + if (NewCO.get()) + CO_Ref = NewCO.take(); + + return state; +} + void GRExprEngine::ProcessEndWorklist(bool hasWorkRemaining) { for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); I != E; ++I) { - I->second->VisitEndAnalysis(G, BR, hasWorkRemaining); + I->second->VisitEndAnalysis(G, BR, *this); } } -void GRExprEngine::ProcessStmt(CFGElement CE, GRStmtNodeBuilder& builder) { +void GRExprEngine::ProcessStmt(const CFGElement CE,GRStmtNodeBuilder& builder) { CurrentStmt = CE.getStmt(); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), CurrentStmt->getLocStart(), @@ -535,15 +646,23 @@ void GRExprEngine::ProcessStmt(CFGElement CE, GRStmtNodeBuilder& builder) { Builder->setAuditor(BatchAuditor.get()); // Create the cleaned state. - const ExplodedNode *BasePred = Builder->getBasePredecessor(); + const LocationContext *LC = EntryNode->getLocationContext(); + SymbolReaper SymReaper(LC, CurrentStmt, SymMgr); + + if (AMgr.shouldPurgeDead()) { + const GRState *St = EntryNode->getState(); - SymbolReaper SymReaper(BasePred->getLocationContext(), CurrentStmt, SymMgr); + for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); + I != E; ++I) { + Checker *checker = I->second; + checker->MarkLiveSymbols(St, SymReaper); + } - CleanedState = AMgr.shouldPurgeDead() - ? StateMgr.RemoveDeadBindings(EntryNode->getState(), - BasePred->getLocationContext()->getCurrentStackFrame(), - SymReaper) - : EntryNode->getState(); + const StackFrameContext *SFC = LC->getCurrentStackFrame(); + CleanedState = StateMgr.RemoveDeadBindings(St, SFC, SymReaper); + } else { + CleanedState = EntryNode->getState(); + } // Process any special transfer function for dead symbols. ExplodedNodeSet Tmp; @@ -625,7 +744,8 @@ void GRExprEngine::ProcessStmt(CFGElement CE, GRStmtNodeBuilder& builder) { Builder = NULL; } -void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { +void GRExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, + ExplodedNodeSet& Dst) { PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), S->getLocStart(), "Error evaluating statement"); @@ -641,7 +761,6 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { switch (S->getStmtClass()) { // C++ stuff we don't support yet. - case Stmt::CXXBindReferenceExprClass: case Stmt::CXXBindTemporaryExprClass: case Stmt::CXXCatchStmtClass: case Stmt::CXXConstructExprClass: @@ -740,13 +859,13 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { break; case Stmt::BinaryOperatorClass: { - BinaryOperator* B = cast<BinaryOperator>(S); + const BinaryOperator* B = cast<BinaryOperator>(S); if (B->isLogicalOp()) { VisitLogicalExpr(B, Pred, Dst); break; } - else if (B->getOpcode() == BinaryOperator::Comma) { + else if (B->getOpcode() == BO_Comma) { const GRState* state = GetState(Pred); MakeNode(Dst, B, Pred, state->BindExpr(B, state->getSVal(B->getRHS()))); break; @@ -766,25 +885,25 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { case Stmt::CallExprClass: case Stmt::CXXOperatorCallExprClass: { - CallExpr* C = cast<CallExpr>(S); + const CallExpr* C = cast<CallExpr>(S); VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst, false); break; } case Stmt::CXXMemberCallExprClass: { - CXXMemberCallExpr *MCE = cast<CXXMemberCallExpr>(S); + const CXXMemberCallExpr *MCE = cast<CXXMemberCallExpr>(S); VisitCXXMemberCallExpr(MCE, Pred, Dst); break; } case Stmt::CXXNewExprClass: { - CXXNewExpr *NE = cast<CXXNewExpr>(S); + const CXXNewExpr *NE = cast<CXXNewExpr>(S); VisitCXXNewExpr(NE, Pred, Dst); break; } case Stmt::CXXDeleteExprClass: { - CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S); + const CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S); VisitCXXDeleteExpr(CDE, Pred, Dst); break; } @@ -792,7 +911,7 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { // the CFG do not model them as explicit control-flow. case Stmt::ChooseExprClass: { // __builtin_choose_expr - ChooseExpr* C = cast<ChooseExpr>(S); + const ChooseExpr* C = cast<ChooseExpr>(S); VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); break; } @@ -806,7 +925,7 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { break; case Stmt::ConditionalOperatorClass: { // '?' operator - ConditionalOperator* C = cast<ConditionalOperator>(S); + const ConditionalOperator* C = cast<ConditionalOperator>(S); VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); break; } @@ -836,7 +955,7 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { case Stmt::CXXReinterpretCastExprClass: case Stmt::CXXConstCastExprClass: case Stmt::CXXFunctionalCastExprClass: { - CastExpr* C = cast<CastExpr>(S); + const CastExpr* C = cast<CastExpr>(S); VisitCast(C, C->getSubExpr(), Pred, Dst, false); break; } @@ -893,7 +1012,7 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { break; case Stmt::StmtExprClass: { - StmtExpr* SE = cast<StmtExpr>(S); + const StmtExpr* SE = cast<StmtExpr>(S); if (SE->getSubStmt()->body_empty()) { // Empty statement expression. @@ -924,8 +1043,8 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { break; case Stmt::UnaryOperatorClass: { - UnaryOperator *U = cast<UnaryOperator>(S); - if (AMgr.shouldEagerlyAssume()&&(U->getOpcode() == UnaryOperator::LNot)) { + const UnaryOperator *U = cast<UnaryOperator>(S); + if (AMgr.shouldEagerlyAssume()&&(U->getOpcode() == UO_LNot)) { ExplodedNodeSet Tmp; VisitUnaryOperator(U, Pred, Tmp, false); EvalEagerlyAssume(Dst, Tmp, U); @@ -943,7 +1062,7 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { } } -void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred, +void GRExprEngine::VisitLValue(const Expr* Ex, ExplodedNode* Pred, ExplodedNodeSet& Dst) { PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), @@ -984,7 +1103,7 @@ void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred, case Stmt::CallExprClass: case Stmt::CXXOperatorCallExprClass: { - CallExpr *C = cast<CallExpr>(Ex); + const CallExpr *C = cast<CallExpr>(Ex); assert(CalleeReturnsReferenceOrRecord(C)); VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst, true); break; @@ -1000,7 +1119,7 @@ void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred, case Stmt::ImplicitCastExprClass: case Stmt::CStyleCastExprClass: { - CastExpr *C = cast<CastExpr>(Ex); + const CastExpr *C = cast<CastExpr>(Ex); QualType T = Ex->getType(); VisitCast(C, C->getSubExpr(), Pred, Dst, true); break; @@ -1015,7 +1134,7 @@ void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred, return; case Stmt::ObjCMessageExprClass: { - ObjCMessageExpr *ME = cast<ObjCMessageExpr>(Ex); + const ObjCMessageExpr *ME = cast<ObjCMessageExpr>(Ex); assert(ReceiverReturnsReferenceOrRecord(ME)); VisitObjCMessageExpr(ME, Pred, Dst, true); return; @@ -1056,6 +1175,9 @@ void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred, // In C++, binding an rvalue to a reference requires to create an object. case Stmt::CXXBoolLiteralExprClass: case Stmt::IntegerLiteralClass: + case Stmt::CharacterLiteralClass: + case Stmt::FloatingLiteralClass: + case Stmt::ImaginaryLiteralClass: CreateCXXTemporaryObject(Ex, Pred, Dst); return; @@ -1081,7 +1203,8 @@ void GRExprEngine::VisitLValue(Expr* Ex, ExplodedNode* Pred, // Block entrance. (Update counters). //===----------------------------------------------------------------------===// -bool GRExprEngine::ProcessBlockEntrance(CFGBlock* B, const ExplodedNode *Pred, +bool GRExprEngine::ProcessBlockEntrance(const CFGBlock* B, + const ExplodedNode *Pred, GRBlockCounter BC) { return BC.getNumVisited(Pred->getLocationContext()->getCurrentStackFrame(), B->getBlockID()) < AMgr.getMaxLoop(); @@ -1091,7 +1214,7 @@ bool GRExprEngine::ProcessBlockEntrance(CFGBlock* B, const ExplodedNode *Pred, // Generic node creation. //===----------------------------------------------------------------------===// -ExplodedNode* GRExprEngine::MakeNode(ExplodedNodeSet& Dst, Stmt* S, +ExplodedNode* GRExprEngine::MakeNode(ExplodedNodeSet& Dst, const Stmt* S, ExplodedNode* Pred, const GRState* St, ProgramPoint::Kind K, const void *tag) { assert (Builder && "GRStmtNodeBuilder not present."); @@ -1105,8 +1228,8 @@ ExplodedNode* GRExprEngine::MakeNode(ExplodedNodeSet& Dst, Stmt* S, //===----------------------------------------------------------------------===// const GRState* GRExprEngine::MarkBranch(const GRState* state, - Stmt* Terminator, - bool branchTaken) { + const Stmt* Terminator, + bool branchTaken) { switch (Terminator->getStmtClass()) { default: @@ -1114,10 +1237,10 @@ const GRState* GRExprEngine::MarkBranch(const GRState* state, case Stmt::BinaryOperatorClass: { // '&&' and '||' - BinaryOperator* B = cast<BinaryOperator>(Terminator); + const BinaryOperator* B = cast<BinaryOperator>(Terminator); BinaryOperator::Opcode Op = B->getOpcode(); - assert (Op == BinaryOperator::LAnd || Op == BinaryOperator::LOr); + assert (Op == BO_LAnd || Op == BO_LOr); // For &&, if we take the true branch, then the value of the whole // expression is that of the RHS expression. @@ -1125,21 +1248,21 @@ const GRState* GRExprEngine::MarkBranch(const GRState* state, // For ||, if we take the false branch, then the value of the whole // expression is that of the RHS expression. - Expr* Ex = (Op == BinaryOperator::LAnd && branchTaken) || - (Op == BinaryOperator::LOr && !branchTaken) - ? B->getRHS() : B->getLHS(); + const Expr* Ex = (Op == BO_LAnd && branchTaken) || + (Op == BO_LOr && !branchTaken) + ? B->getRHS() : B->getLHS(); return state->BindExpr(B, UndefinedVal(Ex)); } case Stmt::ConditionalOperatorClass: { // ?: - ConditionalOperator* C = cast<ConditionalOperator>(Terminator); + const ConditionalOperator* C = cast<ConditionalOperator>(Terminator); // For ?, if branchTaken == true then the value is either the LHS or // the condition itself. (GNU extension). - Expr* Ex; + const Expr* Ex; if (branchTaken) Ex = C->getLHS() ? C->getLHS() : C->getCond(); @@ -1151,9 +1274,9 @@ const GRState* GRExprEngine::MarkBranch(const GRState* state, case Stmt::ChooseExprClass: { // ?: - ChooseExpr* C = cast<ChooseExpr>(Terminator); + const ChooseExpr* C = cast<ChooseExpr>(Terminator); - Expr* Ex = branchTaken ? C->getLHS() : C->getRHS(); + const Expr* Ex = branchTaken ? C->getLHS() : C->getRHS(); return state->BindExpr(C, UndefinedVal(Ex)); } } @@ -1165,16 +1288,16 @@ const GRState* GRExprEngine::MarkBranch(const GRState* state, /// This function returns the SVal bound to Condition->IgnoreCasts if all the // cast(s) did was sign-extend the original value. static SVal RecoverCastedSymbol(GRStateManager& StateMgr, const GRState* state, - Stmt* Condition, ASTContext& Ctx) { + const Stmt* Condition, ASTContext& Ctx) { - Expr *Ex = dyn_cast<Expr>(Condition); + const Expr *Ex = dyn_cast<Expr>(Condition); if (!Ex) return UnknownVal(); uint64_t bits = 0; bool bitsInit = false; - while (CastExpr *CE = dyn_cast<CastExpr>(Ex)) { + while (const CastExpr *CE = dyn_cast<CastExpr>(Ex)) { QualType T = CE->getType(); if (!T->isIntegerType()) @@ -1198,7 +1321,7 @@ static SVal RecoverCastedSymbol(GRStateManager& StateMgr, const GRState* state, return state->getSVal(Ex); } -void GRExprEngine::ProcessBranch(Stmt* Condition, Stmt* Term, +void GRExprEngine::ProcessBranch(const Stmt* Condition, const Stmt* Term, GRBranchNodeBuilder& builder) { // Check for NULL conditions; e.g. "for(;;)" @@ -1285,7 +1408,7 @@ void GRExprEngine::ProcessIndirectGoto(GRIndirectGotoNodeBuilder& builder) { typedef GRIndirectGotoNodeBuilder::iterator iterator; if (isa<loc::GotoLabel>(V)) { - LabelStmt* L = cast<loc::GotoLabel>(V).getLabel(); + const LabelStmt* L = cast<loc::GotoLabel>(V).getLabel(); for (iterator I=builder.begin(), E=builder.end(); I != E; ++I) { if (I.getLabel() == L) { @@ -1314,7 +1437,8 @@ void GRExprEngine::ProcessIndirectGoto(GRIndirectGotoNodeBuilder& builder) { } -void GRExprEngine::VisitGuardedExpr(Expr* Ex, Expr* L, Expr* R, +void GRExprEngine::VisitGuardedExpr(const Expr* Ex, const Expr* L, + const Expr* R, ExplodedNode* Pred, ExplodedNodeSet& Dst) { assert(Ex == CurrentStmt && @@ -1325,7 +1449,7 @@ void GRExprEngine::VisitGuardedExpr(Expr* Ex, Expr* L, Expr* R, assert (X.isUndef()); - Expr *SE = (Expr*) cast<UndefinedVal>(X).getData(); + const Expr *SE = (Expr*) cast<UndefinedVal>(X).getData(); assert(SE); X = state->getSVal(SE); @@ -1350,7 +1474,7 @@ void GRExprEngine::ProcessEndPath(GREndPathNodeBuilder& builder) { void GRExprEngine::ProcessSwitch(GRSwitchNodeBuilder& builder) { typedef GRSwitchNodeBuilder::iterator iterator; const GRState* state = builder.getState(); - Expr* CondE = builder.getCondition(); + const Expr* CondE = builder.getCondition(); SVal CondV_untested = state->getSVal(CondE); if (CondV_untested.isUndef()) { @@ -1363,10 +1487,12 @@ void GRExprEngine::ProcessSwitch(GRSwitchNodeBuilder& builder) { DefinedOrUnknownSVal CondV = cast<DefinedOrUnknownSVal>(CondV_untested); const GRState *DefaultSt = state; - bool defaultIsFeasible = false; + + iterator I = builder.begin(), EI = builder.end(); + bool defaultIsFeasible = I == EI; - for (iterator I = builder.begin(), EI = builder.end(); I != EI; ++I) { - CaseStmt* Case = cast<CaseStmt>(I.getCase()); + for ( ; I != EI; ++I) { + const CaseStmt* Case = I.getCase(); // Evaluate the LHS of the case value. Expr::EvalResult V1; @@ -1382,7 +1508,7 @@ void GRExprEngine::ProcessSwitch(GRSwitchNodeBuilder& builder) { // Get the RHS of the case, if it exists. Expr::EvalResult V2; - if (Expr* E = Case->getRHS()) { + if (const Expr* E = Case->getRHS()) { b = E->Evaluate(V2, getContext()); assert(b && V2.Val.isInt() && !V2.HasSideEffects && "Case condition must evaluate to an integer constant."); @@ -1440,15 +1566,14 @@ void GRExprEngine::ProcessSwitch(GRSwitchNodeBuilder& builder) { } void GRExprEngine::ProcessCallEnter(GRCallEnterNodeBuilder &B) { - const FunctionDecl *FD = B.getCallee(); - const StackFrameContext *LocCtx = AMgr.getStackFrame(FD, - B.getLocationContext(), - B.getCallExpr(), - B.getBlock(), - B.getIndex()); + const StackFrameContext *LocCtx + = AMgr.getStackFrame(B.getCalleeContext(), + B.getLocationContext(), + B.getCallExpr(), + B.getBlock(), + B.getIndex()); - const GRState *state = B.getState(); - state = getStoreManager().EnterStackFrame(state, LocCtx); + const GRState *state = B.getState()->EnterStackFrame(LocCtx); B.GenerateNode(state, LocCtx); } @@ -1490,11 +1615,11 @@ void GRExprEngine::ProcessCallExit(GRCallExitNodeBuilder &B) { // Transfer functions: logical operations ('&&', '||'). //===----------------------------------------------------------------------===// -void GRExprEngine::VisitLogicalExpr(BinaryOperator* B, ExplodedNode* Pred, +void GRExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode* Pred, ExplodedNodeSet& Dst) { - assert(B->getOpcode() == BinaryOperator::LAnd || - B->getOpcode() == BinaryOperator::LOr); + assert(B->getOpcode() == BO_LAnd || + B->getOpcode() == BO_LOr); assert(B==CurrentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(B)); @@ -1534,7 +1659,7 @@ void GRExprEngine::VisitLogicalExpr(BinaryOperator* B, ExplodedNode* Pred, // We took the LHS expression. Depending on whether we are '&&' or // '||' we know what the value of the expression is via properties of // the short-circuiting. - X = ValMgr.makeIntVal(B->getOpcode() == BinaryOperator::LAnd ? 0U : 1U, + X = ValMgr.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U, B->getType()); MakeNode(Dst, B, Pred, state->BindExpr(B, X)); } @@ -1544,7 +1669,7 @@ void GRExprEngine::VisitLogicalExpr(BinaryOperator* B, ExplodedNode* Pred, // Transfer functions: Loads and stores. //===----------------------------------------------------------------------===// -void GRExprEngine::VisitBlockExpr(BlockExpr *BE, ExplodedNode *Pred, +void GRExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { ExplodedNodeSet Tmp; @@ -1557,21 +1682,21 @@ void GRExprEngine::VisitBlockExpr(BlockExpr *BE, ExplodedNode *Pred, ProgramPoint::PostLValueKind); // Post-visit the BlockExpr. - CheckerVisit(BE, Dst, Tmp, false); + CheckerVisit(BE, Dst, Tmp, PostVisitStmtCallback); } -void GRExprEngine::VisitDeclRefExpr(DeclRefExpr *Ex, ExplodedNode *Pred, +void GRExprEngine::VisitDeclRefExpr(const DeclRefExpr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst, bool asLValue) { VisitCommonDeclRefExpr(Ex, Ex->getDecl(), Pred, Dst, asLValue); } -void GRExprEngine::VisitBlockDeclRefExpr(BlockDeclRefExpr *Ex, +void GRExprEngine::VisitBlockDeclRefExpr(const BlockDeclRefExpr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst, bool asLValue) { VisitCommonDeclRefExpr(Ex, Ex->getDecl(), Pred, Dst, asLValue); } -void GRExprEngine::VisitCommonDeclRefExpr(Expr *Ex, const NamedDecl *D, +void GRExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, ExplodedNode *Pred, ExplodedNodeSet &Dst, bool asLValue) { @@ -1618,12 +1743,12 @@ void GRExprEngine::VisitCommonDeclRefExpr(Expr *Ex, const NamedDecl *D, } /// VisitArraySubscriptExpr - Transfer function for array accesses -void GRExprEngine::VisitArraySubscriptExpr(ArraySubscriptExpr* A, +void GRExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr* A, ExplodedNode* Pred, ExplodedNodeSet& Dst, bool asLValue){ - Expr* Base = A->getBase()->IgnoreParens(); - Expr* Idx = A->getIdx()->IgnoreParens(); + const Expr* Base = A->getBase()->IgnoreParens(); + const Expr* Idx = A->getIdx()->IgnoreParens(); ExplodedNodeSet Tmp; if (Base->getType()->isVectorType()) { @@ -1641,7 +1766,7 @@ void GRExprEngine::VisitArraySubscriptExpr(ArraySubscriptExpr* A, Visit(Idx, *I1, Tmp2); // Evaluate the index. ExplodedNodeSet Tmp3; - CheckerVisit(A, Tmp3, Tmp2, true); + CheckerVisit(A, Tmp3, Tmp2, PreVisitStmtCallback); for (ExplodedNodeSet::iterator I2=Tmp3.begin(),E2=Tmp3.end();I2!=E2; ++I2) { const GRState* state = GetState(*I2); @@ -1658,7 +1783,7 @@ void GRExprEngine::VisitArraySubscriptExpr(ArraySubscriptExpr* A, } /// VisitMemberExpr - Transfer function for member expressions. -void GRExprEngine::VisitMemberExpr(MemberExpr* M, ExplodedNode* Pred, +void GRExprEngine::VisitMemberExpr(const MemberExpr* M, ExplodedNode* Pred, ExplodedNodeSet& Dst, bool asLValue) { Expr* Base = M->getBase()->IgnoreParens(); @@ -1689,16 +1814,15 @@ 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, Stmt *AssignE, - Stmt* StoreE, ExplodedNode* Pred, - const GRState* state, SVal location, SVal Val, - bool atDeclInit) { +void GRExprEngine::EvalBind(ExplodedNodeSet& Dst, const Stmt* StoreE, + ExplodedNode* Pred, const GRState* state, + SVal location, SVal Val, bool atDeclInit) { // Do a previsit of the bind. ExplodedNodeSet CheckedSet, Src; Src.Add(Pred); - CheckerVisitBind(AssignE, StoreE, CheckedSet, Src, location, Val, true); + CheckerVisitBind(StoreE, CheckedSet, Src, location, Val, true); for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); I!=E; ++I) { @@ -1731,6 +1855,10 @@ void GRExprEngine::EvalBind(ExplodedNodeSet& Dst, Stmt *AssignE, // 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. + + // NOTE: We use 'AssignE' for the location of the PostStore if 'AssignE' + // is non-NULL. Checkers typically care about + GRStmtNodeBuilderRef BuilderRef(Dst, *Builder, *this, *I, newState, StoreE, newState != state); @@ -1740,12 +1868,14 @@ void GRExprEngine::EvalBind(ExplodedNodeSet& Dst, Stmt *AssignE, /// EvalStore - Handle the semantics of a store via an assignment. /// @param Dst The node set to store generated state nodes -/// @param Ex The expression representing the location of the store +/// @param AssignE The assignment expression if the store happens in an +/// assignment. +/// @param LocatioinE The location expression that is stored to. /// @param state The current simulation state /// @param location The location to store the value /// @param Val The value to be stored -void GRExprEngine::EvalStore(ExplodedNodeSet& Dst, Expr *AssignE, - Expr* StoreE, +void GRExprEngine::EvalStore(ExplodedNodeSet& Dst, const Expr *AssignE, + const Expr* LocationE, ExplodedNode* Pred, const GRState* state, SVal location, SVal Val, const void *tag) { @@ -1754,7 +1884,7 @@ void GRExprEngine::EvalStore(ExplodedNodeSet& Dst, Expr *AssignE, // Evaluate the location (checks for bad dereferences). ExplodedNodeSet Tmp; - EvalLocation(Tmp, StoreE, Pred, state, location, tag, false); + EvalLocation(Tmp, LocationE, Pred, state, location, tag, false); if (Tmp.empty()) return; @@ -1765,12 +1895,16 @@ void GRExprEngine::EvalStore(ExplodedNodeSet& Dst, Expr *AssignE, ProgramPoint::PostStoreKind); SaveAndRestore<const void*> OldTag(Builder->Tag, tag); - // Proceed with the store. + // Proceed with the store. We use AssignE as the anchor for the PostStore + // ProgramPoint if it is non-NULL, and LocationE otherwise. + const Expr *StoreE = AssignE ? AssignE : LocationE; + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) - EvalBind(Dst, AssignE, StoreE, *NI, GetState(*NI), location, Val); + EvalBind(Dst, StoreE, *NI, GetState(*NI), location, Val); } -void GRExprEngine::EvalLoad(ExplodedNodeSet& Dst, Expr *Ex, ExplodedNode* Pred, +void GRExprEngine::EvalLoad(ExplodedNodeSet& Dst, const Expr *Ex, + ExplodedNode* Pred, const GRState* state, SVal location, const void *tag, QualType LoadTy) { @@ -1780,7 +1914,7 @@ void GRExprEngine::EvalLoad(ExplodedNodeSet& Dst, Expr *Ex, ExplodedNode* Pred, if (const TypedRegion *TR = dyn_cast_or_null<TypedRegion>(location.getAsRegion())) { - QualType ValTy = TR->getValueType(getContext()); + QualType ValTy = TR->getValueType(); if (const ReferenceType *RT = ValTy->getAs<ReferenceType>()) { static int loadReferenceTag = 0; ExplodedNodeSet Tmp; @@ -1800,7 +1934,7 @@ void GRExprEngine::EvalLoad(ExplodedNodeSet& Dst, Expr *Ex, ExplodedNode* Pred, EvalLoadCommon(Dst, Ex, Pred, state, location, tag, LoadTy); } -void GRExprEngine::EvalLoadCommon(ExplodedNodeSet& Dst, Expr *Ex, +void GRExprEngine::EvalLoadCommon(ExplodedNodeSet& Dst, const Expr *Ex, ExplodedNode* Pred, const GRState* state, SVal location, const void *tag, QualType LoadTy) { @@ -1834,7 +1968,7 @@ void GRExprEngine::EvalLoadCommon(ExplodedNodeSet& Dst, Expr *Ex, } } -void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, Stmt *S, +void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, const Stmt *S, ExplodedNode* Pred, const GRState* state, SVal location, const void *tag, bool isLoad) { @@ -1885,21 +2019,34 @@ bool GRExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE, if (!FD) return false; - if (!FD->hasBody(FD)) - return false; + // Check if the function definition is in the same translation unit. + if (FD->hasBody(FD)) { + // Now we have the definition of the callee, create a CallEnter node. + CallEnter Loc(CE, AMgr.getAnalysisContext(FD), Pred->getLocationContext()); + + ExplodedNode *N = Builder->generateNode(Loc, state, Pred); + Dst.Add(N); + return true; + } - // Now we have the definition of the callee, create a CallEnter node. - CallEnter Loc(CE, FD, Pred->getLocationContext()); + // Check if we can find the function definition in other translation units. + if (AMgr.hasIndexer()) { + const AnalysisContext *C = AMgr.getAnalysisContextInAnotherTU(FD); + if (C == 0) + return false; - ExplodedNode *N = Builder->generateNode(Loc, state, Pred); - if (N) + CallEnter Loc(CE, C, Pred->getLocationContext()); + ExplodedNode *N = Builder->generateNode(Loc, state, Pred); Dst.Add(N); - return true; + return true; + } + + return false; } -void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred, - CallExpr::arg_iterator AI, - CallExpr::arg_iterator AE, +void GRExprEngine::VisitCall(const CallExpr* CE, ExplodedNode* Pred, + CallExpr::const_arg_iterator AI, + CallExpr::const_arg_iterator AE, ExplodedNodeSet& Dst, bool asLValue) { // Determine the type of function we're calling (if available). @@ -1946,7 +2093,7 @@ void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred, // Now process the call itself. ExplodedNodeSet DstTmp; - Expr* Callee = CE->getCallee()->IgnoreParens(); + const Expr* Callee = CE->getCallee()->IgnoreParens(); for (ExplodedNodeSet::iterator NI=ArgsEvaluated.begin(), NE=ArgsEvaluated.end(); NI != NE; ++NI) { @@ -1954,7 +2101,7 @@ void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred, ExplodedNodeSet DstTmp2; Visit(Callee, *NI, DstTmp2); // Perform the previsit of the CallExpr, storing the results in DstTmp. - CheckerVisit(CE, DstTmp, DstTmp2, true); + CheckerVisit(CE, DstTmp, DstTmp2, PreVisitStmtCallback); } // Finally, evaluate the function call. We try each of the checkers @@ -2009,7 +2156,7 @@ void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred, // If the callee returns a reference and we want an rvalue, skip this check // and do the load. if (!(!asLValue && CalleeReturnsReference(CE))) { - CheckerVisit(CE, Dst, DstTmp3, false); + CheckerVisit(CE, Dst, DstTmp3, PostVisitStmtCallback); return; } @@ -2019,7 +2166,7 @@ void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred, // FIXME: This conversion doesn't actually happen unless the result // of CallExpr is consumed by another expression. ExplodedNodeSet DstTmp4; - CheckerVisit(CE, DstTmp4, DstTmp3, false); + CheckerVisit(CE, DstTmp4, DstTmp3, PostVisitStmtCallback); QualType LoadTy = CE->getType(); static int *ConvertToRvalueTag = 0; @@ -2039,7 +2186,7 @@ static std::pair<const void*,const void*> EagerlyAssumeTag = std::pair<const void*,const void*>(&EagerlyAssumeTag,static_cast<void*>(0)); void GRExprEngine::EvalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, - Expr *Ex) { + const Expr *Ex) { for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) { ExplodedNode *Pred = *I; @@ -2082,10 +2229,11 @@ void GRExprEngine::EvalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, // Transfer function: Objective-C ivar references. //===----------------------------------------------------------------------===// -void GRExprEngine::VisitObjCIvarRefExpr(ObjCIvarRefExpr* Ex, ExplodedNode* Pred, +void GRExprEngine::VisitObjCIvarRefExpr(const ObjCIvarRefExpr* Ex, + ExplodedNode* Pred, ExplodedNodeSet& Dst, bool asLValue) { - Expr* Base = cast<Expr>(Ex->getBase()); + const Expr* Base = cast<Expr>(Ex->getBase()); ExplodedNodeSet Tmp; Visit(Base, Pred, Tmp); @@ -2105,7 +2253,7 @@ void GRExprEngine::VisitObjCIvarRefExpr(ObjCIvarRefExpr* Ex, ExplodedNode* Pred, // Transfer function: Objective-C fast enumeration 'for' statements. //===----------------------------------------------------------------------===// -void GRExprEngine::VisitObjCForCollectionStmt(ObjCForCollectionStmt* S, +void GRExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { // ObjCForCollectionStmts are processed in two places. This method @@ -2133,11 +2281,11 @@ void GRExprEngine::VisitObjCForCollectionStmt(ObjCForCollectionStmt* S, // container is empty. Thus this transfer function will by default // result in state splitting. - Stmt* elem = S->getElement(); + const Stmt* elem = S->getElement(); SVal ElementV; - if (DeclStmt* DS = dyn_cast<DeclStmt>(elem)) { - VarDecl* ElemD = cast<VarDecl>(DS->getSingleDecl()); + if (const DeclStmt* DS = dyn_cast<DeclStmt>(elem)) { + const VarDecl* ElemD = cast<VarDecl>(DS->getSingleDecl()); assert (ElemD->getInit() == 0); ElementV = GetState(Pred)->getLValue(ElemD, Pred->getLocationContext()); VisitObjCForCollectionStmtAux(S, Pred, Dst, ElementV); @@ -2153,12 +2301,12 @@ void GRExprEngine::VisitObjCForCollectionStmt(ObjCForCollectionStmt* S, } } -void GRExprEngine::VisitObjCForCollectionStmtAux(ObjCForCollectionStmt* S, +void GRExprEngine::VisitObjCForCollectionStmtAux(const ObjCForCollectionStmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst, SVal ElementV) { // Check if the location we are writing back to is a null pointer. - Stmt* elem = S->getElement(); + const Stmt* elem = S->getElement(); ExplodedNodeSet Tmp; EvalLocation(Tmp, elem, Pred, GetState(Pred), ElementV, NULL, false); @@ -2182,7 +2330,7 @@ void GRExprEngine::VisitObjCForCollectionStmtAux(ObjCForCollectionStmt* S, // FIXME: The proper thing to do is to really iterate over the // container. We will do this with dispatch logic to the store. // For now, just 'conjure' up a symbolic value. - QualType T = R->getValueType(getContext()); + QualType T = R->getValueType(); assert(Loc::IsLocType(T)); unsigned Count = Builder->getCurrentBlockCount(); SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count); @@ -2207,23 +2355,24 @@ void GRExprEngine::VisitObjCForCollectionStmtAux(ObjCForCollectionStmt* S, namespace { class ObjCMsgWLItem { public: - ObjCMessageExpr::arg_iterator I; + ObjCMessageExpr::const_arg_iterator I; ExplodedNode *N; - ObjCMsgWLItem(const ObjCMessageExpr::arg_iterator &i, ExplodedNode *n) + ObjCMsgWLItem(const ObjCMessageExpr::const_arg_iterator &i, ExplodedNode *n) : I(i), N(n) {} }; } // end anonymous namespace -void GRExprEngine::VisitObjCMessageExpr(ObjCMessageExpr* ME, ExplodedNode* Pred, +void GRExprEngine::VisitObjCMessageExpr(const ObjCMessageExpr* ME, + ExplodedNode* Pred, ExplodedNodeSet& Dst, bool asLValue){ // Create a worklist to process both the arguments. llvm::SmallVector<ObjCMsgWLItem, 20> WL; // But first evaluate the receiver (if any). - ObjCMessageExpr::arg_iterator AI = ME->arg_begin(), AE = ME->arg_end(); - if (Expr *Receiver = ME->getInstanceReceiver()) { + ObjCMessageExpr::const_arg_iterator AI = ME->arg_begin(), AE = ME->arg_end(); + if (const Expr *Receiver = ME->getInstanceReceiver()) { ExplodedNodeSet Tmp; Visit(Receiver, Pred, Tmp); @@ -2261,7 +2410,7 @@ void GRExprEngine::VisitObjCMessageExpr(ObjCMessageExpr* ME, ExplodedNode* Pred, // Now that the arguments are processed, handle the previsits checks. ExplodedNodeSet DstPrevisit; - CheckerVisit(ME, DstPrevisit, ArgsEvaluated, true); + CheckerVisit(ME, DstPrevisit, ArgsEvaluated, PreVisitStmtCallback); // Proceed with evaluate the message expression. ExplodedNodeSet DstEval; @@ -2364,7 +2513,7 @@ void GRExprEngine::VisitObjCMessageExpr(ObjCMessageExpr* ME, ExplodedNode* Pred, // Finally, perform the post-condition check of the ObjCMessageExpr and store // the created nodes in 'Dst'. if (!(!asLValue && ReceiverReturnsReference(ME))) { - CheckerVisit(ME, Dst, DstEval, false); + CheckerVisit(ME, Dst, DstEval, PostVisitStmtCallback); return; } @@ -2374,7 +2523,7 @@ void GRExprEngine::VisitObjCMessageExpr(ObjCMessageExpr* ME, ExplodedNode* Pred, // FIXME: This conversion doesn't actually happen unless the result // of ObjCMessageExpr is consumed by another expression. ExplodedNodeSet DstRValueConvert; - CheckerVisit(ME, DstRValueConvert, DstEval, false); + CheckerVisit(ME, DstRValueConvert, DstEval, PostVisitStmtCallback); QualType LoadTy = ME->getType(); static int *ConvertToRvalueTag = 0; @@ -2390,8 +2539,9 @@ void GRExprEngine::VisitObjCMessageExpr(ObjCMessageExpr* ME, ExplodedNode* Pred, // Transfer functions: Miscellaneous statements. //===----------------------------------------------------------------------===// -void GRExprEngine::VisitCast(CastExpr *CastE, Expr *Ex, ExplodedNode *Pred, - ExplodedNodeSet &Dst, bool asLValue) { +void GRExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, + ExplodedNode *Pred, ExplodedNodeSet &Dst, + bool asLValue) { ExplodedNodeSet S1; QualType T = CastE->getType(); QualType ExTy = Ex->getType(); @@ -2406,7 +2556,7 @@ void GRExprEngine::VisitCast(CastExpr *CastE, Expr *Ex, ExplodedNode *Pred, Visit(Ex, Pred, S1); ExplodedNodeSet S2; - CheckerVisit(CastE, S2, S1, true); + CheckerVisit(CastE, S2, S1, PreVisitStmtCallback); // If we are evaluating the cast in an lvalue context, we implicitly want // the cast to evaluate to a location. @@ -2417,14 +2567,14 @@ void GRExprEngine::VisitCast(CastExpr *CastE, Expr *Ex, ExplodedNode *Pred, } switch (CastE->getCastKind()) { - case CastExpr::CK_ToVoid: + case CK_ToVoid: assert(!asLValue); for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) Dst.Add(*I); return; - case CastExpr::CK_NoOp: - case CastExpr::CK_FunctionToPointerDecay: + case CK_NoOp: + case CK_FunctionToPointerDecay: for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { // Copy the SVal of Ex to CastE. ExplodedNode *N = *I; @@ -2435,20 +2585,21 @@ void GRExprEngine::VisitCast(CastExpr *CastE, Expr *Ex, ExplodedNode *Pred, } return; - case CastExpr::CK_Unknown: - case CastExpr::CK_ArrayToPointerDecay: - case CastExpr::CK_BitCast: - case CastExpr::CK_LValueBitCast: - case CastExpr::CK_IntegralCast: - case CastExpr::CK_IntegralToPointer: - case CastExpr::CK_PointerToIntegral: - case CastExpr::CK_IntegralToFloating: - case CastExpr::CK_FloatingToIntegral: - case CastExpr::CK_FloatingCast: - case CastExpr::CK_AnyPointerToObjCPointerCast: - case CastExpr::CK_AnyPointerToBlockPointerCast: - case CastExpr::CK_DerivedToBase: - case CastExpr::CK_UncheckedDerivedToBase: { + case CK_Unknown: + case CK_ArrayToPointerDecay: + case CK_BitCast: + case CK_LValueBitCast: + case CK_IntegralCast: + case CK_IntegralToPointer: + case CK_PointerToIntegral: + case CK_IntegralToFloating: + case CK_FloatingToIntegral: + case CK_FloatingCast: + case CK_AnyPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_DerivedToBase: + case CK_UncheckedDerivedToBase: + case CK_ObjCObjectLValueCast: { // Delegate to SValuator to process. for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { ExplodedNode* N = *I; @@ -2462,16 +2613,16 @@ void GRExprEngine::VisitCast(CastExpr *CastE, Expr *Ex, ExplodedNode *Pred, } // Various C++ casts that are not handled yet. - case CastExpr::CK_Dynamic: - case CastExpr::CK_ToUnion: - case CastExpr::CK_BaseToDerived: - case CastExpr::CK_NullToMemberPointer: - case CastExpr::CK_BaseToDerivedMemberPointer: - case CastExpr::CK_DerivedToBaseMemberPointer: - case CastExpr::CK_UserDefinedConversion: - case CastExpr::CK_ConstructorConversion: - case CastExpr::CK_VectorSplat: - case CastExpr::CK_MemberPointerToBoolean: { + case CK_Dynamic: + case CK_ToUnion: + case CK_BaseToDerived: + case CK_NullToMemberPointer: + case CK_BaseToDerivedMemberPointer: + case CK_DerivedToBaseMemberPointer: + case CK_UserDefinedConversion: + case CK_ConstructorConversion: + case CK_VectorSplat: + case CK_MemberPointerToBoolean: { SaveAndRestore<bool> OldSink(Builder->BuildSinks); Builder->BuildSinks = true; MakeNode(Dst, CastE, Pred, GetState(Pred)); @@ -2480,11 +2631,12 @@ void GRExprEngine::VisitCast(CastExpr *CastE, Expr *Ex, ExplodedNode *Pred, } } -void GRExprEngine::VisitCompoundLiteralExpr(CompoundLiteralExpr* CL, +void GRExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr* CL, ExplodedNode* Pred, ExplodedNodeSet& Dst, bool asLValue) { - InitListExpr* ILE = cast<InitListExpr>(CL->getInitializer()->IgnoreParens()); + const InitListExpr* ILE + = cast<InitListExpr>(CL->getInitializer()->IgnoreParens()); ExplodedNodeSet Tmp; Visit(ILE, Pred, Tmp); @@ -2502,17 +2654,17 @@ void GRExprEngine::VisitCompoundLiteralExpr(CompoundLiteralExpr* CL, } } -void GRExprEngine::VisitDeclStmt(DeclStmt *DS, ExplodedNode *Pred, +void GRExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, ExplodedNodeSet& Dst) { // The CFG has one DeclStmt per Decl. - Decl* D = *DS->decl_begin(); + const Decl* D = *DS->decl_begin(); if (!D || !isa<VarDecl>(D)) return; const VarDecl* VD = dyn_cast<VarDecl>(D); - Expr* InitEx = const_cast<Expr*>(VD->getInit()); + const Expr* InitEx = VD->getInit(); // FIXME: static variables may have an initializer, but the second // time a function is called those values may not be current. @@ -2534,7 +2686,7 @@ void GRExprEngine::VisitDeclStmt(DeclStmt *DS, ExplodedNode *Pred, Tmp.Add(Pred); ExplodedNodeSet Tmp2; - CheckerVisit(DS, Tmp2, Tmp, true); + CheckerVisit(DS, Tmp2, Tmp, PreVisitStmtCallback); for (ExplodedNodeSet::iterator I=Tmp2.begin(), E=Tmp2.end(); I!=E; ++I) { ExplodedNode *N = *I; @@ -2555,7 +2707,7 @@ void GRExprEngine::VisitDeclStmt(DeclStmt *DS, ExplodedNode *Pred, Builder->getCurrentBlockCount()); } - EvalBind(Dst, DS, DS, *I, state, + EvalBind(Dst, DS, *I, state, loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true); } else { @@ -2565,10 +2717,10 @@ void GRExprEngine::VisitDeclStmt(DeclStmt *DS, ExplodedNode *Pred, } } -void GRExprEngine::VisitCondInit(VarDecl *VD, Stmt *S, +void GRExprEngine::VisitCondInit(const VarDecl *VD, const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet& Dst) { - Expr* InitEx = VD->getInit(); + const Expr* InitEx = VD->getInit(); ExplodedNodeSet Tmp; Visit(InitEx, Pred, Tmp); @@ -2587,7 +2739,7 @@ void GRExprEngine::VisitCondInit(VarDecl *VD, Stmt *S, Builder->getCurrentBlockCount()); } - EvalBind(Dst, S, S, N, state, + EvalBind(Dst, S, N, state, loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true); } } @@ -2599,16 +2751,16 @@ class InitListWLItem { public: llvm::ImmutableList<SVal> Vals; ExplodedNode* N; - InitListExpr::reverse_iterator Itr; + InitListExpr::const_reverse_iterator Itr; InitListWLItem(ExplodedNode* n, llvm::ImmutableList<SVal> vals, - InitListExpr::reverse_iterator itr) + InitListExpr::const_reverse_iterator itr) : Vals(vals), N(n), Itr(itr) {} }; } -void GRExprEngine::VisitInitListExpr(InitListExpr* E, ExplodedNode* Pred, +void GRExprEngine::VisitInitListExpr(const InitListExpr* E, ExplodedNode* Pred, ExplodedNodeSet& Dst) { const GRState* state = GetState(Pred); @@ -2630,7 +2782,7 @@ void GRExprEngine::VisitInitListExpr(InitListExpr* E, ExplodedNode* Pred, llvm::SmallVector<InitListWLItem, 10> WorkList; WorkList.reserve(NumInitElements); WorkList.push_back(InitListWLItem(Pred, StartVals, E->rbegin())); - InitListExpr::reverse_iterator ItrEnd = E->rend(); + InitListExpr::const_reverse_iterator ItrEnd = E->rend(); assert(!(E->rbegin() == E->rend())); // Process the worklist until it is empty. @@ -2641,7 +2793,7 @@ void GRExprEngine::VisitInitListExpr(InitListExpr* E, ExplodedNode* Pred, ExplodedNodeSet Tmp; Visit(*X.Itr, X.N, Tmp); - InitListExpr::reverse_iterator NewItr = X.Itr + 1; + InitListExpr::const_reverse_iterator NewItr = X.Itr + 1; for (ExplodedNodeSet::iterator NI=Tmp.begin(),NE=Tmp.end();NI!=NE;++NI) { // Get the last initializer value. @@ -2673,7 +2825,7 @@ void GRExprEngine::VisitInitListExpr(InitListExpr* E, ExplodedNode* Pred, if (Loc::IsLocType(T) || T->isIntegerType()) { assert (E->getNumInits() == 1); ExplodedNodeSet Tmp; - Expr* Init = E->getInit(0); + const Expr* Init = E->getInit(0); Visit(Init, Pred, Tmp); for (ExplodedNodeSet::iterator I=Tmp.begin(), EI=Tmp.end(); I != EI; ++I) { state = GetState(*I); @@ -2686,7 +2838,7 @@ void GRExprEngine::VisitInitListExpr(InitListExpr* E, ExplodedNode* Pred, } /// VisitSizeOfAlignOfExpr - Transfer function for sizeof(type). -void GRExprEngine::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr* Ex, +void GRExprEngine::VisitSizeOfAlignOfExpr(const SizeOfAlignOfExpr* Ex, ExplodedNode* Pred, ExplodedNodeSet& Dst) { QualType T = Ex->getTypeOfArgument(); @@ -2709,7 +2861,7 @@ void GRExprEngine::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr* Ex, // Get the size by getting the extent of the sub-expression. // First, visit the sub-expression to find its region. - Expr *Arg = Ex->getArgumentExpr(); + const Expr *Arg = Ex->getArgumentExpr(); ExplodedNodeSet Tmp; VisitLValue(Arg, Pred, Tmp); @@ -2751,8 +2903,8 @@ void GRExprEngine::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr* Ex, ValMgr.makeIntVal(amt.getQuantity(), Ex->getType()))); } -void GRExprEngine::VisitOffsetOfExpr(OffsetOfExpr* OOE, ExplodedNode* Pred, - ExplodedNodeSet& Dst) { +void GRExprEngine::VisitOffsetOfExpr(const OffsetOfExpr* OOE, + ExplodedNode* Pred, ExplodedNodeSet& Dst) { Expr::EvalResult Res; if (OOE->Evaluate(Res, getContext()) && Res.Val.isInt()) { const APSInt &IV = Res.Val.getInt(); @@ -2767,7 +2919,8 @@ void GRExprEngine::VisitOffsetOfExpr(OffsetOfExpr* OOE, ExplodedNode* Pred, Dst.Add(Pred); } -void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, +void GRExprEngine::VisitUnaryOperator(const UnaryOperator* U, + ExplodedNode* Pred, ExplodedNodeSet& Dst, bool asLValue) { switch (U->getOpcode()) { @@ -2775,9 +2928,9 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, default: break; - case UnaryOperator::Deref: { + case UO_Deref: { - Expr* Ex = U->getSubExpr()->IgnoreParens(); + const Expr* Ex = U->getSubExpr()->IgnoreParens(); ExplodedNodeSet Tmp; Visit(Ex, Pred, Tmp); @@ -2796,9 +2949,9 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, return; } - case UnaryOperator::Real: { + case UO_Real: { - Expr* Ex = U->getSubExpr()->IgnoreParens(); + const Expr* Ex = U->getSubExpr()->IgnoreParens(); ExplodedNodeSet Tmp; Visit(Ex, Pred, Tmp); @@ -2811,7 +2964,7 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, continue; } - // For all other types, UnaryOperator::Real is an identity operation. + // For all other types, UO_Real is an identity operation. assert (U->getType() == Ex->getType()); const GRState* state = GetState(*I); MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); @@ -2820,9 +2973,9 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, return; } - case UnaryOperator::Imag: { + case UO_Imag: { - Expr* Ex = U->getSubExpr()->IgnoreParens(); + const Expr* Ex = U->getSubExpr()->IgnoreParens(); ExplodedNodeSet Tmp; Visit(Ex, Pred, Tmp); @@ -2834,8 +2987,7 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, continue; } - // For all other types, UnaryOperator::Float returns 0. - assert (Ex->getType()->isIntegerType()); + // For all other types, UO_Imag returns 0. const GRState* state = GetState(*I); SVal X = ValMgr.makeZeroVal(Ex->getType()); MakeNode(Dst, U, *I, state->BindExpr(U, X)); @@ -2843,38 +2995,22 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, return; } - - case UnaryOperator::OffsetOf: { - Expr::EvalResult Res; - if (U->Evaluate(Res, getContext()) && Res.Val.isInt()) { - const APSInt &IV = Res.Val.getInt(); - assert(IV.getBitWidth() == getContext().getTypeSize(U->getType())); - assert(U->getType()->isIntegerType()); - assert(IV.isSigned() == U->getType()->isSignedIntegerType()); - SVal X = ValMgr.makeIntVal(IV); - MakeNode(Dst, U, Pred, GetState(Pred)->BindExpr(U, X)); - return; - } - // FIXME: Handle the case where __builtin_offsetof is not a constant. - Dst.Add(Pred); - return; - } - case UnaryOperator::Plus: assert(!asLValue); // FALL-THROUGH. - case UnaryOperator::Extension: { + case UO_Plus: assert(!asLValue); // FALL-THROUGH. + case UO_Extension: { // Unary "+" is a no-op, similar to a parentheses. We still have places // where it may be a block-level expression, so we need to // generate an extra node that just propagates the value of the // subexpression. - Expr* Ex = U->getSubExpr()->IgnoreParens(); + const Expr* Ex = U->getSubExpr()->IgnoreParens(); ExplodedNodeSet Tmp; if (asLValue) - VisitLValue(Ex, Pred, Tmp); + VisitLValue(Ex, Pred, Tmp); else - Visit(Ex, Pred, Tmp); + Visit(Ex, Pred, Tmp); for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { const GRState* state = GetState(*I); @@ -2884,10 +3020,10 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, return; } - case UnaryOperator::AddrOf: { + case UO_AddrOf: { assert(!asLValue); - Expr* Ex = U->getSubExpr()->IgnoreParens(); + const Expr* Ex = U->getSubExpr()->IgnoreParens(); ExplodedNodeSet Tmp; VisitLValue(Ex, Pred, Tmp); @@ -2901,12 +3037,12 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, return; } - case UnaryOperator::LNot: - case UnaryOperator::Minus: - case UnaryOperator::Not: { + case UO_LNot: + case UO_Minus: + case UO_Not: { assert (!asLValue); - Expr* Ex = U->getSubExpr()->IgnoreParens(); + const Expr* Ex = U->getSubExpr()->IgnoreParens(); ExplodedNodeSet Tmp; Visit(Ex, Pred, Tmp); @@ -2937,17 +3073,17 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, assert(false && "Invalid Opcode."); break; - case UnaryOperator::Not: + case UO_Not: // FIXME: Do we need to handle promotions? state = state->BindExpr(U, EvalComplement(cast<NonLoc>(V))); break; - case UnaryOperator::Minus: + case UO_Minus: // FIXME: Do we need to handle promotions? state = state->BindExpr(U, EvalMinus(cast<NonLoc>(V))); break; - case UnaryOperator::LNot: + case UO_LNot: // C99 6.5.3.3: "The expression !E is equivalent to (0==E)." // @@ -2957,12 +3093,12 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, if (isa<Loc>(V)) { Loc X = ValMgr.makeNull(); - Result = EvalBinOp(state, BinaryOperator::EQ, cast<Loc>(V), X, + Result = EvalBinOp(state, BO_EQ, cast<Loc>(V), X, U->getType()); } else { nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); - Result = EvalBinOp(state, BinaryOperator::EQ, cast<NonLoc>(V), X, + Result = EvalBinOp(state, BO_EQ, cast<NonLoc>(V), X, U->getType()); } @@ -2982,7 +3118,7 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, assert (U->isIncrementDecrementOp()); ExplodedNodeSet Tmp; - Expr* Ex = U->getSubExpr()->IgnoreParens(); + const Expr* Ex = U->getSubExpr()->IgnoreParens(); VisitLValue(Ex, Pred, Tmp); for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { @@ -3007,8 +3143,8 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, DefinedSVal V2 = cast<DefinedSVal>(V2_untested); // Handle all other values. - BinaryOperator::Opcode Op = U->isIncrementOp() ? BinaryOperator::Add - : BinaryOperator::Sub; + BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add + : BO_Sub; // If the UnaryOperator has non-location type, use its type to create the // constant value. If the UnaryOperator has location type, create the @@ -3057,14 +3193,14 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, } } -void GRExprEngine::VisitAsmStmt(AsmStmt* A, ExplodedNode* Pred, +void GRExprEngine::VisitAsmStmt(const AsmStmt* A, ExplodedNode* Pred, ExplodedNodeSet& Dst) { VisitAsmStmtHelperOutputs(A, A->begin_outputs(), A->end_outputs(), Pred, Dst); } -void GRExprEngine::VisitAsmStmtHelperOutputs(AsmStmt* A, - AsmStmt::outputs_iterator I, - AsmStmt::outputs_iterator E, +void GRExprEngine::VisitAsmStmtHelperOutputs(const AsmStmt* A, + AsmStmt::const_outputs_iterator I, + AsmStmt::const_outputs_iterator E, ExplodedNode* Pred, ExplodedNodeSet& Dst) { if (I == E) { VisitAsmStmtHelperInputs(A, A->begin_inputs(), A->end_inputs(), Pred, Dst); @@ -3080,9 +3216,9 @@ void GRExprEngine::VisitAsmStmtHelperOutputs(AsmStmt* A, VisitAsmStmtHelperOutputs(A, I, E, *NI, Dst); } -void GRExprEngine::VisitAsmStmtHelperInputs(AsmStmt* A, - AsmStmt::inputs_iterator I, - AsmStmt::inputs_iterator E, +void GRExprEngine::VisitAsmStmtHelperInputs(const AsmStmt* A, + AsmStmt::const_inputs_iterator I, + AsmStmt::const_inputs_iterator E, ExplodedNode* Pred, ExplodedNodeSet& Dst) { if (I == E) { @@ -3096,7 +3232,7 @@ void GRExprEngine::VisitAsmStmtHelperInputs(AsmStmt* A, const GRState* state = GetState(Pred); - for (AsmStmt::outputs_iterator OI = A->begin_outputs(), + for (AsmStmt::const_outputs_iterator OI = A->begin_outputs(), OE = A->end_outputs(); OI != OE; ++OI) { SVal X = state->getSVal(*OI); @@ -3119,10 +3255,10 @@ void GRExprEngine::VisitAsmStmtHelperInputs(AsmStmt* A, VisitAsmStmtHelperInputs(A, I, E, *NI, Dst); } -void GRExprEngine::VisitReturnStmt(ReturnStmt *RS, ExplodedNode *Pred, +void GRExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred, ExplodedNodeSet &Dst) { ExplodedNodeSet Src; - if (Expr *RetE = RS->getRetValue()) { + if (const Expr *RetE = RS->getRetValue()) { // Record the returned expression in the state. It will be used in // ProcessCallExit to bind the return value to the call expr. { @@ -3141,7 +3277,7 @@ void GRExprEngine::VisitReturnStmt(ReturnStmt *RS, ExplodedNode *Pred, } ExplodedNodeSet CheckedSet; - CheckerVisit(RS, CheckedSet, Src, true); + CheckerVisit(RS, CheckedSet, Src, PreVisitStmtCallback); for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) { @@ -3167,7 +3303,7 @@ void GRExprEngine::VisitReturnStmt(ReturnStmt *RS, ExplodedNode *Pred, // Transfer functions: Binary operators. //===----------------------------------------------------------------------===// -void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, +void GRExprEngine::VisitBinaryOperator(const BinaryOperator* B, ExplodedNode* Pred, ExplodedNodeSet& Dst, bool asLValue) { @@ -3194,7 +3330,7 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, Visit(RHS, *I1, Tmp2); ExplodedNodeSet CheckedSet; - CheckerVisit(B, CheckedSet, Tmp2, true); + CheckerVisit(B, CheckedSet, Tmp2, PreVisitStmtCallback); // With both the LHS and RHS evaluated, process the operation itself. @@ -3207,13 +3343,13 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, BinaryOperator::Opcode Op = B->getOpcode(); - if (Op == BinaryOperator::Assign) { + if (Op == BO_Assign) { // EXPERIMENTAL: "Conjured" symbols. // FIXME: Handle structs. QualType T = RHS->getType(); - if ((RightV.isUnknown()||!getConstraintManager().canReasonAbout(RightV)) - && (Loc::IsLocType(T) || (T->isScalarType()&&T->isIntegerType()))) { + if (RightV.isUnknown() ||!getConstraintManager().canReasonAbout(RightV)) + { unsigned Count = Builder->getCurrentBlockCount(); RightV = ValMgr.getConjuredSymbolVal(NULL, B->getRHS(), Count); } @@ -3253,16 +3389,16 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, switch (Op) { default: assert(0 && "Invalid opcode for compound assignment."); - case BinaryOperator::MulAssign: Op = BinaryOperator::Mul; break; - case BinaryOperator::DivAssign: Op = BinaryOperator::Div; break; - case BinaryOperator::RemAssign: Op = BinaryOperator::Rem; break; - case BinaryOperator::AddAssign: Op = BinaryOperator::Add; break; - case BinaryOperator::SubAssign: Op = BinaryOperator::Sub; break; - case BinaryOperator::ShlAssign: Op = BinaryOperator::Shl; break; - case BinaryOperator::ShrAssign: Op = BinaryOperator::Shr; break; - case BinaryOperator::AndAssign: Op = BinaryOperator::And; break; - case BinaryOperator::XorAssign: Op = BinaryOperator::Xor; break; - case BinaryOperator::OrAssign: Op = BinaryOperator::Or; break; + case BO_MulAssign: Op = BO_Mul; break; + case BO_DivAssign: Op = BO_Div; break; + case BO_RemAssign: Op = BO_Rem; break; + case BO_AddAssign: Op = BO_Add; break; + case BO_SubAssign: Op = BO_Sub; break; + case BO_ShlAssign: Op = BO_Shl; break; + case BO_ShrAssign: Op = BO_Shr; break; + case BO_AndAssign: Op = BO_And; break; + case BO_XorAssign: Op = BO_Xor; break; + case BO_OrAssign: Op = BO_Or; break; } // Perform a load (the LHS). This performs the checks for @@ -3300,10 +3436,8 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, SVal LHSVal; - if ((Result.isUnknown() || - !getConstraintManager().canReasonAbout(Result)) - && (Loc::IsLocType(CTy) - || (CTy->isScalarType() && CTy->isIntegerType()))) { + if (Result.isUnknown() || + !getConstraintManager().canReasonAbout(Result)) { unsigned Count = Builder->getCurrentBlockCount(); @@ -3327,7 +3461,7 @@ void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, } } - CheckerVisit(B, Dst, Tmp3, false); + CheckerVisit(B, Dst, Tmp3, PostVisitStmtCallback); } //===----------------------------------------------------------------------===// @@ -3457,7 +3591,7 @@ struct DOTGraphTraits<ExplodedNode*> : Out << "Edge: (B" << E.getSrc()->getBlockID() << ", B" << E.getDst()->getBlockID() << ')'; - if (Stmt* T = E.getSrc()->getTerminator()) { + if (const Stmt* T = E.getSrc()->getTerminator()) { SourceLocation SLoc = T->getLocStart(); @@ -3473,15 +3607,15 @@ struct DOTGraphTraits<ExplodedNode*> : } if (isa<SwitchStmt>(T)) { - Stmt* Label = E.getDst()->getLabel(); + const Stmt* Label = E.getDst()->getLabel(); if (Label) { - if (CaseStmt* C = dyn_cast<CaseStmt>(Label)) { + if (const CaseStmt* C = dyn_cast<CaseStmt>(Label)) { Out << "\\lcase "; LangOptions LO; // FIXME. C->getLHS()->printPretty(Out, 0, PrintingPolicy(LO)); - if (Stmt* RHS = C->getRHS()) { + if (const Stmt* RHS = C->getRHS()) { Out << " .. "; RHS->printPretty(Out, 0, PrintingPolicy(LO)); } diff --git a/lib/Checker/GRExprEngineExperimentalChecks.cpp b/lib/Checker/GRExprEngineExperimentalChecks.cpp index d138e81..84262b0 100644 --- a/lib/Checker/GRExprEngineExperimentalChecks.cpp +++ b/lib/Checker/GRExprEngineExperimentalChecks.cpp @@ -18,13 +18,14 @@ using namespace clang; -void clang::RegisterExperimentalChecks(GRExprEngine &Eng) { +void clang::RegisterExperimentalChecks(GRExprEngine &Eng) { // These are checks that never belong as internal checks // within GRExprEngine. - RegisterPthreadLockChecker(Eng); + RegisterCStringChecker(Eng); RegisterMallocChecker(Eng); + RegisterPthreadLockChecker(Eng); RegisterStreamChecker(Eng); - RegisterCStringChecker(Eng); + RegisterUnreachableCodeChecker(Eng); } void clang::RegisterExperimentalInternalChecks(GRExprEngine &Eng) { @@ -34,11 +35,10 @@ void clang::RegisterExperimentalInternalChecks(GRExprEngine &Eng) { // Note that this must be registered after ReturnStackAddresEngsChecker. RegisterReturnPointerRangeChecker(Eng); + RegisterArrayBoundChecker(Eng); + RegisterCastSizeChecker(Eng); + RegisterCastToStructChecker(Eng); RegisterFixedAddressChecker(Eng); - RegisterPointerSubChecker(Eng); RegisterPointerArithChecker(Eng); - RegisterCastToStructChecker(Eng); - RegisterCastSizeChecker(Eng); - RegisterArrayBoundChecker(Eng); - + RegisterPointerSubChecker(Eng); } diff --git a/lib/Checker/GRExprEngineExperimentalChecks.h b/lib/Checker/GRExprEngineExperimentalChecks.h index 7d1eb77..7b5b0ed 100644 --- a/lib/Checker/GRExprEngineExperimentalChecks.h +++ b/lib/Checker/GRExprEngineExperimentalChecks.h @@ -20,10 +20,11 @@ namespace clang { class GRExprEngine; void RegisterCStringChecker(GRExprEngine &Eng); -void RegisterPthreadLockChecker(GRExprEngine &Eng); +void RegisterIdempotentOperationChecker(GRExprEngine &Eng); void RegisterMallocChecker(GRExprEngine &Eng); +void RegisterPthreadLockChecker(GRExprEngine &Eng); void RegisterStreamChecker(GRExprEngine &Eng); -void RegisterIdempotentOperationChecker(GRExprEngine &Eng); +void RegisterUnreachableCodeChecker(GRExprEngine &Eng); } // end clang namespace #endif diff --git a/lib/Checker/GRState.cpp b/lib/Checker/GRState.cpp index 9e584b5..d38ae21 100644 --- a/lib/Checker/GRState.cpp +++ b/lib/Checker/GRState.cpp @@ -14,6 +14,7 @@ #include "clang/Analysis/CFG.h" #include "clang/Checker/PathSensitive/GRStateTrait.h" #include "clang/Checker/PathSensitive/GRState.h" +#include "clang/Checker/PathSensitive/GRSubEngine.h" #include "clang/Checker/PathSensitive/GRTransferFuncs.h" #include "llvm/Support/raw_ostream.h" @@ -51,22 +52,105 @@ GRStateManager::RemoveDeadBindings(const GRState* state, state, RegionRoots); // Clean up the store. - const GRState *s = StoreMgr->RemoveDeadBindings(NewState, LCtx, - SymReaper, RegionRoots); + NewState.St = StoreMgr->RemoveDeadBindings(NewState.St, LCtx, + SymReaper, RegionRoots); + state = getPersistentState(NewState); + return ConstraintMgr->RemoveDeadBindings(state, SymReaper); +} + +const GRState *GRStateManager::MarshalState(const GRState *state, + const StackFrameContext *InitLoc) { + // make up an empty state for now. + GRState State(this, + EnvMgr.getInitialEnvironment(), + StoreMgr->getInitialStore(InitLoc), + GDMFactory.GetEmptyMap()); + + return getPersistentState(State); +} + +const GRState *GRState::bindCompoundLiteral(const CompoundLiteralExpr* CL, + const LocationContext *LC, + SVal V) const { + Store new_store = + getStateManager().StoreMgr->BindCompoundLiteral(St, CL, LC, V); + return makeWithStore(new_store); +} + +const GRState *GRState::bindDecl(const VarRegion* VR, SVal IVal) const { + Store new_store = getStateManager().StoreMgr->BindDecl(St, VR, IVal); + return makeWithStore(new_store); +} + +const GRState *GRState::bindDeclWithNoInit(const VarRegion* VR) const { + Store new_store = getStateManager().StoreMgr->BindDeclWithNoInit(St, VR); + return makeWithStore(new_store); +} + +const GRState *GRState::bindLoc(Loc LV, SVal V) const { + GRStateManager &Mgr = getStateManager(); + Store new_store = Mgr.StoreMgr->Bind(St, LV, V); + const GRState *new_state = makeWithStore(new_store); - return ConstraintMgr->RemoveDeadBindings(s, SymReaper); + const MemRegion *MR = LV.getAsRegion(); + if (MR) + return Mgr.getOwningEngine().ProcessRegionChange(new_state, MR); + + return new_state; +} + +const GRState *GRState::bindDefault(SVal loc, SVal V) const { + GRStateManager &Mgr = getStateManager(); + const MemRegion *R = cast<loc::MemRegionVal>(loc).getRegion(); + Store new_store = Mgr.StoreMgr->BindDefault(St, R, V); + const GRState *new_state = makeWithStore(new_store); + return Mgr.getOwningEngine().ProcessRegionChange(new_state, R); +} + +const GRState *GRState::InvalidateRegions(const MemRegion * const *Begin, + const MemRegion * const *End, + const Expr *E, unsigned Count, + StoreManager::InvalidatedSymbols *IS, + bool invalidateGlobals) const { + GRStateManager &Mgr = getStateManager(); + GRSubEngine &Eng = Mgr.getOwningEngine(); + + if (Eng.WantsRegionChangeUpdate(this)) { + StoreManager::InvalidatedRegions Regions; + + Store new_store = Mgr.StoreMgr->InvalidateRegions(St, Begin, End, + E, Count, IS, + invalidateGlobals, + &Regions); + const GRState *new_state = makeWithStore(new_store); + + return Eng.ProcessRegionChanges(new_state, + &Regions.front(), + &Regions.back()+1); + } + + Store new_store = Mgr.StoreMgr->InvalidateRegions(St, Begin, End, + E, Count, IS, + invalidateGlobals, + NULL); + return makeWithStore(new_store); } const GRState *GRState::unbindLoc(Loc LV) const { + assert(!isa<loc::MemRegionVal>(LV) && "Use InvalidateRegion instead."); + Store OldStore = getStore(); Store NewStore = getStateManager().StoreMgr->Remove(OldStore, LV); if (NewStore == OldStore) return this; - GRState NewSt = *this; - NewSt.St = NewStore; - return getStateManager().getPersistentState(NewSt); + return makeWithStore(NewStore); +} + +const GRState *GRState::EnterStackFrame(const StackFrameContext *frame) const { + Store new_store = getStateManager().StoreMgr->EnterStackFrame(this, frame); + return makeWithStore(new_store); } SVal GRState::getSValAsScalarOrLoc(const MemRegion *R) const { @@ -77,7 +161,7 @@ SVal GRState::getSValAsScalarOrLoc(const MemRegion *R) const { return UnknownVal(); if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { - QualType T = TR->getValueType(getStateManager().getContext()); + QualType T = TR->getValueType(); if (Loc::IsLocType(T) || T->isIntegerType()) return getSVal(R); } @@ -85,9 +169,45 @@ SVal GRState::getSValAsScalarOrLoc(const MemRegion *R) const { return UnknownVal(); } +SVal GRState::getSimplifiedSVal(Loc location, QualType T) const { + SVal V = getSVal(cast<Loc>(location), T); + + // If 'V' is a symbolic value that is *perfectly* constrained to + // be a constant value, use that value instead to lessen the burden + // on later analysis stages (so we have less symbolic values to reason + // about). + if (!T.isNull()) { + if (SymbolRef sym = V.getAsSymbol()) { + if (const llvm::APSInt *Int = getSymVal(sym)) { + // FIXME: Because we don't correctly model (yet) sign-extension + // and truncation of symbolic values, we need to convert + // the integer value to the correct signedness and bitwidth. + // + // This shows up in the following: + // + // char foo(); + // unsigned x = foo(); + // if (x == 54) + // ... + // + // The symbolic value stored to 'x' is actually the conjured + // symbol for the call to foo(); the type of that symbol is 'char', + // not unsigned. + const llvm::APSInt &NewV = getBasicVals().Convert(T, *Int); + + if (isa<Loc>(V)) + return loc::ConcreteInt(NewV); + else + return nonloc::ConcreteInt(NewV); + } + } + } + + return V; +} -const GRState *GRState::BindExpr(const Stmt* Ex, SVal V, bool Invalidate) const{ - Environment NewEnv = getStateManager().EnvMgr.BindExpr(Env, Ex, V, +const GRState *GRState::BindExpr(const Stmt* S, SVal V, bool Invalidate) const{ + Environment NewEnv = getStateManager().EnvMgr.bindExpr(Env, S, V, Invalidate); if (NewEnv == Env) return this; @@ -97,6 +217,63 @@ const GRState *GRState::BindExpr(const Stmt* Ex, SVal V, bool Invalidate) const{ return getStateManager().getPersistentState(NewSt); } +const GRState *GRState::bindExprAndLocation(const Stmt *S, SVal location, + SVal V) const { + Environment NewEnv = + getStateManager().EnvMgr.bindExprAndLocation(Env, S, location, V); + + if (NewEnv == Env) + return this; + + GRState NewSt = *this; + NewSt.Env = NewEnv; + return getStateManager().getPersistentState(NewSt); +} + +const GRState *GRState::AssumeInBound(DefinedOrUnknownSVal Idx, + DefinedOrUnknownSVal UpperBound, + bool Assumption) const { + if (Idx.isUnknown() || UpperBound.isUnknown()) + return this; + + // Build an expression for 0 <= Idx < UpperBound. + // This is the same as Idx + MIN < UpperBound + MIN, if overflow is allowed. + // FIXME: This should probably be part of SValuator. + GRStateManager &SM = getStateManager(); + ValueManager &VM = SM.getValueManager(); + SValuator &SV = VM.getSValuator(); + ASTContext &Ctx = VM.getContext(); + + // Get the offset: the minimum value of the array index type. + BasicValueFactory &BVF = VM.getBasicValueFactory(); + // FIXME: This should be using ValueManager::ArrayIndexTy...somehow. + QualType IndexTy = Ctx.IntTy; + nonloc::ConcreteInt Min = BVF.getMinValue(IndexTy); + + // Adjust the index. + SVal NewIdx = SV.EvalBinOpNN(this, BO_Add, + cast<NonLoc>(Idx), Min, IndexTy); + if (NewIdx.isUnknownOrUndef()) + return this; + + // Adjust the upper bound. + SVal NewBound = SV.EvalBinOpNN(this, BO_Add, + cast<NonLoc>(UpperBound), Min, IndexTy); + if (NewBound.isUnknownOrUndef()) + return this; + + // Build the actual comparison. + SVal InBound = SV.EvalBinOpNN(this, BO_LT, + cast<NonLoc>(NewIdx), cast<NonLoc>(NewBound), + Ctx.IntTy); + if (InBound.isUnknownOrUndef()) + return this; + + // Finally, let the constraint manager take care of it. + ConstraintManager &CM = SM.getConstraintManager(); + return CM.Assume(this, cast<DefinedSVal>(InBound), Assumption); +} + const GRState* GRStateManager::getInitialState(const LocationContext *InitLoc) { GRState State(this, EnvMgr.getInitialEnvironment(), @@ -131,6 +308,11 @@ const GRState* GRState::makeWithStore(Store store) const { // State pretty-printing. //===----------------------------------------------------------------------===// +static bool IsEnvLoc(const Stmt *S) { + // FIXME: This is a layering violation. Should be in environment. + return (bool) (((uintptr_t) S) & 0x1); +} + void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl, const char* sep) const { // Print the store. @@ -140,8 +322,9 @@ void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl, // Print Subexpression bindings. bool isFirst = true; + // FIXME: All environment printing should be moved inside Environment. for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { - if (C.isBlkExpr(I.getKey())) + if (C.isBlkExpr(I.getKey()) || IsEnvLoc(I.getKey())) continue; if (isFirst) { @@ -174,6 +357,27 @@ void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl, I.getKey()->printPretty(Out, 0, PrintingPolicy(LO)); Out << " : " << I.getData(); } + + // Print locations. + isFirst = true; + + for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { + if (!IsEnvLoc(I.getKey())) + continue; + + if (isFirst) { + Out << nl << nl << "Load/store locations:" << nl; + isFirst = false; + } + else { Out << nl; } + + const Stmt *S = (Stmt*) (((uintptr_t) I.getKey()) & ((uintptr_t) ~0x1)); + + Out << " (" << (void*) S << ") "; + LangOptions LO; // FIXME. + S->printPretty(Out, 0, PrintingPolicy(LO)); + Out << " : " << I.getData(); + } Mgr.getConstraintManager().print(this, Out, nl, sep); diff --git a/lib/Checker/IdempotentOperationChecker.cpp b/lib/Checker/IdempotentOperationChecker.cpp index 6ed1841..6411c79 100644 --- a/lib/Checker/IdempotentOperationChecker.cpp +++ b/lib/Checker/IdempotentOperationChecker.cpp @@ -36,25 +36,26 @@ // != | 0 | | | | | | //===----------------------------------------------------------------------===// // -// Ways to reduce false positives (that need to be implemented): -// - Don't flag downsizing casts -// - Improved handling of static/global variables -// - Per-block marking of incomplete analysis -// - Handling ~0 values -// - False positives involving silencing unused variable warnings -// -// Other things TODO: +// Things TODO: // - Improved error messages // - Handle mixed assumptions (which assumptions can belong together?) // - Finer grained false positive control (levels) +// - Handling ~0 values #include "GRExprEngineExperimentalChecks.h" +#include "clang/Analysis/CFGStmtMap.h" +#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h" +#include "clang/Checker/BugReporter/BugReporter.h" #include "clang/Checker/BugReporter/BugType.h" +#include "clang/Checker/PathSensitive/CheckerHelpers.h" #include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Checker/PathSensitive/GRCoreEngine.h" #include "clang/Checker/PathSensitive/SVals.h" #include "clang/AST/Stmt.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/Support/ErrorHandling.h" +#include <deque> using namespace clang; @@ -64,38 +65,41 @@ class IdempotentOperationChecker public: static void *getTag(); void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); - void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B, - bool hasWorkRemaining); + void PostVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); + void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B, GRExprEngine &Eng); private: // Our assumption about a particular operation. - enum Assumption { Possible, Impossible, Equal, LHSis1, RHSis1, LHSis0, + enum Assumption { Possible = 0, Impossible, Equal, LHSis1, RHSis1, LHSis0, RHSis0 }; void UpdateAssumption(Assumption &A, const Assumption &New); - /// contains* - Useful recursive methods to see if a statement contains an - /// element somewhere. Used in static analysis to reduce false positives. - static bool containsMacro(const Stmt *S); - static bool containsEnum(const Stmt *S); - static bool containsBuiltinOffsetOf(const Stmt *S); - static bool containsZeroConstant(const Stmt *S); - static bool containsOneConstant(const Stmt *S); - template <class T> static bool containsStmt(const Stmt *S) { - if (isa<T>(S)) - return true; - - for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); - ++I) - if (const Stmt *child = *I) - if (containsStmt<T>(child)) - return true; - - return false; - } - - // Hash table - typedef llvm::DenseMap<const BinaryOperator *, Assumption> AssumptionMap; + // False positive reduction methods + static bool isSelfAssign(const Expr *LHS, const Expr *RHS); + static bool isUnused(const Expr *E, AnalysisContext *AC); + //static bool isTruncationExtensionAssignment(const Expr *LHS, + // const Expr *RHS); + static bool PathWasCompletelyAnalyzed(const CFG *C, + const CFGBlock *CB, + const GRCoreEngine &CE); + static bool CanVary(const Expr *Ex, + AnalysisContext *AC); + static bool isConstantOrPseudoConstant(const DeclRefExpr *DR, + AnalysisContext *AC); + static bool containsNonLocalVarDecl(const Stmt *S); + + // Hash table and related data structures + struct BinaryOperatorData { + BinaryOperatorData() : assumption(Possible), analysisContext(0) {} + + Assumption assumption; + AnalysisContext *analysisContext; + ExplodedNodeSet explodedNodes; // Set of ExplodedNodes that refer to a + // BinaryOperator + }; + typedef llvm::DenseMap<const BinaryOperator *, BinaryOperatorData> + AssumptionMap; AssumptionMap hash; }; } @@ -112,30 +116,38 @@ void clang::RegisterIdempotentOperationChecker(GRExprEngine &Eng) { void IdempotentOperationChecker::PreVisitBinaryOperator( CheckerContext &C, const BinaryOperator *B) { - // Find or create an entry in the hash for this BinaryOperator instance - AssumptionMap::iterator i = hash.find(B); - Assumption &A = i == hash.end() ? hash[B] : i->second; - - // If we had to create an entry, initialise the value to Possible - if (i == hash.end()) - A = Possible; + // Find or create an entry in the hash for this BinaryOperator instance. + // If we haven't done a lookup before, it will get default initialized to + // 'Possible'. At this stage we do not store the ExplodedNode, as it has not + // been created yet. + BinaryOperatorData &Data = hash[B]; + Assumption &A = Data.assumption; + AnalysisContext *AC = C.getCurrentAnalysisContext(); + Data.analysisContext = AC; // If we already have visited this node on a path that does not contain an // idempotent operation, return immediately. if (A == Impossible) return; - // Skip binary operators containing common false positives - if (containsMacro(B) || containsEnum(B) || containsStmt<SizeOfAlignOfExpr>(B) - || containsZeroConstant(B) || containsOneConstant(B) - || containsBuiltinOffsetOf(B)) { - A = Impossible; - return; - } - + // Retrieve both sides of the operator and determine if they can vary (which + // may mean this is a false positive. const Expr *LHS = B->getLHS(); const Expr *RHS = B->getRHS(); + // At this stage we can calculate whether each side contains a false positive + // that applies to all operators. We only need to calculate this the first + // time. + bool LHSContainsFalsePositive = false, RHSContainsFalsePositive = false; + if (A == Possible) { + // An expression contains a false positive if it can't vary, or if it + // contains a known false positive VarDecl. + LHSContainsFalsePositive = !CanVary(LHS, AC) + || containsNonLocalVarDecl(LHS); + RHSContainsFalsePositive = !CanVary(RHS, AC) + || containsNonLocalVarDecl(RHS); + } + const GRState *state = C.getState(); SVal LHSVal = state->getSVal(LHS); @@ -154,16 +166,16 @@ void IdempotentOperationChecker::PreVisitBinaryOperator( break; // Fall through intentional - case BinaryOperator::AddAssign: - case BinaryOperator::SubAssign: - case BinaryOperator::MulAssign: - case BinaryOperator::DivAssign: - case BinaryOperator::AndAssign: - case BinaryOperator::OrAssign: - case BinaryOperator::XorAssign: - case BinaryOperator::ShlAssign: - case BinaryOperator::ShrAssign: - case BinaryOperator::Assign: + case BO_AddAssign: + case BO_SubAssign: + case BO_MulAssign: + case BO_DivAssign: + case BO_AndAssign: + case BO_OrAssign: + case BO_XorAssign: + case BO_ShlAssign: + case BO_ShrAssign: + case BO_Assign: // Assign statements have one extra level of indirection if (!isa<Loc>(LHSVal)) { A = Impossible; @@ -181,20 +193,37 @@ void IdempotentOperationChecker::PreVisitBinaryOperator( break; // We don't care about any other operators. // Fall through intentional - case BinaryOperator::SubAssign: - case BinaryOperator::DivAssign: - case BinaryOperator::AndAssign: - case BinaryOperator::OrAssign: - case BinaryOperator::XorAssign: - case BinaryOperator::Assign: - case BinaryOperator::Sub: - case BinaryOperator::Div: - case BinaryOperator::And: - case BinaryOperator::Or: - case BinaryOperator::Xor: - case BinaryOperator::LOr: - case BinaryOperator::LAnd: - if (LHSVal != RHSVal) + case BO_Assign: + // x Assign x can be used to silence unused variable warnings intentionally. + // If this is a self assignment and the variable is referenced elsewhere, + // then it is a false positive. + if (isSelfAssign(LHS, RHS)) { + if (!isUnused(LHS, AC)) { + UpdateAssumption(A, Equal); + return; + } + else { + A = Impossible; + return; + } + } + + case BO_SubAssign: + case BO_DivAssign: + case BO_AndAssign: + case BO_OrAssign: + case BO_XorAssign: + case BO_Sub: + case BO_Div: + case BO_And: + case BO_Or: + case BO_Xor: + case BO_LOr: + case BO_LAnd: + case BO_EQ: + case BO_NE: + if (LHSVal != RHSVal || LHSContainsFalsePositive + || RHSContainsFalsePositive) break; UpdateAssumption(A, Equal); return; @@ -206,13 +235,13 @@ void IdempotentOperationChecker::PreVisitBinaryOperator( break; // We don't care about any other operators. // Fall through intentional - case BinaryOperator::MulAssign: - case BinaryOperator::DivAssign: - case BinaryOperator::Mul: - case BinaryOperator::Div: - case BinaryOperator::LOr: - case BinaryOperator::LAnd: - if (!RHSVal.isConstant(1)) + case BO_MulAssign: + case BO_DivAssign: + case BO_Mul: + case BO_Div: + case BO_LOr: + case BO_LAnd: + if (!RHSVal.isConstant(1) || RHSContainsFalsePositive) break; UpdateAssumption(A, RHSis1); return; @@ -224,11 +253,11 @@ void IdempotentOperationChecker::PreVisitBinaryOperator( break; // We don't care about any other operators. // Fall through intentional - case BinaryOperator::MulAssign: - case BinaryOperator::Mul: - case BinaryOperator::LOr: - case BinaryOperator::LAnd: - if (!LHSVal.isConstant(1)) + case BO_MulAssign: + case BO_Mul: + case BO_LOr: + case BO_LAnd: + if (!LHSVal.isConstant(1) || LHSContainsFalsePositive) break; UpdateAssumption(A, LHSis1); return; @@ -240,23 +269,23 @@ void IdempotentOperationChecker::PreVisitBinaryOperator( break; // We don't care about any other operators. // Fall through intentional - case BinaryOperator::AddAssign: - case BinaryOperator::SubAssign: - case BinaryOperator::MulAssign: - case BinaryOperator::AndAssign: - case BinaryOperator::OrAssign: - case BinaryOperator::XorAssign: - case BinaryOperator::Add: - case BinaryOperator::Sub: - case BinaryOperator::Mul: - case BinaryOperator::And: - case BinaryOperator::Or: - case BinaryOperator::Xor: - case BinaryOperator::Shl: - case BinaryOperator::Shr: - case BinaryOperator::LOr: - case BinaryOperator::LAnd: - if (!RHSVal.isConstant(0)) + case BO_AddAssign: + case BO_SubAssign: + case BO_MulAssign: + case BO_AndAssign: + case BO_OrAssign: + case BO_XorAssign: + case BO_Add: + case BO_Sub: + case BO_Mul: + case BO_And: + case BO_Or: + case BO_Xor: + case BO_Shl: + case BO_Shr: + case BO_LOr: + case BO_LAnd: + if (!RHSVal.isConstant(0) || RHSContainsFalsePositive) break; UpdateAssumption(A, RHSis0); return; @@ -268,27 +297,27 @@ void IdempotentOperationChecker::PreVisitBinaryOperator( break; // We don't care about any other operators. // Fall through intentional - //case BinaryOperator::AddAssign: // Common false positive - case BinaryOperator::SubAssign: // Check only if unsigned - case BinaryOperator::MulAssign: - case BinaryOperator::DivAssign: - case BinaryOperator::AndAssign: - //case BinaryOperator::OrAssign: // Common false positive - //case BinaryOperator::XorAssign: // Common false positive - case BinaryOperator::ShlAssign: - case BinaryOperator::ShrAssign: - case BinaryOperator::Add: - case BinaryOperator::Sub: - case BinaryOperator::Mul: - case BinaryOperator::Div: - case BinaryOperator::And: - case BinaryOperator::Or: - case BinaryOperator::Xor: - case BinaryOperator::Shl: - case BinaryOperator::Shr: - case BinaryOperator::LOr: - case BinaryOperator::LAnd: - if (!LHSVal.isConstant(0)) + //case BO_AddAssign: // Common false positive + case BO_SubAssign: // Check only if unsigned + case BO_MulAssign: + case BO_DivAssign: + case BO_AndAssign: + //case BO_OrAssign: // Common false positive + //case BO_XorAssign: // Common false positive + case BO_ShlAssign: + case BO_ShrAssign: + case BO_Add: + case BO_Sub: + case BO_Mul: + case BO_Div: + case BO_And: + case BO_Or: + case BO_Xor: + case BO_Shl: + case BO_Shr: + case BO_LOr: + case BO_LAnd: + if (!LHSVal.isConstant(0) || LHSContainsFalsePositive) break; UpdateAssumption(A, LHSis0); return; @@ -298,47 +327,103 @@ void IdempotentOperationChecker::PreVisitBinaryOperator( A = Impossible; } -void IdempotentOperationChecker::VisitEndAnalysis(ExplodedGraph &G, - BugReporter &B, - bool hasWorkRemaining) { - // If there is any work remaining we cannot be 100% sure about our warnings - if (hasWorkRemaining) - return; +// At the post visit stage, the predecessor ExplodedNode will be the +// BinaryOperator that was just created. We use this hook to collect the +// ExplodedNode. +void IdempotentOperationChecker::PostVisitBinaryOperator( + CheckerContext &C, + const BinaryOperator *B) { + // Add the ExplodedNode we just visited + BinaryOperatorData &Data = hash[B]; + Data.explodedNodes.Add(C.getPredecessor()); +} +void IdempotentOperationChecker::VisitEndAnalysis(ExplodedGraph &G, + BugReporter &BR, + GRExprEngine &Eng) { + BugType *BT = new BugType("Idempotent operation", "Dead code"); // Iterate over the hash to see if we have any paths with definite // idempotent operations. - for (AssumptionMap::const_iterator i = - hash.begin(); i != hash.end(); ++i) { - if (i->second != Impossible) { - // Select the error message. - const char *msg = 0; - switch (i->second) { - case Equal: - msg = "idempotent operation; both operands are always equal in value"; - break; - case LHSis1: - msg = "idempotent operation; the left operand is always 1"; - break; - case RHSis1: - msg = "idempotent operation; the right operand is always 1"; - break; - case LHSis0: - msg = "idempotent operation; the left operand is always 0"; - break; - case RHSis0: - msg = "idempotent operation; the right operand is always 0"; - break; - case Possible: - llvm_unreachable("Operation was never marked with an assumption"); - case Impossible: - llvm_unreachable(0); + for (AssumptionMap::const_iterator i = hash.begin(); i != hash.end(); ++i) { + // Unpack the hash contents + const BinaryOperatorData &Data = i->second; + const Assumption &A = Data.assumption; + AnalysisContext *AC = Data.analysisContext; + const ExplodedNodeSet &ES = Data.explodedNodes; + + const BinaryOperator *B = i->first; + + if (A == Impossible) + continue; + + // If the analyzer did not finish, check to see if we can still emit this + // warning + if (Eng.hasWorkRemaining()) { + const CFGStmtMap *CBM = CFGStmtMap::Build(AC->getCFG(), + &AC->getParentMap()); + + // If we can trace back + if (!PathWasCompletelyAnalyzed(AC->getCFG(), + CBM->getBlock(B), + Eng.getCoreEngine())) + continue; + + delete CBM; + } + + // Select the error message and SourceRanges to report. + llvm::SmallString<128> buf; + llvm::raw_svector_ostream os(buf); + bool LHSRelevant = false, RHSRelevant = false; + switch (A) { + case Equal: + LHSRelevant = true; + RHSRelevant = true; + if (B->getOpcode() == BO_Assign) + os << "Assigned value is always the same as the existing value"; + else + os << "Both operands to '" << B->getOpcodeStr() + << "' always have the same value"; + break; + case LHSis1: + LHSRelevant = true; + os << "The left operand to '" << B->getOpcodeStr() << "' is always 1"; + break; + case RHSis1: + RHSRelevant = true; + os << "The right operand to '" << B->getOpcodeStr() << "' is always 1"; + break; + case LHSis0: + LHSRelevant = true; + os << "The left operand to '" << B->getOpcodeStr() << "' is always 0"; + break; + case RHSis0: + RHSRelevant = true; + os << "The right operand to '" << B->getOpcodeStr() << "' is always 0"; + break; + case Possible: + llvm_unreachable("Operation was never marked with an assumption"); + case Impossible: + llvm_unreachable(0); + } + + // Add a report for each ExplodedNode + for (ExplodedNodeSet::iterator I = ES.begin(), E = ES.end(); I != E; ++I) { + EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), *I); + + // Add source ranges and visitor hooks + if (LHSRelevant) { + const Expr *LHS = i->first->getLHS(); + report->addRange(LHS->getSourceRange()); + report->addVisitorCreator(bugreporter::registerVarDeclsLastStore, LHS); + } + if (RHSRelevant) { + const Expr *RHS = i->first->getRHS(); + report->addRange(i->first->getRHS()->getSourceRange()); + report->addVisitorCreator(bugreporter::registerVarDeclsLastStore, RHS); } - // Create the SourceRange Arrays - SourceRange S[2] = { i->first->getLHS()->getSourceRange(), - i->first->getRHS()->getSourceRange() }; - B.EmitBasicReport("Idempotent operation", msg, i->first->getOperatorLoc(), - S, 2); + BR.EmitReport(report); } } } @@ -346,6 +431,10 @@ void IdempotentOperationChecker::VisitEndAnalysis(ExplodedGraph &G, // Updates the current assumption given the new assumption inline void IdempotentOperationChecker::UpdateAssumption(Assumption &A, const Assumption &New) { +// If the assumption is the same, there is nothing to do + if (A == New) + return; + switch (A) { // If we don't currently have an assumption, set it case Possible: @@ -366,89 +455,249 @@ inline void IdempotentOperationChecker::UpdateAssumption(Assumption &A, } } -// Recursively find any substatements containing macros -bool IdempotentOperationChecker::containsMacro(const Stmt *S) { - if (S->getLocStart().isMacroID()) - return true; +// Check for a statement where a variable is self assigned to possibly avoid an +// unused variable warning. +bool IdempotentOperationChecker::isSelfAssign(const Expr *LHS, const Expr *RHS) { + LHS = LHS->IgnoreParenCasts(); + RHS = RHS->IgnoreParenCasts(); - if (S->getLocEnd().isMacroID()) - return true; + const DeclRefExpr *LHS_DR = dyn_cast<DeclRefExpr>(LHS); + if (!LHS_DR) + return false; - for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); - ++I) - if (const Stmt *child = *I) - if (containsMacro(child)) - return true; + const VarDecl *VD = dyn_cast<VarDecl>(LHS_DR->getDecl()); + if (!VD) + return false; - return false; + const DeclRefExpr *RHS_DR = dyn_cast<DeclRefExpr>(RHS); + if (!RHS_DR) + return false; + + if (VD != RHS_DR->getDecl()) + return false; + + return true; } -// Recursively find any substatements containing enum constants -bool IdempotentOperationChecker::containsEnum(const Stmt *S) { - const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S); +// Returns true if the Expr points to a VarDecl that is not read anywhere +// outside of self-assignments. +bool IdempotentOperationChecker::isUnused(const Expr *E, + AnalysisContext *AC) { + if (!E) + return false; - if (DR && isa<EnumConstantDecl>(DR->getDecl())) - return true; + const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()); + if (!DR) + return false; - for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); - ++I) - if (const Stmt *child = *I) - if (containsEnum(child)) - return true; + const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()); + if (!VD) + return false; - return false; + if (AC->getPseudoConstantAnalysis()->wasReferenced(VD)) + return false; + + return true; } -// Recursively find any substatements containing __builtin_offset_of -bool IdempotentOperationChecker::containsBuiltinOffsetOf(const Stmt *S) { - const UnaryOperator *UO = dyn_cast<UnaryOperator>(S); +#if 0 +// Check for self casts truncating/extending a variable +bool IdempotentOperationChecker::isTruncationExtensionAssignment( + const Expr *LHS, + const Expr *RHS) { - if (UO && UO->getOpcode() == UnaryOperator::OffsetOf) - return true; + const DeclRefExpr *LHS_DR = dyn_cast<DeclRefExpr>(LHS->IgnoreParenCasts()); + if (!LHS_DR) + return false; - for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); - ++I) - if (const Stmt *child = *I) - if (containsBuiltinOffsetOf(child)) - return true; + const VarDecl *VD = dyn_cast<VarDecl>(LHS_DR->getDecl()); + if (!VD) + return false; - return false; + const DeclRefExpr *RHS_DR = dyn_cast<DeclRefExpr>(RHS->IgnoreParenCasts()); + if (!RHS_DR) + return false; + + if (VD != RHS_DR->getDecl()) + return false; + + return dyn_cast<DeclRefExpr>(RHS->IgnoreParens()) == NULL; } +#endif + +// Returns false if a path to this block was not completely analyzed, or true +// otherwise. +bool IdempotentOperationChecker::PathWasCompletelyAnalyzed( + const CFG *C, + const CFGBlock *CB, + const GRCoreEngine &CE) { + std::deque<const CFGBlock *> WorkList; + llvm::SmallSet<unsigned, 8> Aborted; + llvm::SmallSet<unsigned, 128> Visited; + + // Create a set of all aborted blocks + typedef GRCoreEngine::BlocksAborted::const_iterator AbortedIterator; + for (AbortedIterator I = CE.blocks_aborted_begin(), + E = CE.blocks_aborted_end(); I != E; ++I) { + const BlockEdge &BE = I->first; + + // The destination block on the BlockEdge is the first block that was not + // analyzed. + Aborted.insert(BE.getDst()->getBlockID()); + } -bool IdempotentOperationChecker::containsZeroConstant(const Stmt *S) { - const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(S); - if (IL && IL->getValue() == 0) + // Save the entry block ID for early exiting + unsigned EntryBlockID = C->getEntry().getBlockID(); + + // Create initial node + WorkList.push_back(CB); + + while (!WorkList.empty()) { + const CFGBlock *Head = WorkList.front(); + WorkList.pop_front(); + Visited.insert(Head->getBlockID()); + + // If we found the entry block, then there exists a path from the target + // node to the entry point of this function -> the path was completely + // analyzed. + if (Head->getBlockID() == EntryBlockID) + return true; + + // If any of the aborted blocks are on the path to the beginning, then all + // paths to this block were not analyzed. + if (Aborted.count(Head->getBlockID())) + return false; + + // Add the predecessors to the worklist unless we have already visited them + for (CFGBlock::const_pred_iterator I = Head->pred_begin(); + I != Head->pred_end(); ++I) + if (!Visited.count((*I)->getBlockID())) + WorkList.push_back(*I); + } + + // If we get to this point, there is no connection to the entry block or an + // aborted block. This path is unreachable and we can report the error. + return true; +} + +// Recursive function that determines whether an expression contains any element +// that varies. This could be due to a compile-time constant like sizeof. An +// expression may also involve a variable that behaves like a constant. The +// function returns true if the expression varies, and false otherwise. +bool IdempotentOperationChecker::CanVary(const Expr *Ex, + AnalysisContext *AC) { + // Parentheses and casts are irrelevant here + Ex = Ex->IgnoreParenCasts(); + + if (Ex->getLocStart().isMacroID()) + return false; + + switch (Ex->getStmtClass()) { + // Trivially true cases + case Stmt::ArraySubscriptExprClass: + case Stmt::MemberExprClass: + case Stmt::StmtExprClass: + case Stmt::CallExprClass: + case Stmt::VAArgExprClass: + case Stmt::ShuffleVectorExprClass: + return true; + default: return true; - const FloatingLiteral *FL = dyn_cast<FloatingLiteral>(S); - if (FL && FL->getValue().isZero()) + // Trivially false cases + case Stmt::IntegerLiteralClass: + case Stmt::CharacterLiteralClass: + case Stmt::FloatingLiteralClass: + case Stmt::PredefinedExprClass: + case Stmt::ImaginaryLiteralClass: + case Stmt::StringLiteralClass: + case Stmt::OffsetOfExprClass: + case Stmt::CompoundLiteralExprClass: + case Stmt::AddrLabelExprClass: + case Stmt::TypesCompatibleExprClass: + case Stmt::GNUNullExprClass: + case Stmt::InitListExprClass: + case Stmt::DesignatedInitExprClass: + case Stmt::BlockExprClass: + case Stmt::BlockDeclRefExprClass: + return false; + + // Cases requiring custom logic + case Stmt::SizeOfAlignOfExprClass: { + const SizeOfAlignOfExpr *SE = cast<const SizeOfAlignOfExpr>(Ex); + if (!SE->isSizeOf()) + return false; + return SE->getTypeOfArgument()->isVariableArrayType(); + } + case Stmt::DeclRefExprClass: + // Check for constants/pseudoconstants + return !isConstantOrPseudoConstant(cast<DeclRefExpr>(Ex), AC); + + // The next cases require recursion for subexpressions + case Stmt::BinaryOperatorClass: { + const BinaryOperator *B = cast<const BinaryOperator>(Ex); + return CanVary(B->getRHS(), AC) + || CanVary(B->getLHS(), AC); + } + case Stmt::UnaryOperatorClass: { + const UnaryOperator *U = cast<const UnaryOperator>(Ex); + // Handle trivial case first + switch (U->getOpcode()) { + case UO_Extension: + return false; + default: + return CanVary(U->getSubExpr(), AC); + } + } + case Stmt::ChooseExprClass: + return CanVary(cast<const ChooseExpr>(Ex)->getChosenSubExpr( + AC->getASTContext()), AC); + case Stmt::ConditionalOperatorClass: + return CanVary(cast<const ConditionalOperator>(Ex)->getCond(), AC); + } +} + +// Returns true if a DeclRefExpr is or behaves like a constant. +bool IdempotentOperationChecker::isConstantOrPseudoConstant( + const DeclRefExpr *DR, + AnalysisContext *AC) { + // Check if the type of the Decl is const-qualified + if (DR->getType().isConstQualified()) return true; - for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); - ++I) - if (const Stmt *child = *I) - if (containsZeroConstant(child)) - return true; + // Check for an enum + if (isa<EnumConstantDecl>(DR->getDecl())) + return true; + + const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()); + if (!VD) + return true; + + // Check if the Decl behaves like a constant. This check also takes care of + // static variables, which can only change between function calls if they are + // modified in the AST. + PseudoConstantAnalysis *PCA = AC->getPseudoConstantAnalysis(); + if (PCA->isPseudoConstant(VD)) + return true; return false; } -bool IdempotentOperationChecker::containsOneConstant(const Stmt *S) { - const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(S); - if (IL && IL->getValue() == 1) - return true; +// Recursively find any substatements containing VarDecl's with storage other +// than local +bool IdempotentOperationChecker::containsNonLocalVarDecl(const Stmt *S) { + const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S); - const FloatingLiteral *FL = dyn_cast<FloatingLiteral>(S); - const llvm::APFloat one(1.0); - if (FL && FL->getValue().compare(one) == llvm::APFloat::cmpEqual) - return true; + if (DR) + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) + if (!VD->hasLocalStorage()) + return true; for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); ++I) if (const Stmt *child = *I) - if (containsOneConstant(child)) + if (containsNonLocalVarDecl(child)) return true; return false; } - diff --git a/lib/Checker/LLVMConventionsChecker.cpp b/lib/Checker/LLVMConventionsChecker.cpp index c121257..2f87da1 100644 --- a/lib/Checker/LLVMConventionsChecker.cpp +++ b/lib/Checker/LLVMConventionsChecker.cpp @@ -128,7 +128,6 @@ public: void VisitDeclStmt(DeclStmt *DS); private: void VisitVarDecl(VarDecl *VD); - void CheckStringRefBoundtoTemporaryString(VarDecl *VD); }; } // end anonymous namespace diff --git a/lib/Checker/Makefile b/lib/Checker/Makefile index 1bc6529..4ec6f65 100644 --- a/lib/Checker/Makefile +++ b/lib/Checker/Makefile @@ -13,7 +13,6 @@ CLANG_LEVEL := ../.. LIBRARYNAME := clangChecker -BUILD_ARCHIVE = 1 include $(CLANG_LEVEL)/Makefile diff --git a/lib/Checker/MallocChecker.cpp b/lib/Checker/MallocChecker.cpp index dcc21ca..c9b6d75 100644 --- a/lib/Checker/MallocChecker.cpp +++ b/lib/Checker/MallocChecker.cpp @@ -24,15 +24,18 @@ using namespace clang; namespace { class RefState { - enum Kind { AllocateUnchecked, AllocateFailed, Released, Escaped } K; + enum Kind { AllocateUnchecked, AllocateFailed, Released, Escaped, + Relinquished } K; const Stmt *S; public: RefState(Kind k, const Stmt *s) : K(k), S(s) {} bool isAllocated() const { return K == AllocateUnchecked; } + //bool isFailed() const { return K == AllocateFailed; } bool isReleased() const { return K == Released; } - bool isEscaped() const { return K == Escaped; } + //bool isEscaped() const { return K == Escaped; } + //bool isRelinquished() const { return K == Relinquished; } bool operator==(const RefState &X) const { return K == X.K && S == X.S; @@ -46,6 +49,9 @@ public: } static RefState getReleased(const Stmt *s) { return RefState(Released, s); } static RefState getEscaped(const Stmt *s) { return RefState(Escaped, s); } + static RefState getRelinquished(const Stmt *s) { + return RefState(Relinquished, s); + } void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); @@ -59,23 +65,30 @@ class MallocChecker : public CheckerVisitor<MallocChecker> { BuiltinBug *BT_DoubleFree; BuiltinBug *BT_Leak; BuiltinBug *BT_UseFree; + BuiltinBug *BT_UseRelinquished; BuiltinBug *BT_BadFree; IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc; public: MallocChecker() - : BT_DoubleFree(0), BT_Leak(0), BT_UseFree(0), BT_BadFree(0), + : BT_DoubleFree(0), BT_Leak(0), BT_UseFree(0), BT_UseRelinquished(0), + BT_BadFree(0), II_malloc(0), II_free(0), II_realloc(0), II_calloc(0) {} static void *getTag(); bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); void EvalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper); void EvalEndPath(GREndPathNodeBuilder &B, void *tag, GRExprEngine &Eng); void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); - const GRState *EvalAssume(const GRState *state, SVal Cond, bool Assumption); + const GRState *EvalAssume(const GRState *state, SVal Cond, bool Assumption, + bool *respondsToCallback); void VisitLocation(CheckerContext &C, const Stmt *S, SVal l); + virtual void PreVisitBind(CheckerContext &C, const Stmt *StoreE, + SVal location, SVal val); private: void MallocMem(CheckerContext &C, const CallExpr *CE); + void MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, + const OwnershipAttr* Att); const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE, const Expr *SizeEx, SVal Init, const GRState *state) { @@ -86,8 +99,10 @@ private: const GRState *state); void FreeMem(CheckerContext &C, const CallExpr *CE); + void FreeMemAttr(CheckerContext &C, const CallExpr *CE, + const OwnershipAttr* Att); const GRState *FreeMemAux(CheckerContext &C, const CallExpr *CE, - const GRState *state); + const GRState *state, unsigned Num, bool Hold); void ReallocMem(CheckerContext &C, const CallExpr *CE); void CallocMem(CheckerContext &C, const CallExpr *CE); @@ -103,7 +118,7 @@ typedef llvm::ImmutableMap<SymbolRef, RefState> RegionStateTy; namespace clang { template <> struct GRStateTrait<RegionState> - : public GRStatePartialTrait<llvm::ImmutableMap<SymbolRef, RefState> > { + : public GRStatePartialTrait<RegionStateTy> { static void *GDMIndex() { return MallocChecker::getTag(); } }; } @@ -156,7 +171,32 @@ bool MallocChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { return true; } - return false; + // Check all the attributes, if there are any. + // There can be multiple of these attributes. + bool rv = false; + if (FD->hasAttrs()) { + for (specific_attr_iterator<OwnershipAttr> + i = FD->specific_attr_begin<OwnershipAttr>(), + e = FD->specific_attr_end<OwnershipAttr>(); + i != e; ++i) { + switch ((*i)->getOwnKind()) { + case OwnershipAttr::Returns: { + MallocMemReturnsAttr(C, CE, *i); + rv = true; + break; + } + case OwnershipAttr::Takes: + case OwnershipAttr::Holds: { + FreeMemAttr(C, CE, *i); + rv = true; + break; + } + default: + break; + } + } + } + return rv; } void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) { @@ -165,6 +205,23 @@ void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) { C.addTransition(state); } +void MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, + const OwnershipAttr* Att) { + if (Att->getModule() != "malloc") + return; + + OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); + if (I != E) { + const GRState *state = + MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), C.getState()); + C.addTransition(state); + return; + } + const GRState *state = MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), + C.getState()); + C.addTransition(state); +} + const GRState *MallocChecker::MallocMemAux(CheckerContext &C, const CallExpr *CE, SVal Size, SVal Init, @@ -196,25 +253,53 @@ const GRState *MallocChecker::MallocMemAux(CheckerContext &C, } void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) { - const GRState *state = FreeMemAux(C, CE, C.getState()); + const GRState *state = FreeMemAux(C, CE, C.getState(), 0, false); if (state) C.addTransition(state); } +void MallocChecker::FreeMemAttr(CheckerContext &C, const CallExpr *CE, + const OwnershipAttr* Att) { + if (Att->getModule() != "malloc") + return; + + for (OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); + I != E; ++I) { + const GRState *state = FreeMemAux(C, CE, C.getState(), *I, + Att->getOwnKind() == OwnershipAttr::Holds); + if (state) + C.addTransition(state); + } +} + const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, - const GRState *state) { - const Expr *ArgExpr = CE->getArg(0); + const GRState *state, unsigned Num, + bool Hold) { + const Expr *ArgExpr = CE->getArg(Num); SVal ArgVal = state->getSVal(ArgExpr); - // If ptr is NULL, no operation is preformed. - if (ArgVal.isZeroConstant()) + DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(ArgVal); + + // Check for null dereferences. + if (!isa<Loc>(location)) return state; - + + // FIXME: Technically using 'Assume' here can result in a path + // bifurcation. In such cases we need to return two states, not just one. + const GRState *notNullState, *nullState; + llvm::tie(notNullState, nullState) = state->Assume(location); + + // The explicit NULL case, no operation is performed. + if (nullState && !notNullState) + return nullState; + + assert(notNullState); + // Unknown values could easily be okay // Undefined values are handled elsewhere if (ArgVal.isUnknownOrUndef()) - return state; + return notNullState; const MemRegion *R = ArgVal.getAsRegion(); @@ -253,24 +338,23 @@ const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, // Various cases could lead to non-symbol values here. // For now, ignore them. if (!SR) - return state; + return notNullState; SymbolRef Sym = SR->getSymbol(); - const RefState *RS = state->get<RegionState>(Sym); // If the symbol has not been tracked, return. This is possible when free() is // called on a pointer that does not get its pointee directly from malloc(). // Full support of this requires inter-procedural analysis. if (!RS) - return state; + return notNullState; // Check double free. if (RS->isReleased()) { - ExplodedNode *N = C.GenerateSink(); - if (N) { + if (ExplodedNode *N = C.GenerateSink()) { if (!BT_DoubleFree) - BT_DoubleFree = new BuiltinBug("Double free", + BT_DoubleFree + = new BuiltinBug("Double free", "Try to free a memory block that has been released"); // FIXME: should find where it's freed last time. BugReport *R = new BugReport(*BT_DoubleFree, @@ -281,7 +365,9 @@ const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, } // Normal free. - return state->set<RegionState>(Sym, RefState::getReleased(CE)); + if (Hold) + return notNullState->set<RegionState>(Sym, RefState::getRelinquished(CE)); + return notNullState->set<RegionState>(Sym, RefState::getReleased(CE)); } bool MallocChecker::SummarizeValue(llvm::raw_ostream& os, SVal V) { @@ -376,8 +462,7 @@ bool MallocChecker::SummarizeRegion(llvm::raw_ostream& os, void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange range) { - ExplodedNode *N = C.GenerateSink(); - if (N) { + if (ExplodedNode *N = C.GenerateSink()) { if (!BT_BadFree) BT_BadFree = new BuiltinBug("Bad free"); @@ -446,13 +531,13 @@ void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) { ValMgr.makeIntValWithPtrWidth(0, false)); if (const GRState *stateSizeZero = stateNotEqual->Assume(SizeZero, true)) { - const GRState *stateFree = FreeMemAux(C, CE, stateSizeZero); + const GRState *stateFree = FreeMemAux(C, CE, stateSizeZero, 0, false); if (stateFree) C.addTransition(stateFree->BindExpr(CE, UndefinedVal(), true)); } if (const GRState *stateSizeNotZero=stateNotEqual->Assume(SizeZero,false)) { - const GRState *stateFree = FreeMemAux(C, CE, stateSizeNotZero); + const GRState *stateFree = FreeMemAux(C, CE, stateSizeNotZero, 0, false); if (stateFree) { // FIXME: We should copy the content of the original buffer. const GRState *stateRealloc = MallocMemAux(C, CE, CE->getArg(1), @@ -471,7 +556,7 @@ void MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE) { SVal Count = state->getSVal(CE->getArg(0)); SVal EleSize = state->getSVal(CE->getArg(1)); - SVal TotalSize = SVator.EvalBinOp(state, BinaryOperator::Mul, Count, EleSize, + SVal TotalSize = SVator.EvalBinOp(state, BO_Mul, Count, EleSize, ValMgr.getContext().getSizeType()); SVal Zero = ValMgr.makeZeroVal(ValMgr.getContext().CharTy); @@ -481,36 +566,42 @@ void MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE) { } void MallocChecker::EvalDeadSymbols(CheckerContext &C,SymbolReaper &SymReaper) { - for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), - E = SymReaper.dead_end(); I != E; ++I) { - SymbolRef Sym = *I; - const GRState *state = C.getState(); - const RefState *RS = state->get<RegionState>(Sym); - if (!RS) - return; - - if (RS->isAllocated()) { - ExplodedNode *N = C.GenerateSink(); - if (N) { - if (!BT_Leak) - BT_Leak = new BuiltinBug("Memory leak", + if (!SymReaper.hasDeadSymbols()) + return; + + const GRState *state = C.getState(); + RegionStateTy RS = state->get<RegionState>(); + RegionStateTy::Factory &F = state->get_context<RegionState>(); + + for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { + if (SymReaper.isDead(I->first)) { + if (I->second.isAllocated()) { + if (ExplodedNode *N = C.GenerateNode()) { + if (!BT_Leak) + BT_Leak = new BuiltinBug("Memory leak", "Allocated memory never released. Potential memory leak."); - // FIXME: where it is allocated. - BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); - C.EmitReport(R); + // FIXME: where it is allocated. + BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); + C.EmitReport(R); + } } + + // Remove the dead symbol from the map. + RS = F.Remove(RS, I->first); } } + + state = state->set<RegionState>(RS); + C.GenerateNode(state); } void MallocChecker::EvalEndPath(GREndPathNodeBuilder &B, void *tag, GRExprEngine &Eng) { SaveAndRestore<bool> OldHasGen(B.HasGeneratedNode); const GRState *state = B.getState(); - typedef llvm::ImmutableMap<SymbolRef, RefState> SymMap; - SymMap M = state->get<RegionState>(); + RegionStateTy M = state->get<RegionState>(); - for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { + for (RegionStateTy::iterator I = M.begin(), E = M.end(); I != E; ++I) { RefState RS = I->second; if (RS.isAllocated()) { ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); @@ -549,7 +640,8 @@ void MallocChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) { } const GRState *MallocChecker::EvalAssume(const GRState *state, SVal Cond, - bool Assumption) { + bool Assumption, + bool * /* respondsToCallback */) { // If a symblic region is assumed to NULL, set its state to AllocateFailed. // FIXME: should also check symbols assumed to non-null. @@ -568,9 +660,8 @@ void MallocChecker::VisitLocation(CheckerContext &C, const Stmt *S, SVal l) { SymbolRef Sym = l.getLocSymbolInBase(); if (Sym) { const RefState *RS = C.getState()->get<RegionState>(Sym); - if (RS) - if (RS->isReleased()) { - ExplodedNode *N = C.GenerateSink(); + if (RS && RS->isReleased()) { + if (ExplodedNode *N = C.GenerateNode()) { if (!BT_UseFree) BT_UseFree = new BuiltinBug("Use dynamically allocated memory after" " it is freed."); @@ -579,5 +670,67 @@ void MallocChecker::VisitLocation(CheckerContext &C, const Stmt *S, SVal l) { N); C.EmitReport(R); } + } + } +} + +void MallocChecker::PreVisitBind(CheckerContext &C, + const Stmt *StoreE, + SVal location, + SVal val) { + // The PreVisitBind implements the same algorithm as already used by the + // Objective C ownership checker: if the pointer escaped from this scope by + // assignment, let it go. However, assigning to fields of a stack-storage + // structure does not transfer ownership. + + const GRState *state = C.getState(); + DefinedOrUnknownSVal l = cast<DefinedOrUnknownSVal>(location); + + // Check for null dereferences. + if (!isa<Loc>(l)) + return; + + // Before checking if the state is null, check if 'val' has a RefState. + // Only then should we check for null and bifurcate the state. + SymbolRef Sym = val.getLocSymbolInBase(); + if (Sym) { + if (const RefState *RS = state->get<RegionState>(Sym)) { + // If ptr is NULL, no operation is performed. + const GRState *notNullState, *nullState; + llvm::tie(notNullState, nullState) = state->Assume(l); + + // Generate a transition for 'nullState' to record the assumption + // that the state was null. + if (nullState) + C.addTransition(nullState); + + if (!notNullState) + return; + + if (RS->isAllocated()) { + // Something we presently own is being assigned somewhere. + const MemRegion *AR = location.getAsRegion(); + if (!AR) + return; + AR = AR->StripCasts()->getBaseRegion(); + do { + // If it is on the stack, we still own it. + if (AR->hasStackNonParametersStorage()) + break; + + // If the state can't represent this binding, we still own it. + if (notNullState == (notNullState->bindLoc(cast<Loc>(location), + UnknownVal()))) + break; + + // We no longer own this pointer. + notNullState = + notNullState->set<RegionState>(Sym, + RefState::getRelinquished(StoreE)); + } + while (false); + } + C.addTransition(notNullState); + } } } diff --git a/lib/Checker/MemRegion.cpp b/lib/Checker/MemRegion.cpp index 9cfeb7a..3f706e1 100644 --- a/lib/Checker/MemRegion.cpp +++ b/lib/Checker/MemRegion.cpp @@ -18,6 +18,7 @@ #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/Support/BumpVector.h" #include "clang/AST/CharUnits.h" +#include "clang/AST/RecordLayout.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -177,7 +178,7 @@ const StackFrameContext *VarRegion::getStackFrame() const { DefinedOrUnknownSVal DeclRegion::getExtent(ValueManager& ValMgr) const { ASTContext& Ctx = ValMgr.getContext(); - QualType T = getDesugaredValueType(Ctx); + QualType T = getDesugaredValueType(); if (isa<VariableArrayType>(T)) return nonloc::SymbolVal(ValMgr.getSymbolManager().getExtentSymbol(this)); @@ -195,8 +196,7 @@ DefinedOrUnknownSVal FieldRegion::getExtent(ValueManager& ValMgr) const { // A zero-length array at the end of a struct often stands for dynamically- // allocated extra memory. if (Extent.isZeroConstant()) { - ASTContext& Ctx = ValMgr.getContext(); - QualType T = getDesugaredValueType(Ctx); + QualType T = getDesugaredValueType(); if (isa<ConstantArrayType>(T)) return UnknownVal(); @@ -785,7 +785,7 @@ static bool IsCompleteType(ASTContext &Ctx, QualType Ty) { return true; } -RegionRawOffset ElementRegion::getAsRawOffset() const { +RegionRawOffset ElementRegion::getAsArrayOffset() const { CharUnits offset = CharUnits::Zero(); const ElementRegion *ER = this; const MemRegion *superR = NULL; @@ -827,6 +827,67 @@ RegionRawOffset ElementRegion::getAsRawOffset() const { return RegionRawOffset(superR, offset.getQuantity()); } +RegionOffset MemRegion::getAsOffset() const { + const MemRegion *R = this; + int64_t Offset = 0; + + while (1) { + switch (R->getKind()) { + default: + return RegionOffset(0); + case SymbolicRegionKind: + case AllocaRegionKind: + case CompoundLiteralRegionKind: + case CXXThisRegionKind: + case StringRegionKind: + case VarRegionKind: + case CXXObjectRegionKind: + goto Finish; + case ElementRegionKind: { + const ElementRegion *ER = cast<ElementRegion>(R); + QualType EleTy = ER->getValueType(); + + if (!IsCompleteType(getContext(), EleTy)) + return RegionOffset(0); + + SVal Index = ER->getIndex(); + if (const nonloc::ConcreteInt *CI=dyn_cast<nonloc::ConcreteInt>(&Index)) { + int64_t i = CI->getValue().getSExtValue(); + CharUnits Size = getContext().getTypeSizeInChars(EleTy); + Offset += i * Size.getQuantity() * 8; + } else { + // We cannot compute offset for non-concrete index. + return RegionOffset(0); + } + R = ER->getSuperRegion(); + break; + } + case FieldRegionKind: { + const FieldRegion *FR = cast<FieldRegion>(R); + const RecordDecl *RD = FR->getDecl()->getParent(); + if (!RD->isDefinition()) + // We cannot compute offset for incomplete type. + return RegionOffset(0); + // Get the field number. + unsigned idx = 0; + for (RecordDecl::field_iterator FI = RD->field_begin(), + FE = RD->field_end(); FI != FE; ++FI, ++idx) + if (FR->getDecl() == *FI) + break; + + const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD); + // This is offset in bits. + Offset += Layout.getFieldOffset(idx); + R = FR->getSuperRegion(); + break; + } + } + } + + Finish: + return RegionOffset(R, Offset); +} + //===----------------------------------------------------------------------===// // BlockDataRegion //===----------------------------------------------------------------------===// diff --git a/lib/Checker/OSAtomicChecker.cpp b/lib/Checker/OSAtomicChecker.cpp index 1ea1bd9..02de0a8 100644 --- a/lib/Checker/OSAtomicChecker.cpp +++ b/lib/Checker/OSAtomicChecker.cpp @@ -110,9 +110,9 @@ bool OSAtomicChecker::EvalOSAtomicCompareAndSwap(CheckerContext &C, QualType LoadTy; if (const TypedRegion *TR = dyn_cast_or_null<TypedRegion>(location.getAsRegion())) { - LoadTy = TR->getValueType(Ctx); + LoadTy = TR->getValueType(); } - Engine.EvalLoad(Tmp, const_cast<Expr *>(theValueExpr), C.getPredecessor(), + Engine.EvalLoad(Tmp, theValueExpr, C.getPredecessor(), state, location, OSAtomicLoadTag, LoadTy); if (Tmp.empty()) { @@ -158,10 +158,10 @@ bool OSAtomicChecker::EvalOSAtomicCompareAndSwap(CheckerContext &C, // Handle implicit value casts. if (const TypedRegion *R = dyn_cast_or_null<TypedRegion>(location.getAsRegion())) { - val = SVator.EvalCast(val,R->getValueType(Ctx),newValueExpr->getType()); + val = SVator.EvalCast(val,R->getValueType(), newValueExpr->getType()); } - Engine.EvalStore(TmpStore, NULL, const_cast<Expr *>(theValueExpr), N, + Engine.EvalStore(TmpStore, NULL, theValueExpr, N, stateEqual, location, val, OSAtomicStoreTag); if (TmpStore.empty()) { diff --git a/lib/Checker/PointerArithChecker.cpp b/lib/Checker/PointerArithChecker.cpp index ed60c42..cbac423 100644 --- a/lib/Checker/PointerArithChecker.cpp +++ b/lib/Checker/PointerArithChecker.cpp @@ -36,8 +36,7 @@ void *PointerArithChecker::getTag() { void PointerArithChecker::PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B) { - if (B->getOpcode() != BinaryOperator::Sub && - B->getOpcode() != BinaryOperator::Add) + if (B->getOpcode() != BO_Sub && B->getOpcode() != BO_Add) return; const GRState *state = C.getState(); diff --git a/lib/Checker/PointerSubChecker.cpp b/lib/Checker/PointerSubChecker.cpp index bc0fd24..d64b6ae 100644 --- a/lib/Checker/PointerSubChecker.cpp +++ b/lib/Checker/PointerSubChecker.cpp @@ -39,7 +39,7 @@ void PointerSubChecker::PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B) { // When doing pointer subtraction, if the two pointers do not point to the // same memory chunk, emit a warning. - if (B->getOpcode() != BinaryOperator::Sub) + if (B->getOpcode() != BO_Sub) return; const GRState *state = C.getState(); diff --git a/lib/Checker/RangeConstraintManager.cpp b/lib/Checker/RangeConstraintManager.cpp index 2a35d32..697694e 100644 --- a/lib/Checker/RangeConstraintManager.cpp +++ b/lib/Checker/RangeConstraintManager.cpp @@ -83,7 +83,6 @@ public: typedef PrimRangeSet::iterator iterator; RangeSet(PrimRangeSet RS) : ranges(RS) {} - RangeSet(Factory& F) : ranges(F.GetEmptySet()) {} iterator begin() const { return ranges.begin(); } iterator end() const { return ranges.end(); } diff --git a/lib/Checker/RegionStore.cpp b/lib/Checker/RegionStore.cpp index 74a7fee..1a3eded 100644 --- a/lib/Checker/RegionStore.cpp +++ b/lib/Checker/RegionStore.cpp @@ -44,10 +44,9 @@ private: uint64_t Offset; explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k) - : P(r, (unsigned) k), Offset(offset) { assert(r); } + : P(r, (unsigned) k), Offset(offset) {} public: - bool isDefault() const { return P.getInt() == Default; } bool isDirect() const { return P.getInt() == Direct; } const MemRegion *getRegion() const { return P.getPointer(); } @@ -72,9 +71,26 @@ public: return P.getOpaqueValue() == X.P.getOpaqueValue() && Offset == X.Offset; } + + bool isValid() const { + return getRegion() != NULL; + } }; } // end anonymous namespace +BindingKey BindingKey::Make(const MemRegion *R, Kind k) { + if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { + const RegionRawOffset &O = ER->getAsArrayOffset(); + + // FIXME: There are some ElementRegions for which we cannot compute + // raw offsets yet, including regions with symbolic offsets. These will be + // ignored by the store. + return BindingKey(O.getRegion(), O.getByteOffset(), k); + } + + return BindingKey(R, 0, k); +} + namespace llvm { static inline llvm::raw_ostream& operator<<(llvm::raw_ostream& os, BindingKey K) { @@ -101,35 +117,20 @@ struct maximal_features_tag {}; class RegionStoreFeatures { bool SupportsFields; - bool SupportsRemaining; - public: RegionStoreFeatures(minimal_features_tag) : - SupportsFields(false), SupportsRemaining(false) {} + SupportsFields(false) {} RegionStoreFeatures(maximal_features_tag) : - SupportsFields(true), SupportsRemaining(false) {} + SupportsFields(true) {} void enableFields(bool t) { SupportsFields = t; } bool supportsFields() const { return SupportsFields; } - bool supportsRemaining() const { return SupportsRemaining; } }; } //===----------------------------------------------------------------------===// -// Utility functions. -//===----------------------------------------------------------------------===// - -static bool IsAnyPointerOrIntptr(QualType ty, ASTContext &Ctx) { - if (ty->isAnyPointerType()) - return true; - - return ty->isIntegerType() && ty->isScalarType() && - Ctx.getTypeSize(ty) == Ctx.getTypeSize(Ctx.VoidPtrTy); -} - -//===----------------------------------------------------------------------===// // Main RegionStore logic. //===----------------------------------------------------------------------===// @@ -180,6 +181,14 @@ public: } }; +void +RegionStoreSubRegionMap::process(llvm::SmallVectorImpl<const SubRegion*> &WL, + const SubRegion *R) { + const MemRegion *superR = R->getSuperRegion(); + if (add(superR, R)) + if (const SubRegion *sr = dyn_cast<SubRegion>(superR)) + WL.push_back(sr); +} class RegionStoreManager : public StoreManager { const RegionStoreFeatures Features; @@ -197,7 +206,6 @@ public: RegionStoreSubRegionMap *getRegionStoreSubRegionMap(Store store); - Optional<SVal> getBinding(RegionBindings B, const MemRegion *R); Optional<SVal> getDirectBinding(RegionBindings B, const MemRegion *R); /// getDefaultBinding - Returns an SVal* representing an optional default /// binding associated with a region and its subregions. @@ -226,18 +234,13 @@ public: // Binding values to regions. //===-------------------------------------------------------------------===// - Store InvalidateRegion(Store store, const MemRegion *R, const Expr *E, - unsigned Count, InvalidatedSymbols *IS) { - return RegionStoreManager::InvalidateRegions(store, &R, &R+1, E, Count, IS, - false); - } - Store InvalidateRegions(Store store, const MemRegion * const *Begin, const MemRegion * const *End, const Expr *E, unsigned Count, InvalidatedSymbols *IS, - bool invalidateGlobals); + bool invalidateGlobals, + InvalidatedRegions *Regions); public: // Made public for helper classes. @@ -260,8 +263,6 @@ public: // Made public for helper classes. return Remove(Remove(B, R, BindingKey::Direct), R, BindingKey::Default); } - Store Remove(Store store, BindingKey K); - public: // Part of public interface to class. Store Bind(Store store, Loc LV, SVal V); @@ -289,7 +290,7 @@ public: // Part of public interface to class. Store BindArray(Store store, const TypedRegion* R, SVal V); /// KillStruct - Set the entire struct to unknown. - Store KillStruct(Store store, const TypedRegion* R); + Store KillStruct(Store store, const TypedRegion* R, SVal DefaultVal); Store Remove(Store store, Loc LV); @@ -352,13 +353,11 @@ public: // Part of public interface to class. /// RemoveDeadBindings - Scans the RegionStore of 'state' for dead values. /// It returns a new Store with these values removed. - const GRState *RemoveDeadBindings(GRState &state, - const StackFrameContext *LCtx, - SymbolReaper& SymReaper, + Store RemoveDeadBindings(Store store, const StackFrameContext *LCtx, + SymbolReaper& SymReaper, llvm::SmallVectorImpl<const MemRegion*>& RegionRoots); - const GRState *EnterStackFrame(const GRState *state, - const StackFrameContext *frame); + Store EnterStackFrame(const GRState *state, const StackFrameContext *frame); //===------------------------------------------------------------------===// // Region "extents". @@ -392,9 +391,6 @@ public: // Part of public interface to class. } } } - - // FIXME: Remove. - ASTContext& getContext() { return StateMgr.getContext(); } }; } // end anonymous namespace @@ -414,14 +410,6 @@ StoreManager *clang::CreateFieldsOnlyRegionStoreManager(GRStateManager &StMgr) { return new RegionStoreManager(StMgr, F); } -void -RegionStoreSubRegionMap::process(llvm::SmallVectorImpl<const SubRegion*> &WL, - const SubRegion *R) { - const MemRegion *superR = R->getSuperRegion(); - if (add(superR, R)) - if (const SubRegion *sr = dyn_cast<SubRegion>(superR)) - WL.push_back(sr); -} RegionStoreSubRegionMap* RegionStoreManager::getRegionStoreSubRegionMap(Store store) { @@ -579,14 +567,16 @@ class InvalidateRegionsWorker : public ClusterAnalysis<InvalidateRegionsWorker> const Expr *Ex; unsigned Count; StoreManager::InvalidatedSymbols *IS; + StoreManager::InvalidatedRegions *Regions; public: InvalidateRegionsWorker(RegionStoreManager &rm, GRStateManager &stateMgr, RegionBindings b, const Expr *ex, unsigned count, - StoreManager::InvalidatedSymbols *is) + StoreManager::InvalidatedSymbols *is, + StoreManager::InvalidatedRegions *r) : ClusterAnalysis<InvalidateRegionsWorker>(rm, stateMgr, b), - Ex(ex), Count(count), IS(is) {} + Ex(ex), Count(count), IS(is), Regions(r) {} void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E); void VisitBaseRegion(const MemRegion *baseR); @@ -657,6 +647,10 @@ void InvalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { return; } + // Otherwise, we have a normal data region. Record that we touched the region. + if (Regions) + Regions->push_back(baseR); + if (isa<AllocaRegion>(baseR) || isa<SymbolicRegion>(baseR)) { // Invalidate the region by setting its default value to // conjured symbol. The type of the symbol is irrelavant. @@ -670,19 +664,12 @@ void InvalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { return; const TypedRegion *TR = cast<TypedRegion>(baseR); - QualType T = TR->getValueType(Ctx); + QualType T = TR->getValueType(); // Invalidate the binding. - if (const RecordType *RT = T->getAsStructureType()) { - const RecordDecl *RD = RT->getDecl()->getDefinition(); - // No record definition. There is nothing we can do. - if (!RD) { - B = RM.Remove(B, baseR); - return; - } - - // Invalidate the region by setting its default value to - // conjured symbol. The type of the symbol is irrelavant. + if (T->isStructureType()) { + // Invalidate the region by setting its default value to + // conjured symbol. The type of the symbol is irrelavant. DefinedOrUnknownSVal V = ValMgr.getConjuredSymbolVal(baseR, Ex, Ctx.IntTy, Count); B = RM.Add(B, baseR, BindingKey::Default, V); @@ -707,10 +694,11 @@ Store RegionStoreManager::InvalidateRegions(Store store, const MemRegion * const *E, const Expr *Ex, unsigned Count, InvalidatedSymbols *IS, - bool invalidateGlobals) { + bool invalidateGlobals, + InvalidatedRegions *Regions) { InvalidateRegionsWorker W(*this, StateMgr, RegionStoreManager::GetRegionBindings(store), - Ex, Count, IS); + Ex, Count, IS, Regions); // Scan the bindings and generate the clusters. W.GenerateClusters(invalidateGlobals); @@ -733,6 +721,11 @@ Store RegionStoreManager::InvalidateRegions(Store store, /* symbol type, doesn't matter */ Ctx.IntTy, Count); B = Add(B, BindingKey::Make(GS, BindingKey::Default), V); + + // Even if there are no bindings in the global scope, we still need to + // record that we touched it. + if (Regions) + Regions->push_back(GS); } return B.getRoot(); @@ -752,7 +745,7 @@ DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const GRState *state, return UnknownVal(); CharUnits RegionSize = CharUnits::fromQuantity(SizeInt->getSExtValue()); - CharUnits EleSize = getContext().getTypeSizeInChars(EleTy); + CharUnits EleSize = Ctx.getTypeSizeInChars(EleTy); // If a variable is reinterpreted as a type that doesn't fit into a larger // type evenly, round it down. @@ -781,13 +774,12 @@ SVal RegionStoreManager::ArrayToPointer(Loc Array) { return UnknownVal(); // Strip off typedefs from the ArrayRegion's ValueType. - QualType T = ArrayR->getValueType(getContext()).getDesugaredType(); + QualType T = ArrayR->getValueType().getDesugaredType(); ArrayType *AT = cast<ArrayType>(T); T = AT->getElementType(); SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); - return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, ArrayR, - getContext())); + return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, ArrayR, Ctx)); } //===----------------------------------------------------------------------===// @@ -806,8 +798,8 @@ SVal RegionStoreManager::EvalBinOp(BinaryOperator::Opcode Op, Loc L, NonLoc R, default: // Handle it normally. break; - case BinaryOperator::Add: - case BinaryOperator::Sub: + case BO_Add: + case BO_Sub: // FIXME: does this need to be casted to match resultTy? return L; } @@ -820,7 +812,7 @@ SVal RegionStoreManager::EvalBinOp(BinaryOperator::Opcode Op, Loc L, NonLoc R, case MemRegion::SymbolicRegionKind: { const SymbolicRegion *SR = cast<SymbolicRegion>(MR); SymbolRef Sym = SR->getSymbol(); - QualType T = Sym->getType(getContext()); + QualType T = Sym->getType(Ctx); QualType EleTy; if (const PointerType *PT = T->getAs<PointerType>()) @@ -829,14 +821,14 @@ SVal RegionStoreManager::EvalBinOp(BinaryOperator::Opcode Op, Loc L, NonLoc R, EleTy = T->getAs<ObjCObjectPointerType>()->getPointeeType(); SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); - ER = MRMgr.getElementRegion(EleTy, ZeroIdx, SR, getContext()); + ER = MRMgr.getElementRegion(EleTy, ZeroIdx, SR, Ctx); break; } case MemRegion::AllocaRegionKind: { const AllocaRegion *AR = cast<AllocaRegion>(MR); - QualType EleTy = getContext().CharTy; // Create an ElementRegion of bytes. + QualType EleTy = Ctx.CharTy; // Create an ElementRegion of bytes. SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); - ER = MRMgr.getElementRegion(EleTy, ZeroIdx, AR, getContext()); + ER = MRMgr.getElementRegion(EleTy, ZeroIdx, AR, Ctx); break; } @@ -891,13 +883,13 @@ SVal RegionStoreManager::EvalBinOp(BinaryOperator::Opcode Op, Loc L, NonLoc R, cast<nonloc::ConcreteInt>(ValMgr.convertToArrayIndex(*Offset))); const MemRegion* NewER = MRMgr.getElementRegion(ER->getElementType(), NewIdx, - ER->getSuperRegion(), getContext()); + ER->getSuperRegion(), Ctx); return ValMgr.makeLoc(NewER); } if (0 == Base->getValue()) { const MemRegion* NewER = MRMgr.getElementRegion(ER->getElementType(), R, - ER->getSuperRegion(), getContext()); + ER->getSuperRegion(), Ctx); return ValMgr.makeLoc(NewER); } } @@ -922,7 +914,7 @@ Optional<SVal> RegionStoreManager::getDefaultBinding(RegionBindings B, const MemRegion *R) { if (R->isBoundable()) if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) - if (TR->getValueType(getContext())->isUnionType()) + if (TR->getValueType()->isUnionType()) return UnknownVal(); if (const SVal *V = Lookup(B, R, BindingKey::Default)) @@ -931,38 +923,6 @@ Optional<SVal> RegionStoreManager::getDefaultBinding(RegionBindings B, return Optional<SVal>(); } -Optional<SVal> RegionStoreManager::getBinding(RegionBindings B, - const MemRegion *R) { - - if (const Optional<SVal> &V = getDirectBinding(B, R)) - return V; - - return getDefaultBinding(B, R); -} - -static bool IsReinterpreted(QualType RTy, QualType UsedTy, ASTContext &Ctx) { - RTy = Ctx.getCanonicalType(RTy); - UsedTy = Ctx.getCanonicalType(UsedTy); - - if (RTy == UsedTy) - return false; - - - // Recursively check the types. We basically want to see if a pointer value - // is ever reinterpreted as a non-pointer, e.g. void** and intptr_t* - // represents a reinterpretation. - if (Loc::IsLocType(RTy) && Loc::IsLocType(UsedTy)) { - const PointerType *PRTy = RTy->getAs<PointerType>(); - const PointerType *PUsedTy = UsedTy->getAs<PointerType>(); - - return PUsedTy && PRTy && - IsReinterpreted(PRTy->getPointeeType(), - PUsedTy->getPointeeType(), Ctx); - } - - return true; -} - SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { assert(!isa<UnknownVal>(L) && "location unknown"); assert(!isa<UndefinedVal>(L) && "location undefined"); @@ -977,7 +937,7 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { if (isa<AllocaRegion>(MR) || isa<SymbolicRegion>(MR)) { if (T.isNull()) { const SymbolicRegion *SR = cast<SymbolicRegion>(MR); - T = SR->getSymbol()->getType(getContext()); + T = SR->getSymbol()->getType(Ctx); } MR = GetElementZeroRegion(MR, T); } @@ -990,7 +950,7 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { // FIXME: Perhaps this method should just take a 'const MemRegion*' argument // instead of 'Loc', and have the other Loc cases handled at a higher level. const TypedRegion *R = cast<TypedRegion>(MR); - QualType RTy = R->getValueType(getContext()); + QualType RTy = R->getValueType(); // FIXME: We should eventually handle funny addressing. e.g.: // @@ -1001,17 +961,6 @@ SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { // // Such funny addressing will occur due to layering of regions. -#if 0 - ASTContext &Ctx = getContext(); - if (!T.isNull() && IsReinterpreted(RTy, T, Ctx)) { - SVal ZeroIdx = ValMgr.makeZeroArrayIndex(); - R = MRMgr.getElementRegion(T, ZeroIdx, R, Ctx); - RTy = T; - assert(Ctx.getCanonicalType(RTy) == - Ctx.getCanonicalType(R->getValueType(Ctx))); - } -#endif - if (RTy->isStructureOrClassType()) return RetrieveStruct(store, R); @@ -1121,8 +1070,7 @@ SVal RegionStoreManager::RetrieveElement(Store store, if (const StringRegion *StrR=dyn_cast<StringRegion>(superR)) { // FIXME: Handle loads from strings where the literal is treated as // an integer, e.g., *((unsigned int*)"hello") - ASTContext &Ctx = getContext(); - QualType T = Ctx.getAsArrayType(StrR->getValueType(Ctx))->getElementType(); + QualType T = Ctx.getAsArrayType(StrR->getValueType())->getElementType(); if (T != Ctx.getCanonicalType(R->getElementType())) return UnknownVal(); @@ -1131,16 +1079,18 @@ SVal RegionStoreManager::RetrieveElement(Store store, if (nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&Idx)) { int64_t i = CI->getValue().getSExtValue(); int64_t byteLength = Str->getByteLength(); - if (i > byteLength) { - // Buffer overflow checking in GRExprEngine should handle this case, - // but we shouldn't rely on it to not overflow here if that checking - // is disabled. - return UnknownVal(); - } - char c = (i == byteLength) ? '\0' : Str->getStrData()[i]; + // Technically, only i == byteLength is guaranteed to be null. + // However, such overflows should be caught before reaching this point; + // the only time such an access would be made is if a string literal was + // used to initialize a larger array. + char c = (i >= byteLength) ? '\0' : Str->getString()[i]; return ValMgr.makeIntVal(c, T); } } + + // Check for loads from a code text region. For such loads, just give up. + if (isa<CodeTextRegion>(superR)) + return UnknownVal(); // Handle the case where we are indexing into a larger scalar object. // For example, this handles: @@ -1148,9 +1098,9 @@ SVal RegionStoreManager::RetrieveElement(Store store, // char *y = &x; // return *y; // FIXME: This is a hack, and doesn't do anything really intelligent yet. - const RegionRawOffset &O = R->getAsRawOffset(); + const RegionRawOffset &O = R->getAsArrayOffset(); if (const TypedRegion *baseR = dyn_cast_or_null<TypedRegion>(O.getRegion())) { - QualType baseT = baseR->getValueType(Ctx); + QualType baseT = baseR->getValueType(); if (baseT->isScalarType()) { QualType elemT = R->getElementType(); if (elemT->isScalarType()) { @@ -1180,7 +1130,7 @@ SVal RegionStoreManager::RetrieveField(Store store, if (const Optional<SVal> &V = getDirectBinding(B, R)) return *V; - QualType Ty = R->getValueType(getContext()); + QualType Ty = R->getValueType(); return RetrieveFieldOrElementCommon(store, R, Ty, R->getSuperRegion()); } @@ -1243,13 +1193,18 @@ SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store, } if (R->hasStackNonParametersStorage()) { - if (isa<ElementRegion>(R)) { + if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { // Currently we don't reason specially about Clang-style vectors. Check // if superR is a vector and if so return Unknown. if (const TypedRegion *typedSuperR = dyn_cast<TypedRegion>(superR)) { - if (typedSuperR->getValueType(getContext())->isVectorType()) + if (typedSuperR->getValueType()->isVectorType()) return UnknownVal(); } + + // FIXME: We also need to take ElementRegions with symbolic indexes into + // account. + if (!ER->getIndex().isConstant()) + return UnknownVal(); } return UndefinedVal(); @@ -1332,21 +1287,18 @@ SVal RegionStoreManager::RetrieveVar(Store store, const VarRegion *R) { } SVal RegionStoreManager::RetrieveLazySymbol(const TypedRegion *R) { - - QualType valTy = R->getValueType(getContext()); - // All other values are symbolic. return ValMgr.getRegionValueSymbolVal(R); } SVal RegionStoreManager::RetrieveStruct(Store store, const TypedRegion* R) { - QualType T = R->getValueType(getContext()); + QualType T = R->getValueType(); assert(T->isStructureOrClassType()); return ValMgr.makeLazyCompoundVal(store, R); } SVal RegionStoreManager::RetrieveArray(Store store, const TypedRegion * R) { - assert(isa<ConstantArrayType>(R->getValueType(getContext()))); + assert(isa<ConstantArrayType>(R->getValueType())); return ValMgr.makeLazyCompoundVal(store, R); } @@ -1371,38 +1323,26 @@ Store RegionStoreManager::Bind(Store store, Loc L, SVal V) { // Check if the region is a struct region. if (const TypedRegion* TR = dyn_cast<TypedRegion>(R)) - if (TR->getValueType(getContext())->isStructureOrClassType()) + if (TR->getValueType()->isStructureOrClassType()) return BindStruct(store, TR, V); - // Special case: the current region represents a cast and it and the super - // region both have pointer types or intptr_t types. If so, perform the - // bind to the super region. - // This is needed to support OSAtomicCompareAndSwap and friends or other - // loads that treat integers as pointers and vis versa. if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { if (ER->getIndex().isZeroConstant()) { if (const TypedRegion *superR = dyn_cast<TypedRegion>(ER->getSuperRegion())) { - ASTContext &Ctx = getContext(); - QualType superTy = superR->getValueType(Ctx); - QualType erTy = ER->getValueType(Ctx); - - if (IsAnyPointerOrIntptr(superTy, Ctx) && - IsAnyPointerOrIntptr(erTy, Ctx)) { - V = ValMgr.getSValuator().EvalCast(V, superTy, erTy); - return Bind(store, loc::MemRegionVal(superR), V); - } + QualType superTy = superR->getValueType(); // For now, just invalidate the fields of the struct/union/class. + // This is for test rdar_test_7185607 in misc-ps-region-store.m. // FIXME: Precisely handle the fields of the record. - if (superTy->isRecordType()) - return InvalidateRegion(store, superR, NULL, 0, NULL); + if (superTy->isStructureOrClassType()) + return KillStruct(store, superR, UnknownVal()); } } } else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { // Binding directly to a symbolic region should be treated as binding // to element 0. - QualType T = SR->getSymbol()->getType(getContext()); + QualType T = SR->getSymbol()->getType(Ctx); // FIXME: Is this the right way to handle symbols that are references? if (const PointerType *PT = T->getAs<PointerType>()) @@ -1454,7 +1394,7 @@ Store RegionStoreManager::setImplicitDefaultValue(Store store, else if (T->isStructureOrClassType() || T->isArrayType()) { // Set the default value to a zero constant when it is a structure // or array. The type doesn't really matter. - V = ValMgr.makeZeroVal(ValMgr.getContext().IntTy); + V = ValMgr.makeZeroVal(Ctx.IntTy); } else { return store; @@ -1466,44 +1406,21 @@ Store RegionStoreManager::setImplicitDefaultValue(Store store, Store RegionStoreManager::BindArray(Store store, const TypedRegion* R, SVal Init) { - ASTContext &Ctx = getContext(); - const ArrayType *AT = - cast<ArrayType>(Ctx.getCanonicalType(R->getValueType(Ctx))); + const ArrayType *AT =cast<ArrayType>(Ctx.getCanonicalType(R->getValueType())); QualType ElementTy = AT->getElementType(); Optional<uint64_t> Size; if (const ConstantArrayType* CAT = dyn_cast<ConstantArrayType>(AT)) Size = CAT->getSize().getZExtValue(); - // Check if the init expr is a StringLiteral. - if (isa<loc::MemRegionVal>(Init)) { - const MemRegion* InitR = cast<loc::MemRegionVal>(Init).getRegion(); - const StringLiteral* S = cast<StringRegion>(InitR)->getStringLiteral(); - const char* str = S->getStrData(); - unsigned len = S->getByteLength(); - unsigned j = 0; - - // Copy bytes from the string literal into the target array. Trailing bytes - // in the array that are not covered by the string literal are initialized - // to zero. + // Check if the init expr is a string literal. + if (loc::MemRegionVal *MRV = dyn_cast<loc::MemRegionVal>(&Init)) { + const StringRegion *S = cast<StringRegion>(MRV->getRegion()); - // We assume that string constants are bound to - // constant arrays. - uint64_t size = Size.getValue(); - - for (uint64_t i = 0; i < size; ++i, ++j) { - if (j >= len) - break; - - SVal Idx = ValMgr.makeArrayIndex(i); - const ElementRegion* ER = MRMgr.getElementRegion(ElementTy, Idx, R, - getContext()); - - SVal V = ValMgr.makeIntVal(str[j], sizeof(char)*8, true); - store = Bind(store, loc::MemRegionVal(ER), V); - } - - return store; + // Treat the string as a lazy compound value. + nonloc::LazyCompoundVal LCV = + cast<nonloc::LazyCompoundVal>(ValMgr.makeLazyCompoundVal(store, S)); + return CopyLazyBindings(LCV, store, R); } // Handle lazy compound values. @@ -1525,10 +1442,12 @@ Store RegionStoreManager::BindArray(Store store, const TypedRegion* R, break; SVal Idx = ValMgr.makeArrayIndex(i); - const ElementRegion *ER = MRMgr.getElementRegion(ElementTy, Idx, R, getContext()); + const ElementRegion *ER = MRMgr.getElementRegion(ElementTy, Idx, R, Ctx); if (ElementTy->isStructureOrClassType()) store = BindStruct(store, ER, *VI); + else if (ElementTy->isArrayType()) + store = BindArray(store, ER, *VI); else store = Bind(store, ValMgr.makeLoc(ER), *VI); } @@ -1547,7 +1466,7 @@ Store RegionStoreManager::BindStruct(Store store, const TypedRegion* R, if (!Features.supportsFields()) return store; - QualType T = R->getValueType(getContext()); + QualType T = R->getValueType(); assert(T->isStructureOrClassType()); const RecordType* RT = T->getAs<RecordType>(); @@ -1560,10 +1479,13 @@ Store RegionStoreManager::BindStruct(Store store, const TypedRegion* R, if (const nonloc::LazyCompoundVal *LCV=dyn_cast<nonloc::LazyCompoundVal>(&V)) return CopyLazyBindings(*LCV, store, R); - // We may get non-CompoundVal accidentally due to imprecise cast logic. - // Ignore them and kill the field values. - if (V.isUnknown() || !isa<nonloc::CompoundVal>(V)) - return KillStruct(store, R); + // We may get non-CompoundVal accidentally due to imprecise cast logic or + // that we are binding symbolic struct value. Kill the field values, and if + // the value is symbolic go and bind it as a "default" binding. + if (V.isUnknown() || !isa<nonloc::CompoundVal>(V)) { + SVal SV = isa<nonloc::SymbolVal>(V) ? V : UnknownVal(); + return KillStruct(store, R, SV); + } nonloc::CompoundVal& CV = cast<nonloc::CompoundVal>(V); nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); @@ -1596,14 +1518,15 @@ Store RegionStoreManager::BindStruct(Store store, const TypedRegion* R, return store; } -Store RegionStoreManager::KillStruct(Store store, const TypedRegion* R) { +Store RegionStoreManager::KillStruct(Store store, const TypedRegion* R, + SVal DefaultVal) { RegionBindings B = GetRegionBindings(store); llvm::OwningPtr<RegionStoreSubRegionMap> SubRegions(getRegionStoreSubRegionMap(store)); RemoveSubRegionBindings(B, R, *SubRegions); // Set the default value of the struct region to "unknown". - return Add(B, R, BindingKey::Default, UnknownVal()).getRoot(); + return Add(B, R, BindingKey::Default, DefaultVal).getRoot(); } Store RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V, @@ -1627,21 +1550,10 @@ Store RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V, // "Raw" retrievals and bindings. //===----------------------------------------------------------------------===// -BindingKey BindingKey::Make(const MemRegion *R, Kind k) { - if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { - const RegionRawOffset &O = ER->getAsRawOffset(); - - if (O.getRegion()) - return BindingKey(O.getRegion(), O.getByteOffset(), k); - - // FIXME: There are some ElementRegions for which we cannot compute - // raw offsets yet, including regions with symbolic offsets. - } - - return BindingKey(R, 0, k); -} RegionBindings RegionStoreManager::Add(RegionBindings B, BindingKey K, SVal V) { + if (!K.isValid()) + return B; return RBFactory.Add(B, K, V); } @@ -1651,6 +1563,8 @@ RegionBindings RegionStoreManager::Add(RegionBindings B, const MemRegion *R, } const SVal *RegionStoreManager::Lookup(RegionBindings B, BindingKey K) { + if (!K.isValid()) + return NULL; return B.lookup(K); } @@ -1661,6 +1575,8 @@ const SVal *RegionStoreManager::Lookup(RegionBindings B, } RegionBindings RegionStoreManager::Remove(RegionBindings B, BindingKey K) { + if (!K.isValid()) + return B; return RBFactory.Remove(B, K); } @@ -1669,11 +1585,6 @@ RegionBindings RegionStoreManager::Remove(RegionBindings B, const MemRegion *R, return Remove(B, BindingKey::Make(R, k)); } -Store RegionStoreManager::Remove(Store store, BindingKey K) { - RegionBindings B = GetRegionBindings(store); - return Remove(B, K).getRoot(); -} - //===----------------------------------------------------------------------===// // State pruning. //===----------------------------------------------------------------------===// @@ -1818,12 +1729,12 @@ bool RemoveDeadBindingsWorker::UpdatePostponed() { return changed; } -const GRState *RegionStoreManager::RemoveDeadBindings(GRState &state, +Store RegionStoreManager::RemoveDeadBindings(Store store, const StackFrameContext *LCtx, SymbolReaper& SymReaper, llvm::SmallVectorImpl<const MemRegion*>& RegionRoots) { - RegionBindings B = GetRegionBindings(state.getStore()); + RegionBindings B = GetRegionBindings(store); RemoveDeadBindingsWorker W(*this, StateMgr, B, SymReaper, LCtx); W.GenerateClusters(); @@ -1856,14 +1767,13 @@ const GRState *RegionStoreManager::RemoveDeadBindings(GRState &state, for (; SI != SE; ++SI) SymReaper.maybeDead(*SI); } - state.setStore(B.getRoot()); - const GRState *s = StateMgr.getPersistentState(state); - return s; + + return B.getRoot(); } -GRState const *RegionStoreManager::EnterStackFrame(GRState const *state, - StackFrameContext const *frame) { +Store RegionStoreManager::EnterStackFrame(const GRState *state, + const StackFrameContext *frame) { FunctionDecl const *FD = cast<FunctionDecl>(frame->getDecl()); FunctionDecl::param_const_iterator PI = FD->param_begin(); Store store = state->getStore(); @@ -1887,9 +1797,9 @@ GRState const *RegionStoreManager::EnterStackFrame(GRState const *state, store = Bind(store, ValMgr.makeLoc(MRMgr.getVarRegion(*PI,frame)),ArgVal); } } else - assert(0 && "Unhandled call expression."); + llvm_unreachable("Unhandled call expression."); - return state->makeWithStore(store); + return store; } //===----------------------------------------------------------------------===// diff --git a/lib/Checker/ReturnPointerRangeChecker.cpp b/lib/Checker/ReturnPointerRangeChecker.cpp index 14edf56..a9eb5ce 100644 --- a/lib/Checker/ReturnPointerRangeChecker.cpp +++ b/lib/Checker/ReturnPointerRangeChecker.cpp @@ -66,7 +66,7 @@ void ReturnPointerRangeChecker::PreVisitReturnStmt(CheckerContext &C, DefinedOrUnknownSVal NumElements = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), - ER->getValueType(C.getASTContext())); + ER->getValueType()); const GRState *StInBound = state->AssumeInBound(Idx, NumElements, true); const GRState *StOutBound = state->AssumeInBound(Idx, NumElements, false); diff --git a/lib/Checker/ReturnUndefChecker.cpp b/lib/Checker/ReturnUndefChecker.cpp index 52a0b30..73d1890 100644 --- a/lib/Checker/ReturnUndefChecker.cpp +++ b/lib/Checker/ReturnUndefChecker.cpp @@ -61,6 +61,7 @@ void ReturnUndefChecker::PreVisitReturnStmt(CheckerContext &C, EnhancedBugReport *report = new EnhancedBugReport(*BT, BT->getDescription(), N); + report->addRange(RetE->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, RetE); C.EmitReport(report); diff --git a/lib/Checker/SVals.cpp b/lib/Checker/SVals.cpp index 7a99e86..97ba74e 100644 --- a/lib/Checker/SVals.cpp +++ b/lib/Checker/SVals.cpp @@ -62,6 +62,9 @@ const FunctionDecl *SVal::getAsFunctionDecl() const { /// wraps a symbol, return that SymbolRef. Otherwise return 0. // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? SymbolRef SVal::getAsLocSymbol() const { + if (const nonloc::LocAsInteger *X = dyn_cast<nonloc::LocAsInteger>(this)) + return X->getLoc().getAsLocSymbol(); + if (const loc::MemRegionVal *X = dyn_cast<loc::MemRegionVal>(this)) { const MemRegion *R = X->StripCasts(); if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R)) @@ -247,8 +250,8 @@ SVal loc::ConcreteInt::EvalBinOp(BasicValueFactory& BasicVals, BinaryOperator::Opcode Op, const loc::ConcreteInt& R) const { - assert (Op == BinaryOperator::Add || Op == BinaryOperator::Sub || - (Op >= BinaryOperator::LT && Op <= BinaryOperator::NE)); + assert (Op == BO_Add || Op == BO_Sub || + (Op >= BO_LT && Op <= BO_NE)); const llvm::APSInt* X = BasicVals.EvaluateAPSInt(Op, getValue(), R.getValue()); diff --git a/lib/Checker/SValuator.cpp b/lib/Checker/SValuator.cpp index a7e15fc..273e574 100644 --- a/lib/Checker/SValuator.cpp +++ b/lib/Checker/SValuator.cpp @@ -37,7 +37,7 @@ SVal SValuator::EvalBinOp(const GRState *ST, BinaryOperator::Opcode Op, if (isa<Loc>(R)) { // Support pointer arithmetic where the addend is on the left // and the pointer on the right. - assert(Op == BinaryOperator::Add); + assert(Op == BO_Add); // Commute the operands. return EvalBinOpLN(ST, Op, cast<Loc>(R), cast<NonLoc>(L), T); @@ -49,7 +49,7 @@ SVal SValuator::EvalBinOp(const GRState *ST, BinaryOperator::Opcode Op, DefinedOrUnknownSVal SValuator::EvalEQ(const GRState *ST, DefinedOrUnknownSVal L, DefinedOrUnknownSVal R) { - return cast<DefinedOrUnknownSVal>(EvalBinOp(ST, BinaryOperator::EQ, L, R, + return cast<DefinedOrUnknownSVal>(EvalBinOp(ST, BO_EQ, L, R, ValMgr.getContext().IntTy)); } diff --git a/lib/Checker/SimpleConstraintManager.cpp b/lib/Checker/SimpleConstraintManager.cpp index 321381b..04496e1 100644 --- a/lib/Checker/SimpleConstraintManager.cpp +++ b/lib/Checker/SimpleConstraintManager.cpp @@ -31,17 +31,17 @@ bool SimpleConstraintManager::canReasonAbout(SVal X) const { if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) { switch (SIE->getOpcode()) { // We don't reason yet about bitwise-constraints on symbolic values. - case BinaryOperator::And: - case BinaryOperator::Or: - case BinaryOperator::Xor: + case BO_And: + case BO_Or: + case BO_Xor: return false; // We don't reason yet about these arithmetic constraints on // symbolic values. - case BinaryOperator::Mul: - case BinaryOperator::Div: - case BinaryOperator::Rem: - case BinaryOperator::Shl: - case BinaryOperator::Shr: + case BO_Mul: + case BO_Div: + case BO_Rem: + case BO_Shl: + case BO_Shr: return false; // All other cases. default: @@ -125,12 +125,12 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { switch (op) { default: assert(false && "Invalid opcode."); - case BinaryOperator::LT: return BinaryOperator::GE; - case BinaryOperator::GT: return BinaryOperator::LE; - case BinaryOperator::LE: return BinaryOperator::GT; - case BinaryOperator::GE: return BinaryOperator::LT; - case BinaryOperator::EQ: return BinaryOperator::NE; - case BinaryOperator::NE: return BinaryOperator::EQ; + case BO_LT: return BO_GE; + case BO_GT: return BO_LE; + case BO_LE: return BO_GT; + case BO_GE: return BO_LT; + case BO_EQ: return BO_NE; + case BO_NE: return BO_EQ; } } @@ -178,7 +178,7 @@ const GRState *SimpleConstraintManager::AssumeAux(const GRState *state, if (!BinaryOperator::isComparisonOp(op)) { QualType T = SymMgr.getType(SE); const llvm::APSInt &zero = BasicVals.getValue(0, T); - op = (Assumption ? BinaryOperator::NE : BinaryOperator::EQ); + op = (Assumption ? BO_NE : BO_EQ); return AssumeSymRel(state, SE, op, zero); } @@ -238,10 +238,10 @@ const GRState *SimpleConstraintManager::AssumeSymRel(const GRState *state, // Get the constant out of the expression "($sym+constant1)". switch (SE->getOpcode()) { - case BinaryOperator::Add: + case BO_Add: Adjustment = SE->getRHS(); break; - case BinaryOperator::Sub: + case BO_Sub: Adjustment = -SE->getRHS(); break; default: @@ -276,48 +276,24 @@ const GRState *SimpleConstraintManager::AssumeSymRel(const GRState *state, // No logic yet for other operators. Assume the constraint is feasible. return state; - case BinaryOperator::EQ: + case BO_EQ: return AssumeSymEQ(state, Sym, ConvertedInt, Adjustment); - case BinaryOperator::NE: + case BO_NE: return AssumeSymNE(state, Sym, ConvertedInt, Adjustment); - case BinaryOperator::GT: + case BO_GT: return AssumeSymGT(state, Sym, ConvertedInt, Adjustment); - case BinaryOperator::GE: + case BO_GE: return AssumeSymGE(state, Sym, ConvertedInt, Adjustment); - case BinaryOperator::LT: + case BO_LT: return AssumeSymLT(state, Sym, ConvertedInt, Adjustment); - case BinaryOperator::LE: + case BO_LE: return AssumeSymLE(state, Sym, ConvertedInt, Adjustment); } // end switch } -const GRState *SimpleConstraintManager::AssumeInBound(const GRState *state, - DefinedSVal Idx, - DefinedSVal UpperBound, - bool Assumption) { - - // Only support ConcreteInt for now. - if (!(isa<nonloc::ConcreteInt>(Idx) && isa<nonloc::ConcreteInt>(UpperBound))) - return state; - - const llvm::APSInt& Zero = state->getBasicVals().getZeroWithPtrWidth(false); - llvm::APSInt IdxV = cast<nonloc::ConcreteInt>(Idx).getValue(); - // IdxV might be too narrow. - if (IdxV.getBitWidth() < Zero.getBitWidth()) - IdxV.extend(Zero.getBitWidth()); - // UBV might be too narrow, too. - llvm::APSInt UBV = cast<nonloc::ConcreteInt>(UpperBound).getValue(); - if (UBV.getBitWidth() < Zero.getBitWidth()) - UBV.extend(Zero.getBitWidth()); - - bool InBound = (Zero <= IdxV) && (IdxV < UBV); - bool isFeasible = Assumption ? InBound : !InBound; - return isFeasible ? state : NULL; -} - } // end of namespace clang diff --git a/lib/Checker/SimpleConstraintManager.h b/lib/Checker/SimpleConstraintManager.h index 45057e6..96811b3 100644 --- a/lib/Checker/SimpleConstraintManager.h +++ b/lib/Checker/SimpleConstraintManager.h @@ -43,10 +43,6 @@ public: BinaryOperator::Opcode op, const llvm::APSInt& Int); - const GRState *AssumeInBound(const GRState *state, DefinedSVal Idx, - DefinedSVal UpperBound, - bool Assumption); - protected: //===------------------------------------------------------------------===// diff --git a/lib/Checker/SimpleSValuator.cpp b/lib/Checker/SimpleSValuator.cpp index 3bc4ee7..782cd4f 100644 --- a/lib/Checker/SimpleSValuator.cpp +++ b/lib/Checker/SimpleSValuator.cpp @@ -168,12 +168,12 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { switch (op) { default: assert(false && "Invalid opcode."); - case BinaryOperator::LT: return BinaryOperator::GE; - case BinaryOperator::GT: return BinaryOperator::LE; - case BinaryOperator::LE: return BinaryOperator::GT; - case BinaryOperator::GE: return BinaryOperator::LT; - case BinaryOperator::EQ: return BinaryOperator::NE; - case BinaryOperator::NE: return BinaryOperator::EQ; + case BO_LT: return BO_GE; + case BO_GT: return BO_LE; + case BO_LE: return BO_GT; + case BO_GE: return BO_LT; + case BO_EQ: return BO_NE; + case BO_NE: return BO_EQ; } } @@ -181,12 +181,12 @@ static BinaryOperator::Opcode ReverseComparison(BinaryOperator::Opcode op) { switch (op) { default: assert(false && "Invalid opcode."); - case BinaryOperator::LT: return BinaryOperator::GT; - case BinaryOperator::GT: return BinaryOperator::LT; - case BinaryOperator::LE: return BinaryOperator::GE; - case BinaryOperator::GE: return BinaryOperator::LE; - case BinaryOperator::EQ: - case BinaryOperator::NE: + case BO_LT: return BO_GT; + case BO_GT: return BO_LT; + case BO_LE: return BO_GE; + case BO_GE: return BO_LE; + case BO_EQ: + case BO_NE: return op; } } @@ -202,14 +202,14 @@ SVal SimpleSValuator::MakeSymIntVal(const SymExpr *LHS, default: // We can't reduce this case; just treat it normally. break; - case BinaryOperator::Mul: + case BO_Mul: // a*0 and a*1 if (RHS == 0) return ValMgr.makeIntVal(0, resultTy); else if (RHS == 1) isIdempotent = true; break; - case BinaryOperator::Div: + case BO_Div: // a/0 and a/1 if (RHS == 0) // This is also handled elsewhere. @@ -217,7 +217,7 @@ SVal SimpleSValuator::MakeSymIntVal(const SymExpr *LHS, else if (RHS == 1) isIdempotent = true; break; - case BinaryOperator::Rem: + case BO_Rem: // a%0 and a%1 if (RHS == 0) // This is also handled elsewhere. @@ -225,23 +225,23 @@ SVal SimpleSValuator::MakeSymIntVal(const SymExpr *LHS, else if (RHS == 1) return ValMgr.makeIntVal(0, resultTy); break; - case BinaryOperator::Add: - case BinaryOperator::Sub: - case BinaryOperator::Shl: - case BinaryOperator::Shr: - case BinaryOperator::Xor: + case BO_Add: + case BO_Sub: + case BO_Shl: + case BO_Shr: + case BO_Xor: // a+0, a-0, a<<0, a>>0, a^0 if (RHS == 0) isIdempotent = true; break; - case BinaryOperator::And: + case BO_And: // a&0 and a&(~0) if (RHS == 0) return ValMgr.makeIntVal(0, resultTy); else if (RHS.isAllOnesValue()) isIdempotent = true; break; - case BinaryOperator::Or: + case BO_Or: // a|0 and a|(~0) if (RHS == 0) isIdempotent = true; @@ -275,19 +275,19 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state, switch (op) { default: break; - case BinaryOperator::EQ: - case BinaryOperator::LE: - case BinaryOperator::GE: + case BO_EQ: + case BO_LE: + case BO_GE: return ValMgr.makeTruthVal(true, resultTy); - case BinaryOperator::LT: - case BinaryOperator::GT: - case BinaryOperator::NE: + case BO_LT: + case BO_GT: + case BO_NE: return ValMgr.makeTruthVal(false, resultTy); - case BinaryOperator::Xor: - case BinaryOperator::Sub: + case BO_Xor: + case BO_Sub: return ValMgr.makeIntVal(0, resultTy); - case BinaryOperator::Or: - case BinaryOperator::And: + case BO_Or: + case BO_And: return EvalCastNL(lhs, resultTy); } @@ -312,9 +312,9 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state, } default: switch (op) { - case BinaryOperator::EQ: + case BO_EQ: return ValMgr.makeTruthVal(false, resultTy); - case BinaryOperator::NE: + case BO_NE: return ValMgr.makeTruthVal(true, resultTy); default: // This case also handles pointer arithmetic. @@ -333,7 +333,7 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state, return UnknownVal(); // Is this a logical not? (!x is represented as x == 0.) - if (op == BinaryOperator::EQ && rhs.isZeroConstant()) { + if (op == BO_EQ && rhs.isZeroConstant()) { // We know how to negate certain expressions. Simplify them here. BinaryOperator::Opcode opc = symIntExpr->getOpcode(); @@ -342,34 +342,34 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state, // We don't know how to negate this operation. // Just handle it as if it were a normal comparison to 0. break; - case BinaryOperator::LAnd: - case BinaryOperator::LOr: + case BO_LAnd: + case BO_LOr: assert(false && "Logical operators handled by branching logic."); return UnknownVal(); - case BinaryOperator::Assign: - case BinaryOperator::MulAssign: - case BinaryOperator::DivAssign: - case BinaryOperator::RemAssign: - case BinaryOperator::AddAssign: - case BinaryOperator::SubAssign: - case BinaryOperator::ShlAssign: - case BinaryOperator::ShrAssign: - case BinaryOperator::AndAssign: - case BinaryOperator::XorAssign: - case BinaryOperator::OrAssign: - case BinaryOperator::Comma: + case BO_Assign: + case BO_MulAssign: + case BO_DivAssign: + case BO_RemAssign: + case BO_AddAssign: + case BO_SubAssign: + case BO_ShlAssign: + case BO_ShrAssign: + case BO_AndAssign: + case BO_XorAssign: + case BO_OrAssign: + case BO_Comma: assert(false && "'=' and ',' operators handled by GRExprEngine."); return UnknownVal(); - case BinaryOperator::PtrMemD: - case BinaryOperator::PtrMemI: + case BO_PtrMemD: + case BO_PtrMemI: assert(false && "Pointer arithmetic not handled here."); return UnknownVal(); - case BinaryOperator::LT: - case BinaryOperator::GT: - case BinaryOperator::LE: - case BinaryOperator::GE: - case BinaryOperator::EQ: - case BinaryOperator::NE: + case BO_LT: + case BO_GT: + case BO_LE: + case BO_GE: + case BO_EQ: + case BO_NE: // Negate the comparison and make a value. opc = NegateComparison(opc); assert(symIntExpr->getType(ValMgr.getContext()) == resultTy); @@ -402,9 +402,9 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state, const llvm::APSInt *newRHS; if (lop == op) - newRHS = BVF.EvaluateAPSInt(BinaryOperator::Add, first, second); + newRHS = BVF.EvaluateAPSInt(BO_Add, first, second); else - newRHS = BVF.EvaluateAPSInt(BinaryOperator::Sub, first, second); + newRHS = BVF.EvaluateAPSInt(BO_Sub, first, second); return MakeSymIntVal(symIntExpr->getLHS(), lop, *newRHS, resultTy); } } @@ -429,26 +429,26 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state, lhs = tmp; switch (op) { - case BinaryOperator::LT: - case BinaryOperator::GT: - case BinaryOperator::LE: - case BinaryOperator::GE: + case BO_LT: + case BO_GT: + case BO_LE: + case BO_GE: op = ReverseComparison(op); continue; - case BinaryOperator::EQ: - case BinaryOperator::NE: - case BinaryOperator::Add: - case BinaryOperator::Mul: - case BinaryOperator::And: - case BinaryOperator::Xor: - case BinaryOperator::Or: + case BO_EQ: + case BO_NE: + case BO_Add: + case BO_Mul: + case BO_And: + case BO_Xor: + case BO_Or: continue; - case BinaryOperator::Shr: + case BO_Shr: if (lhsValue.isAllOnesValue() && lhsValue.isSigned()) // At this point lhs and rhs have been swapped. return rhs; // FALL-THROUGH - case BinaryOperator::Shl: + case BO_Shl: if (lhsValue == 0) // At this point lhs and rhs have been swapped. return rhs; @@ -461,10 +461,12 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state, case nonloc::SymbolValKind: { nonloc::SymbolVal *slhs = cast<nonloc::SymbolVal>(&lhs); SymbolRef Sym = slhs->getSymbol(); - + + ASTContext& Ctx = ValMgr.getContext(); + // Does the symbol simplify to a constant? If so, "fold" the constant // by setting 'lhs' to a ConcreteInt and try again. - if (Sym->getType(ValMgr.getContext())->isIntegerType()) + if (Sym->getType(Ctx)->isIntegerType()) if (const llvm::APSInt *Constant = state->getSymVal(Sym)) { // The symbol evaluates to a constant. If necessary, promote the // folded constant (LHS) to the result type. @@ -474,7 +476,7 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state, // Also promote the RHS (if necessary). - // For shifts, it necessary promote the RHS to the result type. + // For shifts, it is not necessary to promote the RHS. if (BinaryOperator::isShiftOp(op)) continue; @@ -486,7 +488,20 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state, continue; } - + + // Is the RHS a symbol we can simplify? + if (const nonloc::SymbolVal *srhs = dyn_cast<nonloc::SymbolVal>(&rhs)) { + SymbolRef RSym = srhs->getSymbol(); + if (RSym->getType(Ctx)->isIntegerType()) { + if (const llvm::APSInt *Constant = state->getSymVal(RSym)) { + // The symbol evaluates to a constant. + BasicValueFactory &BVF = ValMgr.getBasicValueFactory(); + const llvm::APSInt &rhs_I = BVF.Convert(resultTy, *Constant); + rhs = nonloc::ConcreteInt(rhs_I); + } + } + } + if (isa<nonloc::ConcreteInt>(rhs)) { return MakeSymIntVal(slhs->getSymbol(), op, cast<nonloc::ConcreteInt>(rhs).getValue(), @@ -510,7 +525,7 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state, // calling this function with another operation (PR7527). We don't attempt to // model this for now, but it could be useful, particularly when the // "location" is actually an integer value that's been passed through a void*. - if (!(BinaryOperator::isComparisonOp(op) || op == BinaryOperator::Sub)) + if (!(BinaryOperator::isComparisonOp(op) || op == BO_Sub)) return UnknownVal(); // Special cases for when both sides are identical. @@ -519,15 +534,15 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state, default: assert(false && "Unimplemented operation for two identical values"); return UnknownVal(); - case BinaryOperator::Sub: + case BO_Sub: return ValMgr.makeZeroVal(resultTy); - case BinaryOperator::EQ: - case BinaryOperator::LE: - case BinaryOperator::GE: + case BO_EQ: + case BO_LE: + case BO_GE: return ValMgr.makeTruthVal(true, resultTy); - case BinaryOperator::NE: - case BinaryOperator::LT: - case BinaryOperator::GT: + case BO_NE: + case BO_LT: + case BO_GT: return ValMgr.makeTruthVal(false, resultTy); } } @@ -543,15 +558,15 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state, switch (op) { default: break; - case BinaryOperator::Sub: + case BO_Sub: return EvalCastL(lhs, resultTy); - case BinaryOperator::EQ: - case BinaryOperator::LE: - case BinaryOperator::LT: + case BO_EQ: + case BO_LE: + case BO_LT: return ValMgr.makeTruthVal(false, resultTy); - case BinaryOperator::NE: - case BinaryOperator::GT: - case BinaryOperator::GE: + case BO_NE: + case BO_GT: + case BO_GE: return ValMgr.makeTruthVal(true, resultTy); } } @@ -594,13 +609,13 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state, switch (op) { default: break; - case BinaryOperator::EQ: - case BinaryOperator::GT: - case BinaryOperator::GE: + case BO_EQ: + case BO_GT: + case BO_GE: return ValMgr.makeTruthVal(false, resultTy); - case BinaryOperator::NE: - case BinaryOperator::LT: - case BinaryOperator::LE: + case BO_NE: + case BO_LT: + case BO_LE: return ValMgr.makeTruthVal(true, resultTy); } } @@ -624,15 +639,15 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state, switch (op) { default: break; - case BinaryOperator::Sub: + case BO_Sub: return EvalCastL(lhs, resultTy); - case BinaryOperator::EQ: - case BinaryOperator::LT: - case BinaryOperator::LE: + case BO_EQ: + case BO_LT: + case BO_LE: return ValMgr.makeTruthVal(false, resultTy); - case BinaryOperator::NE: - case BinaryOperator::GT: - case BinaryOperator::GE: + case BO_NE: + case BO_GT: + case BO_GE: return ValMgr.makeTruthVal(true, resultTy); } } @@ -660,9 +675,9 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state, switch (op) { default: return UnknownVal(); - case BinaryOperator::EQ: + case BO_EQ: return ValMgr.makeTruthVal(false, resultTy); - case BinaryOperator::NE: + case BO_NE: return ValMgr.makeTruthVal(true, resultTy); } } @@ -711,8 +726,8 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state, } // If the element indexes aren't comparable, see if the raw offsets are. - RegionRawOffset LeftOffset = LeftER->getAsRawOffset(); - RegionRawOffset RightOffset = RightER->getAsRawOffset(); + RegionRawOffset LeftOffset = LeftER->getAsArrayOffset(); + RegionRawOffset RightOffset = RightER->getAsArrayOffset(); if (LeftOffset.getRegion() != NULL && LeftOffset.getRegion() == RightOffset.getRegion()) { @@ -722,17 +737,17 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state, switch (op) { default: return UnknownVal(); - case BinaryOperator::LT: + case BO_LT: return ValMgr.makeTruthVal(left < right, resultTy); - case BinaryOperator::GT: + case BO_GT: return ValMgr.makeTruthVal(left > right, resultTy); - case BinaryOperator::LE: + case BO_LE: return ValMgr.makeTruthVal(left <= right, resultTy); - case BinaryOperator::GE: + case BO_GE: return ValMgr.makeTruthVal(left >= right, resultTy); - case BinaryOperator::EQ: + case BO_EQ: return ValMgr.makeTruthVal(left == right, resultTy); - case BinaryOperator::NE: + case BO_NE: return ValMgr.makeTruthVal(left != right, resultTy); } } @@ -770,16 +785,16 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state, // We know for sure that the two fields are not the same, since that // would have given us the same SVal. - if (op == BinaryOperator::EQ) + if (op == BO_EQ) return ValMgr.makeTruthVal(false, resultTy); - if (op == BinaryOperator::NE) + if (op == BO_NE) return ValMgr.makeTruthVal(true, resultTy); // Iterate through the fields and see which one comes first. // [C99 6.7.2.1.13] "Within a structure object, the non-bit-field // members and the units in which bit-fields reside have addresses that // increase in the order in which they are declared." - bool leftFirst = (op == BinaryOperator::LT || op == BinaryOperator::LE); + bool leftFirst = (op == BO_LT || op == BO_LE); for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I!=E; ++I) { if (*I == LeftFD) @@ -818,8 +833,41 @@ SVal SimpleSValuator::EvalBinOpLN(const GRState *state, } } } + + // We are dealing with pointer arithmetic. + + // Handle pointer arithmetic on constant values. + if (nonloc::ConcreteInt *rhsInt = dyn_cast<nonloc::ConcreteInt>(&rhs)) { + if (loc::ConcreteInt *lhsInt = dyn_cast<loc::ConcreteInt>(&lhs)) { + const llvm::APSInt &leftI = lhsInt->getValue(); + assert(leftI.isUnsigned()); + llvm::APSInt rightI(rhsInt->getValue(), /* isUnsigned */ true); + + // Convert the bitwidth of rightI. This should deal with overflow + // since we are dealing with concrete values. + rightI.extOrTrunc(leftI.getBitWidth()); + + // Offset the increment by the pointer size. + llvm::APSInt Multiplicand(rightI.getBitWidth(), /* isUnsigned */ true); + rightI *= Multiplicand; + + // Compute the adjusted pointer. + switch (op) { + case BO_Add: + rightI = leftI + rightI; + break; + case BO_Sub: + rightI = leftI - rightI; + break; + default: + llvm_unreachable("Invalid pointer arithmetic operation"); + } + return loc::ConcreteInt(ValMgr.getBasicValueFactory().getValue(rightI)); + } + } + - // Delegate pointer arithmetic to the StoreManager. + // Delegate remaining pointer arithmetic to the StoreManager. return state->getStateManager().getStoreManager().EvalBinOp(op, lhs, rhs, resultTy); } diff --git a/lib/Checker/StackAddrLeakChecker.cpp b/lib/Checker/StackAddrLeakChecker.cpp index f4a9db6..c67a81d 100644 --- a/lib/Checker/StackAddrLeakChecker.cpp +++ b/lib/Checker/StackAddrLeakChecker.cpp @@ -108,7 +108,7 @@ void StackAddrLeakChecker::EmitStackError(CheckerContext &C, const MemRegion *R, report->addRange(range); C.EmitReport(report); -} +} void StackAddrLeakChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS) { diff --git a/lib/Checker/Store.cpp b/lib/Checker/Store.cpp index b128331..1cb5cd7 100644 --- a/lib/Checker/Store.cpp +++ b/lib/Checker/Store.cpp @@ -21,6 +21,11 @@ StoreManager::StoreManager(GRStateManager &stateMgr) : ValMgr(stateMgr.getValueManager()), StateMgr(stateMgr), MRMgr(ValMgr.getRegionManager()), Ctx(stateMgr.getContext()) {} +Store StoreManager::EnterStackFrame(const GRState *state, + const StackFrameContext *frame) { + return state->getStore(); +} + const MemRegion *StoreManager::MakeElementRegion(const MemRegion *Base, QualType EleTy, uint64_t index) { SVal idx = ValMgr.makeArrayIndex(index); @@ -78,7 +83,7 @@ const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy) // Handle casts from compatible types. if (R->isBoundable()) if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) { - QualType ObjTy = Ctx.getCanonicalType(TR->getValueType(Ctx)); + QualType ObjTy = Ctx.getCanonicalType(TR->getValueType()); if (CanonPointeeTy == ObjTy) return R; } @@ -96,17 +101,10 @@ const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy) assert(0 && "Invalid region cast"); break; } - + case MemRegion::FunctionTextRegionKind: case MemRegion::BlockTextRegionKind: - case MemRegion::BlockDataRegionKind: { - // CodeTextRegion should be cast to only a function or block pointer type, - // although they can in practice be casted to anything, e.g, void*, char*, - // etc. - // Just return the region. - return R; - } - + case MemRegion::BlockDataRegionKind: case MemRegion::StringRegionKind: // FIXME: Need to handle arbitrary downcasts. case MemRegion::SymbolicRegionKind: @@ -139,7 +137,7 @@ const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy) // FIXME: Handle symbolic raw offsets. const ElementRegion *elementR = cast<ElementRegion>(R); - const RegionRawOffset &rawOff = elementR->getAsRawOffset(); + const RegionRawOffset &rawOff = elementR->getAsArrayOffset(); const MemRegion *baseR = rawOff.getRegion(); // If we cannot compute a raw offset, throw up our hands and return @@ -154,7 +152,7 @@ const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy) // check to see if type we are casting to is the same as the base // region. If so, just return the base region. if (const TypedRegion *TR = dyn_cast<TypedRegion>(baseR)) { - QualType ObjTy = Ctx.getCanonicalType(TR->getValueType(Ctx)); + QualType ObjTy = Ctx.getCanonicalType(TR->getValueType()); QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy); if (CanonPointeeTy == ObjTy) return baseR; @@ -217,7 +215,7 @@ SVal StoreManager::CastRetrievedVal(SVal V, const TypedRegion *R, if (performTestOnly) { // Automatically translate references to pointers. - QualType T = R->getValueType(Ctx); + QualType T = R->getValueType(); if (const ReferenceType *RT = T->getAs<ReferenceType>()) T = Ctx.getPointerType(RT->getPointeeType()); @@ -279,10 +277,6 @@ SVal StoreManager::getLValueElement(QualType elementType, SVal Offset, if (Base.isUnknownOrUndef() || isa<loc::ConcreteInt>(Base)) return Base; - // Only handle integer offsets... for now. - if (!isa<nonloc::ConcreteInt>(Offset)) - return UnknownVal(); - const MemRegion* BaseRegion = cast<loc::MemRegionVal>(Base).getRegion(); // Pointer of any type can be cast and used as array base. @@ -311,6 +305,19 @@ SVal StoreManager::getLValueElement(QualType elementType, SVal Offset, return UnknownVal(); const llvm::APSInt& BaseIdxI = cast<nonloc::ConcreteInt>(BaseIdx).getValue(); + + // Only allow non-integer offsets if the base region has no offset itself. + // FIXME: This is a somewhat arbitrary restriction. We should be using + // SValuator here to add the two offsets without checking their types. + if (!isa<nonloc::ConcreteInt>(Offset)) { + if (isa<ElementRegion>(BaseRegion->StripCasts())) + return UnknownVal(); + + return loc::MemRegionVal(MRMgr.getElementRegion(elementType, Offset, + ElemR->getSuperRegion(), + Ctx)); + } + const llvm::APSInt& OffI = cast<nonloc::ConcreteInt>(Offset).getValue(); assert(BaseIdxI.isSigned()); diff --git a/lib/Checker/StreamChecker.cpp b/lib/Checker/StreamChecker.cpp index c527ca2..8553875 100644 --- a/lib/Checker/StreamChecker.cpp +++ b/lib/Checker/StreamChecker.cpp @@ -23,18 +23,49 @@ using namespace clang; namespace { +struct StreamState { + enum Kind { Opened, Closed, OpenFailed, Escaped } K; + const Stmt *S; + + StreamState(Kind k, const Stmt *s) : K(k), S(s) {} + + bool isOpened() const { return K == Opened; } + bool isClosed() const { return K == Closed; } + //bool isOpenFailed() const { return K == OpenFailed; } + //bool isEscaped() const { return K == Escaped; } + + bool operator==(const StreamState &X) const { + return K == X.K && S == X.S; + } + + static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); } + static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); } + static StreamState getOpenFailed(const Stmt *s) { + return StreamState(OpenFailed, s); + } + static StreamState getEscaped(const Stmt *s) { + return StreamState(Escaped, s); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(K); + ID.AddPointer(S); + } +}; + class StreamChecker : public CheckerVisitor<StreamChecker> { - IdentifierInfo *II_fopen, *II_fread, *II_fwrite, + IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread, *II_fwrite, *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos, *II_clearerr, *II_feof, *II_ferror, *II_fileno; - BuiltinBug *BT_nullfp, *BT_illegalwhence; + BuiltinBug *BT_nullfp, *BT_illegalwhence, *BT_doubleclose, *BT_ResourceLeak; public: StreamChecker() - : II_fopen(0), II_fread(0), II_fwrite(0), + : II_fopen(0), II_tmpfile(0) ,II_fclose(0), II_fread(0), II_fwrite(0), II_fseek(0), II_ftell(0), II_rewind(0), II_fgetpos(0), II_fsetpos(0), II_clearerr(0), II_feof(0), II_ferror(0), II_fileno(0), - BT_nullfp(0), BT_illegalwhence(0) {} + BT_nullfp(0), BT_illegalwhence(0), BT_doubleclose(0), + BT_ResourceLeak(0) {} static void *getTag() { static int x; @@ -42,9 +73,14 @@ public: } virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE); + void EvalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper); + void EvalEndPath(GREndPathNodeBuilder &B, void *tag, GRExprEngine &Eng); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); private: void Fopen(CheckerContext &C, const CallExpr *CE); + void Tmpfile(CheckerContext &C, const CallExpr *CE); + void Fclose(CheckerContext &C, const CallExpr *CE); void Fread(CheckerContext &C, const CallExpr *CE); void Fwrite(CheckerContext &C, const CallExpr *CE); void Fseek(CheckerContext &C, const CallExpr *CE); @@ -56,14 +92,25 @@ private: void Feof(CheckerContext &C, const CallExpr *CE); void Ferror(CheckerContext &C, const CallExpr *CE); void Fileno(CheckerContext &C, const CallExpr *CE); + + void OpenFileAux(CheckerContext &C, const CallExpr *CE); - // Return true indicates the stream pointer is NULL. const GRState *CheckNullStream(SVal SV, const GRState *state, CheckerContext &C); + const GRState *CheckDoubleClose(const CallExpr *CE, const GRState *state, + CheckerContext &C); }; } // end anonymous namespace +namespace clang { + template <> + struct GRStateTrait<StreamState> + : public GRStatePartialTrait<llvm::ImmutableMap<SymbolRef, StreamState> > { + static void *GDMIndex() { return StreamChecker::getTag(); } + }; +} + void clang::RegisterStreamChecker(GRExprEngine &Eng) { Eng.registerCheck(new StreamChecker()); } @@ -79,6 +126,10 @@ bool StreamChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { ASTContext &Ctx = C.getASTContext(); if (!II_fopen) II_fopen = &Ctx.Idents.get("fopen"); + if (!II_tmpfile) + II_tmpfile = &Ctx.Idents.get("tmpfile"); + if (!II_fclose) + II_fclose = &Ctx.Idents.get("fclose"); if (!II_fread) II_fread = &Ctx.Idents.get("fread"); if (!II_fwrite) @@ -106,6 +157,14 @@ bool StreamChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { Fopen(C, CE); return true; } + if (FD->getIdentifier() == II_tmpfile) { + Tmpfile(C, CE); + return true; + } + if (FD->getIdentifier() == II_fclose) { + Fclose(C, CE); + return true; + } if (FD->getIdentifier() == II_fread) { Fread(C, CE); return true; @@ -155,21 +214,43 @@ bool StreamChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) { } void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) { + OpenFileAux(C, CE); +} + +void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) { + OpenFileAux(C, CE); +} + +void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) { const GRState *state = C.getState(); unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); ValueManager &ValMgr = C.getValueManager(); DefinedSVal RetVal = cast<DefinedSVal>(ValMgr.getConjuredSymbolVal(0, CE, Count)); state = state->BindExpr(CE, RetVal); - + ConstraintManager &CM = C.getConstraintManager(); // Bifurcate the state into two: one with a valid FILE* pointer, the other // with a NULL. const GRState *stateNotNull, *stateNull; llvm::tie(stateNotNull, stateNull) = CM.AssumeDual(state, RetVal); + + if (SymbolRef Sym = RetVal.getAsSymbol()) { + // if RetVal is not NULL, set the symbol's state to Opened. + stateNotNull = + stateNotNull->set<StreamState>(Sym,StreamState::getOpened(CE)); + stateNull = + stateNull->set<StreamState>(Sym, StreamState::getOpenFailed(CE)); + + C.addTransition(stateNotNull); + C.addTransition(stateNull); + } +} - C.addTransition(stateNotNull); - C.addTransition(stateNull); +void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) { + const GRState *state = CheckDoubleClose(CE, C.getState(), C); + if (state) + C.addTransition(state); } void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) { @@ -285,3 +366,103 @@ const GRState *StreamChecker::CheckNullStream(SVal SV, const GRState *state, } return stateNotNull; } + +const GRState *StreamChecker::CheckDoubleClose(const CallExpr *CE, + const GRState *state, + CheckerContext &C) { + SymbolRef Sym = state->getSVal(CE->getArg(0)).getAsSymbol(); + if (!Sym) + return state; + + const StreamState *SS = state->get<StreamState>(Sym); + + // If the file stream is not tracked, return. + if (!SS) + return state; + + // Check: Double close a File Descriptor could cause undefined behaviour. + // Conforming to man-pages + if (SS->isClosed()) { + ExplodedNode *N = C.GenerateSink(); + if (N) { + if (!BT_doubleclose) + BT_doubleclose = new BuiltinBug("Double fclose", + "Try to close a file Descriptor already" + " closed. Cause undefined behaviour."); + BugReport *R = new BugReport(*BT_doubleclose, + BT_doubleclose->getDescription(), N); + C.EmitReport(R); + } + return NULL; + } + + // Close the File Descriptor. + return state->set<StreamState>(Sym, StreamState::getClosed(CE)); +} + +void StreamChecker::EvalDeadSymbols(CheckerContext &C,SymbolReaper &SymReaper) { + for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), + E = SymReaper.dead_end(); I != E; ++I) { + SymbolRef Sym = *I; + const GRState *state = C.getState(); + const StreamState *SS = state->get<StreamState>(Sym); + if (!SS) + return; + + if (SS->isOpened()) { + ExplodedNode *N = C.GenerateSink(); + if (N) { + if (!BT_ResourceLeak) + BT_ResourceLeak = new BuiltinBug("Resource Leak", + "Opened File never closed. Potential Resource leak."); + BugReport *R = new BugReport(*BT_ResourceLeak, + BT_ResourceLeak->getDescription(), N); + C.EmitReport(R); + } + } + } +} + +void StreamChecker::EvalEndPath(GREndPathNodeBuilder &B, void *tag, + GRExprEngine &Eng) { + SaveAndRestore<bool> OldHasGen(B.HasGeneratedNode); + const GRState *state = B.getState(); + typedef llvm::ImmutableMap<SymbolRef, StreamState> SymMap; + SymMap M = state->get<StreamState>(); + + for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { + StreamState SS = I->second; + if (SS.isOpened()) { + ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); + if (N) { + if (!BT_ResourceLeak) + BT_ResourceLeak = new BuiltinBug("Resource Leak", + "Opened File never closed. Potential Resource leak."); + BugReport *R = new BugReport(*BT_ResourceLeak, + BT_ResourceLeak->getDescription(), N); + Eng.getBugReporter().EmitReport(R); + } + } + } +} + +void StreamChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) { + const Expr *RetE = S->getRetValue(); + if (!RetE) + return; + + const GRState *state = C.getState(); + SymbolRef Sym = state->getSVal(RetE).getAsSymbol(); + + if (!Sym) + return; + + const StreamState *SS = state->get<StreamState>(Sym); + if(!SS) + return; + + if (SS->isOpened()) + state = state->set<StreamState>(Sym, StreamState::getEscaped(S)); + + C.addTransition(state); +} diff --git a/lib/Checker/SymbolManager.cpp b/lib/Checker/SymbolManager.cpp index c2b557e..3b1bb6d 100644 --- a/lib/Checker/SymbolManager.cpp +++ b/lib/Checker/SymbolManager.cpp @@ -28,22 +28,22 @@ static void print(llvm::raw_ostream& os, BinaryOperator::Opcode Op) { default: assert(false && "operator printing not implemented"); break; - case BinaryOperator::Mul: os << '*' ; break; - case BinaryOperator::Div: os << '/' ; break; - case BinaryOperator::Rem: os << '%' ; break; - case BinaryOperator::Add: os << '+' ; break; - case BinaryOperator::Sub: os << '-' ; break; - case BinaryOperator::Shl: os << "<<" ; break; - case BinaryOperator::Shr: os << ">>" ; break; - case BinaryOperator::LT: os << "<" ; break; - case BinaryOperator::GT: os << '>' ; break; - case BinaryOperator::LE: os << "<=" ; break; - case BinaryOperator::GE: os << ">=" ; break; - case BinaryOperator::EQ: os << "==" ; break; - case BinaryOperator::NE: os << "!=" ; break; - case BinaryOperator::And: os << '&' ; break; - case BinaryOperator::Xor: os << '^' ; break; - case BinaryOperator::Or: os << '|' ; break; + case BO_Mul: os << '*' ; break; + case BO_Div: os << '/' ; break; + case BO_Rem: os << '%' ; break; + case BO_Add: os << '+' ; break; + case BO_Sub: os << '-' ; break; + case BO_Shl: os << "<<" ; break; + case BO_Shr: os << ">>" ; break; + case BO_LT: os << "<" ; break; + case BO_GT: os << '>' ; break; + case BO_LE: os << "<=" ; break; + case BO_GE: os << ">=" ; break; + case BO_EQ: os << "==" ; break; + case BO_NE: os << "!=" ; break; + case BO_And: os << '&' ; break; + case BO_Xor: os << '^' ; break; + case BO_Or: os << '|' ; break; } } @@ -78,6 +78,11 @@ void SymbolExtent::dumpToStream(llvm::raw_ostream& os) const { os << "extent_$" << getSymbolID() << '{' << getRegion() << '}'; } +void SymbolMetadata::dumpToStream(llvm::raw_ostream& os) const { + os << "meta_$" << getSymbolID() << '{' + << getRegion() << ',' << T.getAsString() << '}'; +} + void SymbolRegionValue::dumpToStream(llvm::raw_ostream& os) const { os << "reg_$" << getSymbolID() << "<" << R << ">"; } @@ -150,6 +155,24 @@ SymbolManager::getExtentSymbol(const SubRegion *R) { return cast<SymbolExtent>(SD); } +const SymbolMetadata* +SymbolManager::getMetadataSymbol(const MemRegion* R, const Stmt* S, QualType T, + unsigned Count, const void* SymbolTag) { + + llvm::FoldingSetNodeID profile; + SymbolMetadata::Profile(profile, R, S, T, Count, SymbolTag); + void* InsertPos; + SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); + if (!SD) { + SD = (SymExpr*) BPAlloc.Allocate<SymbolMetadata>(); + new (SD) SymbolMetadata(SymbolCounter, R, S, T, Count, SymbolTag); + DataSet.InsertNode(SD, InsertPos); + ++SymbolCounter; + } + + return cast<SymbolMetadata>(SD); +} + const SymIntExpr *SymbolManager::getSymIntExpr(const SymExpr *lhs, BinaryOperator::Opcode op, const llvm::APSInt& v, @@ -191,21 +214,34 @@ QualType SymbolConjured::getType(ASTContext&) const { } QualType SymbolDerived::getType(ASTContext& Ctx) const { - return R->getValueType(Ctx); + return R->getValueType(); } QualType SymbolExtent::getType(ASTContext& Ctx) const { return Ctx.getSizeType(); } +QualType SymbolMetadata::getType(ASTContext&) const { + return T; +} + QualType SymbolRegionValue::getType(ASTContext& C) const { - return R->getValueType(C); + return R->getValueType(); } SymbolManager::~SymbolManager() {} bool SymbolManager::canSymbolicate(QualType T) { - return Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType()); + if (Loc::IsLocType(T)) + return true; + + if (T->isIntegerType()) + return T->isScalarType(); + + if (T->isRecordType()) + return true; + + return false; } void SymbolReaper::markLive(SymbolRef sym) { @@ -213,6 +249,11 @@ void SymbolReaper::markLive(SymbolRef sym) { TheDead.erase(sym); } +void SymbolReaper::markInUse(SymbolRef sym) { + if (isa<SymbolMetadata>(sym)) + MetadataInUse.insert(sym); +} + bool SymbolReaper::maybeDead(SymbolRef sym) { if (isLive(sym)) return false; @@ -221,6 +262,31 @@ bool SymbolReaper::maybeDead(SymbolRef sym) { return true; } +static bool IsLiveRegion(SymbolReaper &Reaper, const MemRegion *MR) { + MR = MR->getBaseRegion(); + + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR)) + return Reaper.isLive(SR->getSymbol()); + + if (const VarRegion *VR = dyn_cast<VarRegion>(MR)) + return Reaper.isLive(VR); + + // FIXME: This is a gross over-approximation. What we really need is a way to + // tell if anything still refers to this region. Unlike SymbolicRegions, + // AllocaRegions don't have associated symbols, though, so we don't actually + // have a way to track their liveness. + if (isa<AllocaRegion>(MR)) + return true; + + if (isa<CXXThisRegion>(MR)) + return true; + + if (isa<MemSpaceRegion>(MR)) + return true; + + return false; +} + bool SymbolReaper::isLive(SymbolRef sym) { if (TheLiving.count(sym)) return true; @@ -234,11 +300,21 @@ bool SymbolReaper::isLive(SymbolRef sym) { } if (const SymbolExtent *extent = dyn_cast<SymbolExtent>(sym)) { - const MemRegion *Base = extent->getRegion()->getBaseRegion(); - if (const VarRegion *VR = dyn_cast<VarRegion>(Base)) - return isLive(VR); - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Base)) - return isLive(SR->getSymbol()); + if (IsLiveRegion(*this, extent->getRegion())) { + markLive(sym); + return true; + } + return false; + } + + if (const SymbolMetadata *metadata = dyn_cast<SymbolMetadata>(sym)) { + if (MetadataInUse.count(sym)) { + if (IsLiveRegion(*this, metadata->getRegion())) { + markLive(sym); + MetadataInUse.erase(sym); + return true; + } + } return false; } @@ -248,16 +324,19 @@ bool SymbolReaper::isLive(SymbolRef sym) { } bool SymbolReaper::isLive(const Stmt* ExprVal) const { - return LCtx->getLiveVariables()->isLive(Loc, ExprVal); + return LCtx->getAnalysisContext()->getRelaxedLiveVariables()-> + isLive(Loc, ExprVal); } bool SymbolReaper::isLive(const VarRegion *VR) const { - const StackFrameContext *SFC = VR->getStackFrame(); + const StackFrameContext *VarContext = VR->getStackFrame(); + const StackFrameContext *CurrentContext = LCtx->getCurrentStackFrame(); + + if (VarContext == CurrentContext) + return LCtx->getAnalysisContext()->getRelaxedLiveVariables()-> + isLive(Loc, VR->getDecl()); - if (SFC == LCtx->getCurrentStackFrame()) - return LCtx->getLiveVariables()->isLive(Loc, VR->getDecl()); - else - return SFC->isParentOf(LCtx->getCurrentStackFrame()); + return VarContext->isParentOf(CurrentContext); } SymbolVisitor::~SymbolVisitor() {} diff --git a/lib/Checker/UndefBranchChecker.cpp b/lib/Checker/UndefBranchChecker.cpp index 9088345..1ff0641 100644 --- a/lib/Checker/UndefBranchChecker.cpp +++ b/lib/Checker/UndefBranchChecker.cpp @@ -29,27 +29,28 @@ class UndefBranchChecker : public Checker { FindUndefExpr(GRStateManager& V, const GRState* S) : VM(V), St(S) {} - Expr* FindExpr(Expr* Ex) { + const Expr* FindExpr(const Expr* Ex) { if (!MatchesCriteria(Ex)) return 0; - for (Stmt::child_iterator I=Ex->child_begin(), E=Ex->child_end();I!=E;++I) - if (Expr* ExI = dyn_cast_or_null<Expr>(*I)) { - Expr* E2 = FindExpr(ExI); + for (Stmt::const_child_iterator I = Ex->child_begin(), + E = Ex->child_end();I!=E;++I) + if (const Expr* ExI = dyn_cast_or_null<Expr>(*I)) { + const Expr* E2 = FindExpr(ExI); if (E2) return E2; } return Ex; } - bool MatchesCriteria(Expr* Ex) { return St->getSVal(Ex).isUndef(); } + bool MatchesCriteria(const Expr* Ex) { return St->getSVal(Ex).isUndef(); } }; public: UndefBranchChecker() : BT(0) {} static void *getTag(); void VisitBranchCondition(GRBranchNodeBuilder &Builder, GRExprEngine &Eng, - Stmt *Condition, void *tag); + const Stmt *Condition, void *tag); }; } @@ -65,7 +66,7 @@ void *UndefBranchChecker::getTag() { void UndefBranchChecker::VisitBranchCondition(GRBranchNodeBuilder &Builder, GRExprEngine &Eng, - Stmt *Condition, void *tag) { + const Stmt *Condition, void *tag){ const GRState *state = Builder.getState(); SVal X = state->getSVal(Condition); if (X.isUndef()) { @@ -81,7 +82,7 @@ void UndefBranchChecker::VisitBranchCondition(GRBranchNodeBuilder &Builder, // subexpressions and roughly look for the most nested subexpression // that binds to Undefined. We then highlight that expression's range. BlockEdge B = cast<BlockEdge>(N->getLocation()); - Expr* Ex = cast<Expr>(B.getSrc()->getTerminatorCondition()); + const Expr* Ex = cast<Expr>(B.getSrc()->getTerminatorCondition()); assert (Ex && "Block must have a terminator."); // Get the predecessor node and check if is a PostStmt with the Stmt diff --git a/lib/Checker/UndefinedAssignmentChecker.cpp b/lib/Checker/UndefinedAssignmentChecker.cpp index 6cef60e..ccc9748 100644 --- a/lib/Checker/UndefinedAssignmentChecker.cpp +++ b/lib/Checker/UndefinedAssignmentChecker.cpp @@ -25,9 +25,8 @@ class UndefinedAssignmentChecker public: UndefinedAssignmentChecker() : BT(0) {} static void *getTag(); - virtual void PreVisitBind(CheckerContext &C, const Stmt *AssignE, - const Stmt *StoreE, SVal location, - SVal val); + virtual void PreVisitBind(CheckerContext &C, const Stmt *StoreE, + SVal location, SVal val); }; } @@ -41,7 +40,6 @@ void *UndefinedAssignmentChecker::getTag() { } void UndefinedAssignmentChecker::PreVisitBind(CheckerContext &C, - const Stmt *AssignE, const Stmt *StoreE, SVal location, SVal val) { @@ -61,8 +59,8 @@ void UndefinedAssignmentChecker::PreVisitBind(CheckerContext &C, // Generate a report for this bug. const Expr *ex = 0; - while (AssignE) { - if (const BinaryOperator *B = dyn_cast<BinaryOperator>(AssignE)) { + while (StoreE) { + if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) { if (B->isCompoundAssignmentOp()) { const GRState *state = C.getState(); if (state->getSVal(B->getLHS()).isUndef()) { @@ -77,7 +75,7 @@ void UndefinedAssignmentChecker::PreVisitBind(CheckerContext &C, break; } - if (const DeclStmt *DS = dyn_cast<DeclStmt>(AssignE)) { + if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) { const VarDecl* VD = dyn_cast<VarDecl>(DS->getSingleDecl()); ex = VD->getInit(); } diff --git a/lib/Checker/UnixAPIChecker.cpp b/lib/Checker/UnixAPIChecker.cpp index e9b8f09..de7346d 100644 --- a/lib/Checker/UnixAPIChecker.cpp +++ b/lib/Checker/UnixAPIChecker.cpp @@ -100,7 +100,7 @@ static void CheckOpen(CheckerContext &C, UnixAPIChecker &UC, NonLoc ocreateFlag = cast<NonLoc>(C.getValueManager().makeIntVal(UC.Val_O_CREAT.getValue(), oflagsEx->getType())); - SVal maskedFlagsUC = C.getSValuator().EvalBinOpNN(state, BinaryOperator::And, + SVal maskedFlagsUC = C.getSValuator().EvalBinOpNN(state, BO_And, oflags, ocreateFlag, oflagsEx->getType()); if (maskedFlagsUC.isUnknownOrUndef()) diff --git a/lib/Checker/UnreachableCodeChecker.cpp b/lib/Checker/UnreachableCodeChecker.cpp new file mode 100644 index 0000000..7a56c7f --- /dev/null +++ b/lib/Checker/UnreachableCodeChecker.cpp @@ -0,0 +1,226 @@ +//==- UnreachableCodeChecker.cpp - Generalized dead code checker -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This file implements a generalized unreachable code checker using a +// path-sensitive analysis. We mark any path visited, and then walk the CFG as a +// post-analysis to determine what was never visited. +// +// A similar flow-sensitive only check exists in Analysis/ReachableCode.cpp +//===----------------------------------------------------------------------===// + +#include "clang/AST/ParentMap.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Checker/PathSensitive/CheckerVisitor.h" +#include "clang/Checker/PathSensitive/ExplodedGraph.h" +#include "clang/Checker/PathSensitive/SVals.h" +#include "clang/Checker/PathSensitive/CheckerHelpers.h" +#include "clang/Checker/BugReporter/BugReporter.h" +#include "GRExprEngineExperimentalChecks.h" +#include "llvm/ADT/SmallPtrSet.h" + +// The number of CFGBlock pointers we want to reserve memory for. This is used +// once for each function we analyze. +#define DEFAULT_CFGBLOCKS 256 + +using namespace clang; + +namespace { +class UnreachableCodeChecker : public CheckerVisitor<UnreachableCodeChecker> { +public: + static void *getTag(); + void VisitEndAnalysis(ExplodedGraph &G, + BugReporter &B, + GRExprEngine &Eng); +private: + static inline const Stmt *getUnreachableStmt(const CFGBlock *CB); + void FindUnreachableEntryPoints(const CFGBlock *CB); + static bool isInvalidPath(const CFGBlock *CB, const ParentMap &PM); + static inline bool isEmptyCFGBlock(const CFGBlock *CB); + + llvm::SmallSet<unsigned, DEFAULT_CFGBLOCKS> reachable; + llvm::SmallSet<unsigned, DEFAULT_CFGBLOCKS> visited; +}; +} + +void *UnreachableCodeChecker::getTag() { + static int x = 0; + return &x; +} + +void clang::RegisterUnreachableCodeChecker(GRExprEngine &Eng) { + Eng.registerCheck(new UnreachableCodeChecker()); +} + +void UnreachableCodeChecker::VisitEndAnalysis(ExplodedGraph &G, + BugReporter &B, + GRExprEngine &Eng) { + // Bail out if we didn't cover all paths + if (Eng.hasWorkRemaining()) + return; + + CFG *C = 0; + ParentMap *PM = 0; + // Iterate over ExplodedGraph + for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end(); + I != E; ++I) { + const ProgramPoint &P = I->getLocation(); + const LocationContext *LC = P.getLocationContext(); + + // Save the CFG if we don't have it already + if (!C) + C = LC->getAnalysisContext()->getUnoptimizedCFG(); + if (!PM) + PM = &LC->getParentMap(); + + if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&P)) { + const CFGBlock *CB = BE->getBlock(); + reachable.insert(CB->getBlockID()); + } + } + + // Bail out if we didn't get the CFG or the ParentMap. + if (!C || !PM) + return; + + ASTContext &Ctx = B.getContext(); + + // Find CFGBlocks that were not covered by any node + for (CFG::const_iterator I = C->begin(); I != C->end(); ++I) { + const CFGBlock *CB = *I; + // Check if the block is unreachable + if (reachable.count(CB->getBlockID())) + continue; + + // Check if the block is empty (an artificial block) + if (isEmptyCFGBlock(CB)) + continue; + + // Find the entry points for this block + FindUnreachableEntryPoints(CB); + + // This block may have been pruned; check if we still want to report it + if (reachable.count(CB->getBlockID())) + continue; + + // Check for false positives + if (CB->size() > 0 && isInvalidPath(CB, *PM)) + continue; + + // Special case for __builtin_unreachable. + // FIXME: This should be extended to include other unreachable markers, + // such as llvm_unreachable. + if (!CB->empty()) { + const Stmt *First = CB->front(); + if (const CallExpr *CE = dyn_cast<CallExpr>(First)) { + if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable) + continue; + } + } + + // We found a block that wasn't covered - find the statement to report + SourceRange SR; + SourceLocation SL; + if (const Stmt *S = getUnreachableStmt(CB)) { + SR = S->getSourceRange(); + SL = S->getLocStart(); + if (SR.isInvalid() || SL.isInvalid()) + continue; + } + else + continue; + + // Check if the SourceLocation is in a system header + const SourceManager &SM = B.getSourceManager(); + if (SM.isInSystemHeader(SL) || SM.isInExternCSystemHeader(SL)) + continue; + + B.EmitBasicReport("Unreachable code", "Dead code", "This statement is never" + " executed", SL, SR); + } +} + +// Recursively finds the entry point(s) for this dead CFGBlock. +void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB) { + bool allPredecessorsReachable = true; + + visited.insert(CB->getBlockID()); + + for (CFGBlock::const_pred_iterator I = CB->pred_begin(); I != CB->pred_end(); + ++I) { + // Recurse over all unreachable blocks + if (!reachable.count((*I)->getBlockID())) { + // At least one predeccessor was unreachable + allPredecessorsReachable = false; + + // Only visit the block once + if (!visited.count((*I)->getBlockID())) + FindUnreachableEntryPoints(*I); + } + } + + // If at least one predecessor is unreachable, mark this block as reachable + // so we don't report this block. + if (!allPredecessorsReachable) { + reachable.insert(CB->getBlockID()); + } +} + +// Find the Stmt* in a CFGBlock for reporting a warning +const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) { + if (CB->size() > 0) + return CB->front().getStmt(); + else if (const Stmt *S = CB->getTerminator()) + return S; + else + return 0; +} + +// Determines if the path to this CFGBlock contained an element that infers this +// block is a false positive. We assume that FindUnreachableEntryPoints has +// already marked only the entry points to any dead code, so we need only to +// find the condition that led to this block (the predecessor of this block.) +// There will never be more than one predecessor. +bool UnreachableCodeChecker::isInvalidPath(const CFGBlock *CB, + const ParentMap &PM) { + // We only expect a predecessor size of 0 or 1. If it is >1, then an external + // condition has broken our assumption (for example, a sink being placed by + // another check). In these cases, we choose not to report. + if (CB->pred_size() > 1) + return true; + + // If there are no predecessors, then this block is trivially unreachable + if (CB->pred_size() == 0) + return false; + + const CFGBlock *pred = *CB->pred_begin(); + + // Get the predecessor block's terminator conditon + const Stmt *cond = pred->getTerminatorCondition(); + + //assert(cond && "CFGBlock's predecessor has a terminator condition"); + // The previous assertion is invalid in some cases (eg do/while). Leaving + // reporting of these situations on at the moment to help triage these cases. + if (!cond) + return false; + + // Run each of the checks on the conditions + if (containsMacro(cond) || containsEnum(cond) + || containsStaticLocal(cond) || containsBuiltinOffsetOf(cond) + || containsStmt<SizeOfAlignOfExpr>(cond)) + return true; + + return false; +} + +// Returns true if the given CFGBlock is empty +bool UnreachableCodeChecker::isEmptyCFGBlock(const CFGBlock *CB) { + return CB->getLabel() == 0 // No labels + && CB->size() == 0 // No statements + && CB->getTerminator() == 0; // No terminator +} diff --git a/lib/Checker/VLASizeChecker.cpp b/lib/Checker/VLASizeChecker.cpp index 936991d..0800b8b 100644 --- a/lib/Checker/VLASizeChecker.cpp +++ b/lib/Checker/VLASizeChecker.cpp @@ -117,7 +117,7 @@ void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { SVal EleSizeVal = ValMgr.makeIntVal(EleSize.getQuantity(), SizeTy); // Multiply the array length by the element size. - SVal ArraySizeVal = SV.EvalBinOpNN(state, BinaryOperator::Mul, ArrayLength, + SVal ArraySizeVal = SV.EvalBinOpNN(state, BO_Mul, ArrayLength, cast<NonLoc>(EleSizeVal), SizeTy); // Finally, Assume that the array's extent matches the given size. diff --git a/lib/Checker/ValueManager.cpp b/lib/Checker/ValueManager.cpp index aa0c3c8..8b7cd7b 100644 --- a/lib/Checker/ValueManager.cpp +++ b/lib/Checker/ValueManager.cpp @@ -72,7 +72,7 @@ SVal ValueManager::convertToArrayIndex(SVal V) { DefinedOrUnknownSVal ValueManager::getRegionValueSymbolVal(const TypedRegion* R) { - QualType T = R->getValueType(SymMgr.getContext()); + QualType T = R->getValueType(); if (!SymbolManager::canSymbolicate(T)) return UnknownVal(); @@ -117,11 +117,24 @@ DefinedOrUnknownSVal ValueManager::getConjuredSymbolVal(const void *SymbolTag, return nonloc::SymbolVal(sym); } +DefinedSVal ValueManager::getMetadataSymbolVal(const void *SymbolTag, + const MemRegion *MR, + const Expr *E, QualType T, + unsigned Count) { + assert(SymbolManager::canSymbolicate(T) && "Invalid metadata symbol type"); + + SymbolRef sym = SymMgr.getMetadataSymbol(MR, E, T, Count, SymbolTag); + + if (Loc::IsLocType(T)) + return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); + + return nonloc::SymbolVal(sym); +} DefinedOrUnknownSVal ValueManager::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, const TypedRegion *R) { - QualType T = R->getValueType(R->getContext()); + QualType T = R->getValueType(); if (!SymbolManager::canSymbolicate(T)) return UnknownVal(); |