diff options
author | Ted Kremenek <kremenek@apple.com> | 2009-12-16 03:18:58 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2009-12-16 03:18:58 +0000 |
commit | 852274d4257134906995cb252fb3dfd2d71deae8 (patch) | |
tree | 11e4100946b163761748ef9442d0614a0c65a761 /lib/Analysis/GRExprEngine.cpp | |
parent | 8f6a3eda1344042dcc2760b276f71afc0b7d4801 (diff) |
Add (initial?) static analyzer support for handling C++ references.
This change was a lot bigger than I originally anticipated; among
other things it requires us storing more information in the CFG to
record what block-level expressions need to be evaluated as lvalues.
The big change is that CFGBlocks no longer contain Stmt*'s by
CFGElements. Currently CFGElements just wrap Stmt*, but they also
store a bit indicating whether the block-level expression should be
evalauted as an lvalue. DeclStmts involving the initialization of a
reference require us treating the initialization expression as an
lvalue, even though that information isn't recorded in the AST.
Conceptually this change isn't that complicated, but it required
bubbling up the data through the CFGBuilder, to GRCoreEngine, and
eventually to GRExprEngine.
The addition of CFGElement is also useful for when we want to handle
more control-flow constructs or other data we want to keep in the CFG
that isn't represented well with just a block of statements.
In GRExprEngine, this patch introduces logic for evaluating the
lvalues of references, which currently retrieves the internal "pointer
value" that the reference represents. EvalLoad does a two stage load
to catch null dereferences involving an invalid reference (although
this could possibly be caught earlier during the initialization of a
reference).
Symbols are currently symbolicated using the reference type, instead
of a pointer type, and special handling is required creating
ElementRegions that layer on SymbolicRegions (see the changes to
RegionStoreManager).
Along the way, the DeadStoresChecker also silences warnings involving
dead stores to references. This was the original change I introduced
(which I wrote test cases for) that I realized caused GRExprEngine to
crash.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@91501 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Analysis/GRExprEngine.cpp')
-rw-r--r-- | lib/Analysis/GRExprEngine.cpp | 79 |
1 files changed, 63 insertions, 16 deletions
diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp index 51e6a54752..a0ec87d2f7 100644 --- a/lib/Analysis/GRExprEngine.cpp +++ b/lib/Analysis/GRExprEngine.cpp @@ -33,6 +33,7 @@ using namespace clang; using llvm::dyn_cast; +using llvm::dyn_cast_or_null; using llvm::cast; using llvm::APSInt; @@ -378,17 +379,15 @@ const GRState* GRExprEngine::getInitialState(const LocationContext *InitLoc) { // Top-level transfer function logic (Dispatcher). //===----------------------------------------------------------------------===// -void GRExprEngine::ProcessStmt(Stmt* S, GRStmtNodeBuilder& builder) { - +void GRExprEngine::ProcessStmt(CFGElement CE, GRStmtNodeBuilder& builder) { + CurrentStmt = CE.getStmt(); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), - S->getLocStart(), + CurrentStmt->getLocStart(), "Error evaluating statement"); Builder = &builder; EntryNode = builder.getLastNode(); - CurrentStmt = S; - // Set up our simple checks. if (BatchAuditor) Builder->setAuditor(BatchAuditor.get()); @@ -415,7 +414,7 @@ void GRExprEngine::ProcessStmt(Stmt* S, GRStmtNodeBuilder& builder) { // FIXME: This should soon be removed. ExplodedNodeSet Tmp2; - getTF().EvalDeadSymbols(Tmp2, *this, *Builder, EntryNode, S, + getTF().EvalDeadSymbols(Tmp2, *this, *Builder, EntryNode, CurrentStmt, CleanedState, SymReaper); if (Checkers.empty()) @@ -437,8 +436,8 @@ void GRExprEngine::ProcessStmt(Stmt* S, GRStmtNodeBuilder& builder) { Checker *checker = I->second; for (ExplodedNodeSet::iterator NI = SrcSet->begin(), NE = SrcSet->end(); NI != NE; ++NI) - checker->GR_EvalDeadSymbols(*DstSet, *Builder, *this, S, *NI, - SymReaper, tag); + checker->GR_EvalDeadSymbols(*DstSet, *Builder, *this, CurrentStmt, + *NI, SymReaper, tag); SrcSet = DstSet; } } @@ -457,7 +456,10 @@ void GRExprEngine::ProcessStmt(Stmt* S, GRStmtNodeBuilder& builder) { Builder->SetCleanedState(*I == EntryNode ? CleanedState : GetState(*I)); // Visit the statement. - Visit(S, *I, Dst); + if (CE.asLValue()) + VisitLValue(cast<Expr>(CurrentStmt), *I, Dst); + else + Visit(CurrentStmt, *I, Dst); // Do we need to auto-generate a node? We only need to do this to generate // a node with a "cleaned" state; GRCoreEngine will actually handle @@ -465,7 +467,7 @@ void GRExprEngine::ProcessStmt(Stmt* S, GRStmtNodeBuilder& builder) { if (Dst.size() == 1 && *Dst.begin() == EntryNode && !Builder->HasGeneratedNode && !HasAutoGenerated) { HasAutoGenerated = true; - builder.generateNode(S, GetState(EntryNode), *I); + builder.generateNode(CurrentStmt, GetState(EntryNode), *I); } } @@ -1232,13 +1234,23 @@ void GRExprEngine::VisitCommonDeclRefExpr(Expr *Ex, const NamedDecl *D, SVal V = state->getLValue(VD, Pred->getLocationContext()); - if (asLValue) - MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), - ProgramPoint::PostLValueKind); + if (asLValue) { + // For references, the 'lvalue' is the pointer address stored in the + // reference region. + if (VD->getType()->isReferenceType()) { + if (const MemRegion *R = V.getAsRegion()) + V = state->getSVal(R); + else + V = UnknownVal(); + } + + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V, + ProgramPoint::PostLValueKind)); + } else EvalLoad(Dst, Ex, Pred, state, V); - return; + return; } else if (const EnumConstantDecl* ED = dyn_cast<EnumConstantDecl>(D)) { assert(!asLValue && "EnumConstantDecl does not have lvalue."); @@ -1415,6 +1427,37 @@ void GRExprEngine::EvalLoad(ExplodedNodeSet& Dst, Expr *Ex, ExplodedNode* Pred, const GRState* state, SVal location, const void *tag, QualType LoadTy) { + // Are we loading from a region? This actually results in two loads; one + // to fetch the address of the referenced value and one to fetch the + // referenced value. + if (const TypedRegion *TR = + dyn_cast_or_null<TypedRegion>(location.getAsRegion())) { + + QualType ValTy = TR->getValueType(getContext()); + if (const ReferenceType *RT = ValTy->getAs<ReferenceType>()) { + static int loadReferenceTag = 0; + ExplodedNodeSet Tmp; + EvalLoadCommon(Tmp, Ex, Pred, state, location, &loadReferenceTag, + getContext().getPointerType(RT->getPointeeType())); + + // Perform the load from the referenced value. + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end() ; I!=E; ++I) { + state = GetState(*I); + location = state->getSVal(Ex); + EvalLoadCommon(Dst, Ex, *I, state, location, tag, LoadTy); + } + return; + } + } + + EvalLoadCommon(Dst, Ex, Pred, state, location, tag, LoadTy); +} + +void GRExprEngine::EvalLoadCommon(ExplodedNodeSet& Dst, Expr *Ex, + ExplodedNode* Pred, + const GRState* state, SVal location, + const void *tag, QualType LoadTy) { + // Evaluate the location (checks for bad dereferences). ExplodedNodeSet Tmp; EvalLocation(Tmp, Ex, Pred, state, location, tag, true); @@ -1992,8 +2035,12 @@ void GRExprEngine::VisitDeclStmt(DeclStmt *DS, ExplodedNode *Pred, // time a function is called those values may not be current. ExplodedNodeSet Tmp; - if (InitEx) - Visit(InitEx, Pred, Tmp); + if (InitEx) { + if (VD->getType()->isReferenceType()) + VisitLValue(InitEx, Pred, Tmp); + else + Visit(InitEx, Pred, Tmp); + } else Tmp.Add(Pred); |