From b21b479653fdd3adc350c15f1ee69b078f717024 Mon Sep 17 00:00:00 2001 From: Artem Dergachev Date: Wed, 1 Aug 2018 01:58:15 +0000 Subject: [PATCH] [analyzer] CallEvent: Add helper methods for obtaining the callee stack frame. Newly added methods allow reasoning about the stack frame of the call (as opposed to the stack frame on which the call was made, which was always available) - obtain the stack frame context, obtain parameter regions - even if the call is not going to be (or was not) inlined, i.e. even if the analysis has never actually entered the stack frame. Differential Revision: https://reviews.llvm.org/D49715 llvm-svn: 338474 --- .../clang/Analysis/ConstructionContext.h | 7 +++ .../Core/PathSensitive/CallEvent.h | 41 ++++++++++++ .../Core/PathSensitive/ExprEngine.h | 15 ++--- clang/lib/StaticAnalyzer/Core/CallEvent.cpp | 63 +++++++++++++++++++ 4 files changed, 119 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/Analysis/ConstructionContext.h b/clang/include/clang/Analysis/ConstructionContext.h index 2724c0f68b987a..48e0540803af7e 100644 --- a/clang/include/clang/Analysis/ConstructionContext.h +++ b/clang/include/clang/Analysis/ConstructionContext.h @@ -112,6 +112,13 @@ class ConstructionContextItem { ConstructionContextItem(const ObjCMessageExpr *ME, unsigned Index) : Data(ME), Kind(ArgumentKind), Index(Index) {} + // A polymorphic version of the previous calls with dynamic type check. + ConstructionContextItem(const Expr *E, unsigned Index) + : Data(E), Kind(ArgumentKind), Index(Index) { + assert(isa(E) || isa(E) || + isa(E)); + } + ConstructionContextItem(const CXXCtorInitializer *Init) : Data(Init), Kind(InitializerKind), Index(0) {} diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index 68c50eb57beb32..9078fb94d2821d 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -29,6 +29,7 @@ #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" @@ -404,6 +405,46 @@ class CallEvent { /// \p D must not be null. static bool isVariadic(const Decl *D); + /// Returns AnalysisDeclContext for the callee stack frame. + /// Currently may fail; returns null on failure. + AnalysisDeclContext *getCalleeAnalysisDeclContext() const; + + /// Returns the callee stack frame. That stack frame will only be entered + /// during analysis if the call is inlined, but it may still be useful + /// in intermediate calculations even if the call isn't inlined. + /// May fail; returns null on failure. + const StackFrameContext *getCalleeStackFrame() const; + + /// Returns memory location for a parameter variable within the callee stack + /// frame. May fail; returns null on failure. + const VarRegion *getParameterLocation(unsigned Index) const; + + /// Returns true if on the current path, the argument was constructed by + /// calling a C++ constructor over it. This is an internal detail of the + /// analysis which doesn't necessarily represent the program semantics: + /// if we are supposed to construct an argument directly, we may still + /// not do that because we don't know how (i.e., construction context is + /// unavailable in the CFG or not supported by the analyzer). + bool isArgumentConstructedDirectly(unsigned Index) const { + // This assumes that the object was not yet removed from the state. + return ExprEngine::getObjectUnderConstruction( + getState(), {getOriginExpr(), Index}, getCalleeStackFrame()).hasValue(); + } + + /// Some calls have parameter numbering mismatched from argument numbering. + /// This function converts an argument index to the corresponding + /// parameter index. Returns None is the argument doesn't correspond + /// to any parameter variable. + Optional getAdjustedParameterIndex(unsigned ArgumentIndex) const { + if (dyn_cast_or_null(getOriginExpr()) && + dyn_cast_or_null(getDecl())) { + // For member operator calls argument 0 on the expression corresponds + // to implicit this-parameter on the declaration. + return (ArgumentIndex > 0) ? Optional(ArgumentIndex - 1) : None; + } + return ArgumentIndex; + } + // Iterator access to formal parameters and their types. private: struct GetTypeFn { diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index e2de1d634ab3e5..06a90fa847a6f9 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -581,6 +581,14 @@ class ExprEngine : public SubEngine { return svalBuilder.evalBinOp(ST, Op, LHS, RHS, T); } + /// By looking at a certain item that may be potentially part of an object's + /// ConstructionContext, retrieve such object's location. A particular + /// statement can be transparently passed as \p Item in most cases. + static Optional + getObjectUnderConstruction(ProgramStateRef State, + const ConstructionContextItem &Item, + const LocationContext *LC); + protected: /// evalBind - Handle the semantics of binding a value to a specific location. /// This method is used by evalStore, VisitDeclStmt, and others. @@ -773,13 +781,6 @@ class ExprEngine : public SubEngine { const ConstructionContextItem &Item, const LocationContext *LC); - /// If the given statement corresponds to an object under construction, - /// being part of its construciton context, retrieve that object's location. - static Optional - getObjectUnderConstruction(ProgramStateRef State, - const ConstructionContextItem &Item, - const LocationContext *LC); - /// If the given expression corresponds to a temporary that was used for /// passing into an elidable copy/move constructor and that constructor /// was actually elided, track that we also need to elide the destructor. diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index 8db7b06f186dad..fe9260e32dd8bc 100644 --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -27,6 +27,7 @@ #include "clang/AST/Type.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" +#include "clang/Analysis/CFGStmtMap.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/CrossTU/CrossTranslationUnit.h" #include "clang/Basic/IdentifierTable.h" @@ -166,6 +167,68 @@ bool CallEvent::isGlobalCFunction(StringRef FunctionName) const { return CheckerContext::isCLibraryFunction(FD, FunctionName); } +AnalysisDeclContext *CallEvent::getCalleeAnalysisDeclContext() const { + const Decl *D = getDecl(); + + // If the callee is completely unknown, we cannot construct the stack frame. + if (!D) + return nullptr; + + // FIXME: Skip virtual functions for now. There's no easy procedure to foresee + // the exact decl that should be used, especially when it's not a definition. + if (const Decl *RD = getRuntimeDefinition().getDecl()) + if (RD != D) + return nullptr; + + return LCtx->getAnalysisDeclContext()->getManager()->getContext(D); +} + +const StackFrameContext *CallEvent::getCalleeStackFrame() const { + AnalysisDeclContext *ADC = getCalleeAnalysisDeclContext(); + if (!ADC) + return nullptr; + + const Expr *E = getOriginExpr(); + if (!E) + return nullptr; + + // Recover CFG block via reverse lookup. + // TODO: If we were to keep CFG element information as part of the CallEvent + // instead of doing this reverse lookup, we would be able to build the stack + // frame for non-expression-based calls, and also we wouldn't need the reverse + // lookup. + CFGStmtMap *Map = LCtx->getAnalysisDeclContext()->getCFGStmtMap(); + const CFGBlock *B = Map->getBlock(E); + assert(B); + + // Also recover CFG index by scanning the CFG block. + unsigned Idx = 0, Sz = B->size(); + for (; Idx < Sz; ++Idx) + if (auto StmtElem = (*B)[Idx].getAs()) + if (StmtElem->getStmt() == E) + break; + assert(Idx < Sz); + + return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, Idx); +} + +const VarRegion *CallEvent::getParameterLocation(unsigned Index) const { + const StackFrameContext *SFC = getCalleeStackFrame(); + // We cannot construct a VarRegion without a stack frame. + if (!SFC) + return nullptr; + + const ParmVarDecl *PVD = parameters()[Index]; + const VarRegion *VR = + State->getStateManager().getRegionManager().getVarRegion(PVD, SFC); + + // This sanity check would fail if our parameter declaration doesn't + // correspond to the stack frame's function declaration. + assert(VR->getStackFrame() == SFC); + + return VR; +} + /// Returns true if a type is a pointer-to-const or reference-to-const /// with no further indirection. static bool isPointerToConst(QualType Ty) {