diff options
Diffstat (limited to 'lib/StaticAnalyzer/Core/BugReporter.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Core/BugReporter.cpp | 179 |
1 files changed, 145 insertions, 34 deletions
diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index a2642120b70f8..7ba2fa7fdd0dd 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -48,6 +48,10 @@ static inline const Stmt *GetStmt(const ProgramPoint &P) { return SP->getStmt(); else if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) return BE->getSrc()->getTerminator(); + else if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) + return CE->getCallExpr(); + else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&P)) + return CEE->getCalleeContext()->getCallSite(); return 0; } @@ -427,7 +431,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); - if (const CallExit *CE = dyn_cast<CallExit>(&P)) { + if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) { PathDiagnosticCallPiece *C = PathDiagnosticCallPiece::construct(N, *CE, SMgr); PD.getActivePath().push_front(C); @@ -437,21 +441,23 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, } if (const CallEnter *CE = dyn_cast<CallEnter>(&P)) { + // Flush all locations, and pop the active path. + bool VisitedEntireCall = PD.isWithinCall(); PD.popActivePath(); - // The current active path should never be empty. Either we - // just added a bunch of stuff to the top-level path, or - // we have a previous CallExit. If the front of the active - // path is not a PathDiagnosticCallPiece, it means that the + + // Either we just added a bunch of stuff to the top-level path, or + // we have a previous CallExitEnd. If the former, it means that the // path terminated within a function call. We must then take the // current contents of the active path and place it within // a new PathDiagnosticCallPiece. - assert(!PD.getActivePath().empty()); - PathDiagnosticCallPiece *C = - dyn_cast<PathDiagnosticCallPiece>(PD.getActivePath().front()); - if (!C) { + PathDiagnosticCallPiece *C; + if (VisitedEntireCall) { + C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front()); + } else { const Decl *Caller = CE->getLocationContext()->getDecl(); C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); } + C->setCallee(*CE, SMgr); if (!CallStack.empty()) { assert(CallStack.back().first == C); @@ -864,6 +870,7 @@ public: void rawAddEdge(PathDiagnosticLocation NewLoc); void addContext(const Stmt *S); + void addContext(const PathDiagnosticLocation &L); void addExtendedContext(const Stmt *S); }; } // end anonymous namespace @@ -1031,7 +1038,10 @@ void EdgeBuilder::addContext(const Stmt *S) { return; PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.LC); + addContext(L); +} +void EdgeBuilder::addContext(const PathDiagnosticLocation &L) { while (!CLocs.empty()) { const PathDiagnosticLocation &TopContextLoc = CLocs.back(); @@ -1051,6 +1061,78 @@ void EdgeBuilder::addContext(const Stmt *S) { CLocs.push_back(L); } +// Cone-of-influence: support the reverse propagation of "interesting" symbols +// and values by tracing interesting calculations backwards through evaluated +// expressions along a path. This is probably overly complicated, but the idea +// is that if an expression computed an "interesting" value, the child +// expressions are are also likely to be "interesting" as well (which then +// propagates to the values they in turn compute). This reverse propagation +// is needed to track interesting correlations across function call boundaries, +// where formal arguments bind to actual arguments, etc. This is also needed +// because the constraint solver sometimes simplifies certain symbolic values +// into constants when appropriate, and this complicates reasoning about +// interesting values. +typedef llvm::DenseSet<const Expr *> InterestingExprs; + +static void reversePropagateIntererstingSymbols(BugReport &R, + InterestingExprs &IE, + const ProgramState *State, + const Expr *Ex, + const LocationContext *LCtx) { + SVal V = State->getSVal(Ex, LCtx); + if (!(R.isInteresting(V) || IE.count(Ex))) + return; + + switch (Ex->getStmtClass()) { + default: + if (!isa<CastExpr>(Ex)) + break; + // Fall through. + case Stmt::BinaryOperatorClass: + case Stmt::UnaryOperatorClass: { + for (Stmt::const_child_iterator CI = Ex->child_begin(), + CE = Ex->child_end(); + CI != CE; ++CI) { + if (const Expr *child = dyn_cast_or_null<Expr>(*CI)) { + IE.insert(child); + SVal ChildV = State->getSVal(child, LCtx); + R.markInteresting(ChildV); + } + break; + } + } + } + + R.markInteresting(V); +} + +static void reversePropagateInterestingSymbols(BugReport &R, + InterestingExprs &IE, + const ProgramState *State, + const LocationContext *CalleeCtx, + const LocationContext *CallerCtx) +{ + // FIXME: Handle non-CallExpr-based CallEvents. + const StackFrameContext *Callee = CalleeCtx->getCurrentStackFrame(); + const Stmt *CallSite = Callee->getCallSite(); + if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite)) { + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) { + FunctionDecl::param_const_iterator PI = FD->param_begin(), + PE = FD->param_end(); + CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); + for (; AI != AE && PI != PE; ++AI, ++PI) { + if (const Expr *ArgE = *AI) { + if (const ParmVarDecl *PD = *PI) { + Loc LV = State->getLValue(PD, CalleeCtx); + if (R.isInteresting(LV) || R.isInteresting(State->getRawSVal(LV))) + IE.insert(ArgE); + } + } + } + } + } +} + static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N, @@ -1058,6 +1140,7 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, EdgeBuilder EB(PD, PDB); const SourceManager& SM = PDB.getSourceManager(); StackDiagVector CallStack; + InterestingExprs IE; const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); while (NextNode) { @@ -1066,16 +1149,27 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, ProgramPoint P = N->getLocation(); do { - if (const CallExit *CE = dyn_cast<CallExit>(&P)) { - const StackFrameContext *LCtx = - CE->getLocationContext()->getCurrentStackFrame(); - PathDiagnosticLocation Loc(LCtx->getCallSite(), - PDB.getSourceManager(), - LCtx); - EB.addEdge(Loc, true); - EB.flushLocations(); + if (const PostStmt *PS = dyn_cast<PostStmt>(&P)) { + if (const Expr *Ex = PS->getStmtAs<Expr>()) + reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, + N->getState().getPtr(), Ex, + N->getLocationContext()); + } + + if (const CallExitEnd *CE = dyn_cast<CallExitEnd>(&P)) { + const Stmt *S = CE->getCalleeContext()->getCallSite(); + if (const Expr *Ex = dyn_cast_or_null<Expr>(S)) { + reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, + N->getState().getPtr(), Ex, + N->getLocationContext()); + } + PathDiagnosticCallPiece *C = PathDiagnosticCallPiece::construct(N, *CE, SM); + + EB.addEdge(C->callReturn, true); + EB.flushLocations(); + PD.getActivePath().push_front(C); PD.pushActivePath(&C->path); CallStack.push_back(StackDiagPair(C, N)); @@ -1092,26 +1186,26 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, EB.addEdge(pos); // Flush all locations, and pop the active path. + bool VisitedEntireCall = PD.isWithinCall(); EB.flushLocations(); PD.popActivePath(); - assert(!PD.getActivePath().empty()); PDB.LC = N->getLocationContext(); - // The current active path should never be empty. Either we - // just added a bunch of stuff to the top-level path, or - // we have a previous CallExit. If the front of the active - // path is not a PathDiagnosticCallPiece, it means that the + // Either we just added a bunch of stuff to the top-level path, or + // we have a previous CallExitEnd. If the former, it means that the // path terminated within a function call. We must then take the // current contents of the active path and place it within // a new PathDiagnosticCallPiece. - PathDiagnosticCallPiece *C = - dyn_cast<PathDiagnosticCallPiece>(PD.getActivePath().front()); - if (!C) { - const Decl * Caller = CE->getLocationContext()->getDecl(); + PathDiagnosticCallPiece *C; + if (VisitedEntireCall) { + C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front()); + } else { + const Decl *Caller = CE->getLocationContext()->getDecl(); C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); } + C->setCallee(*CE, SM); - EB.addContext(CE->getCallExpr()); + EB.addContext(C->getLocation()); if (!CallStack.empty()) { assert(CallStack.back().first == C); @@ -1127,7 +1221,19 @@ static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, PDB.LC = N->getLocationContext(); // Block edges. - if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) { + // Does this represent entering a call? If so, look at propagating + // interesting symbols across call boundaries. + if (NextNode) { + const LocationContext *CallerCtx = NextNode->getLocationContext(); + const LocationContext *CalleeCtx = PDB.LC; + if (CallerCtx != CalleeCtx) { + reversePropagateInterestingSymbols(*PDB.getBugReport(), IE, + N->getState().getPtr(), + CalleeCtx, CallerCtx); + } + } + const CFGBlock &Blk = *BE->getSrc(); const Stmt *Term = Blk.getTerminator(); @@ -1430,9 +1536,12 @@ void BugReporter::FlushReports() { I = bugTypes.begin(), E = bugTypes.end(); I != E; ++I) const_cast<BugType*>(*I)->FlushReports(*this); - typedef llvm::FoldingSet<BugReportEquivClass> SetTy; - for (SetTy::iterator EI=EQClasses.begin(), EE=EQClasses.end(); EI!=EE;++EI){ - BugReportEquivClass& EQ = *EI; + // We need to flush reports in deterministic order to ensure the order + // of the reports is consistent between runs. + typedef std::vector<BugReportEquivClass *> ContVecTy; + for (ContVecTy::iterator EI=EQClassesVector.begin(), EE=EQClassesVector.end(); + EI != EE; ++EI){ + BugReportEquivClass& EQ = **EI; FlushReport(EQ); } @@ -1768,9 +1877,11 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, } while(finalReportConfigToken != originalReportConfigToken); // Finally, prune the diagnostic path of uninteresting stuff. - bool hasSomethingInteresting = RemoveUneededCalls(PD.getMutablePieces()); - assert(hasSomethingInteresting); - (void) hasSomethingInteresting; + if (R->shouldPrunePath()) { + bool hasSomethingInteresting = RemoveUneededCalls(PD.getMutablePieces()); + assert(hasSomethingInteresting); + (void) hasSomethingInteresting; + } } void BugReporter::Register(BugType *BT) { |