void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, const MemRegion *Dest, ExplodedNode *Pred, ExplodedNodeSet &Dst) { if (!Dest) Dest = svalBuilder.getRegionManager().getCXXTempObjectRegion(E, Pred->getLocationContext()); if (E->isElidable()) { VisitAggExpr(E->getArg(0), Dest, Pred, Dst); // FIXME: this is here to force propogation if VisitAggExpr doesn't if (Dst.empty()) Dst.Add(Pred); return; } const CXXConstructorDecl *CD = E->getConstructor(); assert(CD); if (!(CD->isThisDeclarationADefinition() && AMgr.shouldInlineCall())) // FIXME: invalidate the object. return; // Evaluate other arguments. ExplodedNodeSet argsEvaluated; const FunctionProtoType *FnType = CD->getType()->getAs<FunctionProtoType>(); 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(), E, Builder->getBlock(), Builder->getIndex()); const CXXThisRegion *ThisR =getCXXThisRegion(E->getConstructor()->getParent(), SFC); CallEnter Loc(E, SFC, Pred->getLocationContext()); for (ExplodedNodeSet::iterator NI = argsEvaluated.begin(), NE = argsEvaluated.end(); NI != NE; ++NI) { const GRState *state = GetState(*NI); // Setup 'this' region, so that the ctor is evaluated on the object pointed // by 'Dest'. state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest)); ExplodedNode *N = Builder->generateNode(Loc, state, Pred); if (N) Dst.Add(N); } }
void ExprEngine::evalMethodCall(const CallExpr *MCE, const CXXMethodDecl *MD, const Expr *ThisExpr, ExplodedNode *Pred, ExplodedNodeSet &Src, ExplodedNodeSet &Dst) { // Allow checkers to pre-visit the member call. ExplodedNodeSet PreVisitChecks; CheckerVisit(MCE, PreVisitChecks, Src, PreVisitStmtCallback); if (!(MD->isThisDeclarationADefinition() && AMgr.shouldInlineCall())) { // FIXME: conservative method call evaluation. CheckerVisit(MCE, Dst, PreVisitChecks, PostVisitStmtCallback); return; } const StackFrameContext *SFC = AMgr.getStackFrame(MD, Pred->getLocationContext(), MCE, Builder->getBlock(), Builder->getIndex()); const CXXThisRegion *ThisR = getCXXThisRegion(MD, SFC); CallEnter Loc(MCE, SFC, Pred->getLocationContext()); for (ExplodedNodeSet::iterator I = PreVisitChecks.begin(), E = PreVisitChecks.end(); I != E; ++I) { // Set up 'this' region. const GRState *state = GetState(*I); state = state->bindLoc(loc::MemRegionVal(ThisR), state->getSVal(ThisExpr)); Dst.Add(Builder->generateNode(Loc, state, *I)); } }
void CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, unsigned Steps, const ProgramState *InitState, ExplodedNodeSet &Dst) { ExecuteWorkList(L, Steps, InitState); for (SmallVectorImpl<ExplodedNode*>::iterator I = G->EndNodes.begin(), E = G->EndNodes.end(); I != E; ++I) { Dst.Add(*I); } }
bool CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, unsigned Steps, ProgramStateRef InitState, ExplodedNodeSet &Dst) { bool DidNotFinish = ExecuteWorkList(L, Steps, InitState); for (ExplodedGraph::eop_iterator I = G->eop_begin(), E = G->eop_end(); I != E; ++I) { Dst.Add(*I); } return DidNotFinish; }
void GRExprEngine::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { // Get the method type. const FunctionProtoType *FnType = MCE->getCallee()->getType()->getAs<FunctionProtoType>(); assert(FnType && "Method type not available"); // Evaluate explicit arguments with a worklist. ExplodedNodeSet ArgsEvaluated; EvalArguments(MCE->arg_begin(), MCE->arg_end(), FnType, Pred, ArgsEvaluated); // Evaluate the implicit object argument. ExplodedNodeSet AllArgsEvaluated; const MemberExpr *ME = dyn_cast<MemberExpr>(MCE->getCallee()->IgnoreParens()); if (!ME) return; Expr *ObjArgExpr = ME->getBase(); for (ExplodedNodeSet::iterator I = ArgsEvaluated.begin(), E = ArgsEvaluated.end(); I != E; ++I) { if (ME->isArrow()) Visit(ObjArgExpr, *I, AllArgsEvaluated); else VisitLValue(ObjArgExpr, *I, AllArgsEvaluated); } const CXXMethodDecl *MD = cast<CXXMethodDecl>(ME->getMemberDecl()); assert(MD && "not a CXXMethodDecl?"); if (!(MD->isThisDeclarationADefinition() && AMgr.shouldInlineCall())) // FIXME: conservative method call evaluation. return; const StackFrameContext *SFC = AMgr.getStackFrame(MD, Pred->getLocationContext(), MCE, Builder->getBlock(), Builder->getIndex()); const CXXThisRegion *ThisR = getCXXThisRegion(MD, SFC); CallEnter Loc(MCE, SFC->getAnalysisContext(), Pred->getLocationContext()); for (ExplodedNodeSet::iterator I = AllArgsEvaluated.begin(), E = AllArgsEvaluated.end(); I != E; ++I) { // Set up 'this' region. const GRState *state = GetState(*I); state = state->bindLoc(loc::MemRegionVal(ThisR),state->getSVal(ObjArgExpr)); ExplodedNode *N = Builder->generateNode(Loc, state, *I); if (N) Dst.Add(N); } }
void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred, ExplodedNodeSet &Dst) { const VarDecl *VD = CS->getExceptionDecl(); if (!VD) { Dst.Add(Pred); return; } const LocationContext *LCtx = Pred->getLocationContext(); SVal V = svalBuilder.conjureSymbolVal(CS, LCtx, VD->getType(), currBldrCtx->blockCount()); ProgramStateRef state = Pred->getState(); state = state->bindLoc(state->getLValue(VD, LCtx), V); StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); Bldr.generateNode(CS, Pred, state); }
void GRExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, SVal Dest, ExplodedNode *Pred, ExplodedNodeSet &Dst) { if (E->isElidable()) { VisitAggExpr(E->getArg(0), Dest, Pred, Dst); return; } const CXXConstructorDecl *CD = E->getConstructor(); assert(CD); if (!(CD->isThisDeclarationADefinition() && AMgr.shouldInlineCall())) // FIXME: invalidate the object. return; // Evaluate other arguments. ExplodedNodeSet ArgsEvaluated; const FunctionProtoType *FnType = CD->getType()->getAs<FunctionProtoType>(); 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(), E, Builder->getBlock(), Builder->getIndex()); const CXXThisRegion *ThisR = getCXXThisRegion(E->getConstructor(), SFC); 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. state = state->bindLoc(loc::MemRegionVal(ThisR), Dest); ExplodedNode *N = Builder->generateNode(Loc, state, Pred); if (N) Dst.Add(N); } }
void ExprEngine::removeDeadOnEndOfFunction(NodeBuilderContext& BC, ExplodedNode *Pred, ExplodedNodeSet &Dst) { // Find the last statement in the function and the corresponding basic block. const Stmt *LastSt = nullptr; const CFGBlock *Blk = nullptr; std::tie(LastSt, Blk) = getLastStmt(Pred); if (!Blk || !LastSt) { Dst.Add(Pred); return; } // Here, we destroy the current location context. We use the current // function's entire body as a diagnostic statement, with which the program // point will be associated. However, we only want to use LastStmt as a // reference for what to clean up if it's a ReturnStmt; otherwise, everything // is dead. SaveAndRestore<const NodeBuilderContext *> NodeContextRAII(currBldrCtx, &BC); const LocationContext *LCtx = Pred->getLocationContext(); removeDead(Pred, Dst, dyn_cast<ReturnStmt>(LastSt), LCtx, LCtx->getAnalysisDeclContext()->getBody(), ProgramPoint::PostStmtPurgeDeadSymbolsKind); }
void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD, const MemRegion *Dest, const Stmt *S, ExplodedNode *Pred, ExplodedNodeSet &Dst) { if (!(DD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall())) return; // Create the context for 'this' region. const StackFrameContext *SFC = AMgr.getStackFrame(DD, Pred->getLocationContext(), S, Builder->getBlock(), Builder->getIndex()); const CXXThisRegion *ThisR = getCXXThisRegion(DD->getParent(), SFC); CallEnter PP(S, SFC, Pred->getLocationContext()); const GRState *state = Pred->getState(); state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest)); ExplodedNode *N = Builder->generateNode(PP, state, Pred); if (N) Dst.Add(N); }
// Returns the successor nodes of N whose CFGBlocks cannot reach N's CFGBlock. // This effectively gives us a set of points in the ExplodedGraph where // subsequent execution could not affect the idempotent operation on this path. // This is useful for displaying paths after the point of the error, providing // an example of how this idempotent operation cannot change. const ExplodedNodeSet IdempotentOperationChecker::getLastRelevantNodes( const CFGBlock *Begin, const ExplodedNode *N) { std::deque<const ExplodedNode *> WorkList; llvm::SmallPtrSet<const ExplodedNode *, 32> Visited; ExplodedNodeSet Result; WorkList.push_back(N); while (!WorkList.empty()) { const ExplodedNode *Head = WorkList.front(); WorkList.pop_front(); Visited.insert(Head); const ProgramPoint &PP = Head->getLocation(); if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&PP)) { // Get the CFGBlock and test the reachability const CFGBlock *CB = BE->getBlock(); // If we cannot reach the beginning CFGBlock from this block, then we are // finished if (!CRA.isReachable(CB, Begin)) { Result.Add(const_cast<ExplodedNode *>(Head)); continue; } } // Add unvisited children to the worklist for (ExplodedNode::const_succ_iterator I = Head->succ_begin(), E = Head->succ_end(); I != E; ++I) if (!Visited.count(*I)) WorkList.push_back(*I); } // Return the ExplodedNodes that were found return Result; }
/// The call exit is simulated with a sequence of nodes, which occur between /// CallExitBegin and CallExitEnd. The following operations occur between the /// two program points: /// 1. CallExitBegin (triggers the start of call exit sequence) /// 2. Bind the return value /// 3. Run Remove dead bindings to clean up the dead symbols from the callee. /// 4. CallExitEnd (switch to the caller context) /// 5. PostStmt<CallExpr> void ExprEngine::processCallExit(ExplodedNode *CEBNode) { // Step 1 CEBNode was generated before the call. PrettyStackTraceLocationContext CrashInfo(CEBNode->getLocationContext()); const StackFrameContext *calleeCtx = CEBNode->getLocationContext()->getCurrentStackFrame(); // The parent context might not be a stack frame, so make sure we // look up the first enclosing stack frame. const StackFrameContext *callerCtx = calleeCtx->getParent()->getCurrentStackFrame(); const Stmt *CE = calleeCtx->getCallSite(); ProgramStateRef state = CEBNode->getState(); // Find the last statement in the function and the corresponding basic block. const Stmt *LastSt = nullptr; const CFGBlock *Blk = nullptr; std::tie(LastSt, Blk) = getLastStmt(CEBNode); // Generate a CallEvent /before/ cleaning the state, so that we can get the // correct value for 'this' (if necessary). CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<> Call = CEMgr.getCaller(calleeCtx, state); // Step 2: generate node with bound return value: CEBNode -> BindedRetNode. // If the callee returns an expression, bind its value to CallExpr. if (CE) { if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) { const LocationContext *LCtx = CEBNode->getLocationContext(); SVal V = state->getSVal(RS, LCtx); // Ensure that the return type matches the type of the returned Expr. if (wasDifferentDeclUsedForInlining(Call, calleeCtx)) { QualType ReturnedTy = CallEvent::getDeclaredResultType(calleeCtx->getDecl()); if (!ReturnedTy.isNull()) { if (const Expr *Ex = dyn_cast<Expr>(CE)) { V = adjustReturnValue(V, Ex->getType(), ReturnedTy, getStoreManager()); } } } state = state->BindExpr(CE, callerCtx, V); } // Bind the constructed object value to CXXConstructExpr. if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) { loc::MemRegionVal This = svalBuilder.getCXXThis(CCE->getConstructor()->getParent(), calleeCtx); SVal ThisV = state->getSVal(This); // If the constructed object is a temporary prvalue, get its bindings. if (isTemporaryPRValue(CCE, ThisV)) ThisV = state->getSVal(ThisV.castAs<Loc>()); state = state->BindExpr(CCE, callerCtx, ThisV); } } // Step 3: BindedRetNode -> CleanedNodes // If we can find a statement and a block in the inlined function, run remove // dead bindings before returning from the call. This is important to ensure // that we report the issues such as leaks in the stack contexts in which // they occurred. ExplodedNodeSet CleanedNodes; if (LastSt && Blk && AMgr.options.AnalysisPurgeOpt != PurgeNone) { static SimpleProgramPointTag retValBind("ExprEngine", "Bind Return Value"); PostStmt Loc(LastSt, calleeCtx, &retValBind); bool isNew; ExplodedNode *BindedRetNode = G.getNode(Loc, state, false, &isNew); BindedRetNode->addPredecessor(CEBNode, G); if (!isNew) return; NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode); currBldrCtx = &Ctx; // Here, we call the Symbol Reaper with 0 statement and callee location // context, telling it to clean up everything in the callee's context // (and its children). We use the callee's function body as a diagnostic // statement, with which the program point will be associated. removeDead(BindedRetNode, CleanedNodes, nullptr, calleeCtx, calleeCtx->getAnalysisDeclContext()->getBody(), ProgramPoint::PostStmtPurgeDeadSymbolsKind); currBldrCtx = nullptr; } else { CleanedNodes.Add(CEBNode); } for (ExplodedNodeSet::iterator I = CleanedNodes.begin(), E = CleanedNodes.end(); I != E; ++I) { // Step 4: Generate the CallExit and leave the callee's context. // CleanedNodes -> CEENode CallExitEnd Loc(calleeCtx, callerCtx); bool isNew; ProgramStateRef CEEState = (*I == CEBNode) ? state : (*I)->getState(); ExplodedNode *CEENode = G.getNode(Loc, CEEState, false, &isNew); CEENode->addPredecessor(*I, G); if (!isNew) return; // Step 5: Perform the post-condition check of the CallExpr and enqueue the // result onto the work list. // CEENode -> Dst -> WorkList NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode); SaveAndRestore<const NodeBuilderContext*> NBCSave(currBldrCtx, &Ctx); SaveAndRestore<unsigned> CBISave(currStmtIdx, calleeCtx->getIndex()); CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState); ExplodedNodeSet DstPostCall; getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, *UpdatedCall, *this, /*WasInlined=*/true); ExplodedNodeSet Dst; if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) { getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, *Msg, *this, /*WasInlined=*/true); } else if (CE) { getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE, *this, /*WasInlined=*/true); } else { Dst.insert(DstPostCall); } // Enqueue the next element in the block. for (ExplodedNodeSet::iterator PSI = Dst.begin(), PSE = Dst.end(); PSI != PSE; ++PSI) { Engine.getWorkList()->enqueue(*PSI, calleeCtx->getCallSiteBlock(), calleeCtx->getIndex()+1); } } }
void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, ExplodedNode *Pred, ExplodedNodeSet &destNodes) { const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); const MemRegion *Target = nullptr; // FIXME: Handle arrays, which run the same constructor for every element. // For now, we just run the first constructor (which should still invalidate // the entire array). switch (CE->getConstructionKind()) { case CXXConstructExpr::CK_Complete: { Target = getRegionForConstructedObject(CE, Pred, *this, currStmtIdx); break; } case CXXConstructExpr::CK_VirtualBase: // Make sure we are not calling virtual base class initializers twice. // Only the most-derived object should initialize virtual base classes. if (const Stmt *Outer = LCtx->getCurrentStackFrame()->getCallSite()) { const CXXConstructExpr *OuterCtor = dyn_cast<CXXConstructExpr>(Outer); if (OuterCtor) { switch (OuterCtor->getConstructionKind()) { case CXXConstructExpr::CK_NonVirtualBase: case CXXConstructExpr::CK_VirtualBase: // Bail out! destNodes.Add(Pred); return; case CXXConstructExpr::CK_Complete: case CXXConstructExpr::CK_Delegating: break; } } } // FALLTHROUGH case CXXConstructExpr::CK_NonVirtualBase: case CXXConstructExpr::CK_Delegating: { const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, LCtx->getCurrentStackFrame()); SVal ThisVal = State->getSVal(ThisPtr); if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) { Target = ThisVal.getAsRegion(); } else { // Cast to the base type. bool IsVirtual = (CE->getConstructionKind() == CXXConstructExpr::CK_VirtualBase); SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, CE->getType(), IsVirtual); Target = BaseVal.getAsRegion(); } break; } } CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<CXXConstructorCall> Call = CEMgr.getCXXConstructorCall(CE, Target, State, LCtx); ExplodedNodeSet DstPreVisit; getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this); ExplodedNodeSet PreInitialized; { StmtNodeBuilder Bldr(DstPreVisit, PreInitialized, *currBldrCtx); if (CE->requiresZeroInitialization()) { // Type of the zero doesn't matter. SVal ZeroVal = svalBuilder.makeZeroVal(getContext().CharTy); for (ExplodedNodeSet::iterator I = DstPreVisit.begin(), E = DstPreVisit.end(); I != E; ++I) { ProgramStateRef State = (*I)->getState(); // FIXME: Once we properly handle constructors in new-expressions, we'll // need to invalidate the region before setting a default value, to make // sure there aren't any lingering bindings around. This probably needs // to happen regardless of whether or not the object is zero-initialized // to handle random fields of a placement-initialized object picking up // old bindings. We might only want to do it when we need to, though. // FIXME: This isn't actually correct for arrays -- we need to zero- // initialize the entire array, not just the first element -- but our // handling of arrays everywhere else is weak as well, so this shouldn't // actually make things worse. Placement new makes this tricky as well, // since it's then possible to be initializing one part of a multi- // dimensional array. State = State->bindDefault(loc::MemRegionVal(Target), ZeroVal); Bldr.generateNode(CE, *I, State, /*tag=*/nullptr, ProgramPoint::PreStmtKind); } } } ExplodedNodeSet DstPreCall; getCheckerManager().runCheckersForPreCall(DstPreCall, PreInitialized, *Call, *this); ExplodedNodeSet DstEvaluated; StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx); bool IsArray = isa<ElementRegion>(Target); if (CE->getConstructor()->isTrivial() && CE->getConstructor()->isCopyOrMoveConstructor() && !IsArray) { // FIXME: Handle other kinds of trivial constructors as well. for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); I != E; ++I) performTrivialCopy(Bldr, *I, *Call); } else { for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); I != E; ++I) defaultEvalCall(Bldr, *I, *Call); } ExplodedNodeSet DstPostCall; getCheckerManager().runCheckersForPostCall(DstPostCall, DstEvaluated, *Call, *this); getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this); }
void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, const MemRegion *Dest, ExplodedNode *Pred, ExplodedNodeSet &destNodes) { const CXXConstructorDecl *CD = E->getConstructor(); assert(CD); #if 0 if (!(CD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall())) // FIXME: invalidate the object. return; #endif // Evaluate other arguments. ExplodedNodeSet argsEvaluated; const FunctionProtoType *FnType = CD->getType()->getAs<FunctionProtoType>(); evalArguments(E->arg_begin(), E->arg_end(), FnType, Pred, argsEvaluated); #if 0 // Is the constructor elidable? if (E->isElidable()) { VisitAggExpr(E->getArg(0), destNodes, Pred, Dst); // FIXME: this is here to force propagation if VisitAggExpr doesn't if (destNodes.empty()) destNodes.Add(Pred); return; } #endif // Perform the previsit of the constructor. ExplodedNodeSet destPreVisit; getCheckerManager().runCheckersForPreStmt(destPreVisit, argsEvaluated, E, *this); // Evaluate the constructor. Currently we don't now allow checker-specific // implementations of specific constructors (as we do with ordinary // function calls. We can re-evaluate this in the future. #if 0 // Inlining currently isn't fully implemented. if (AMgr.shouldInlineCall()) { if (!Dest) Dest = svalBuilder.getRegionManager().getCXXTempObjectRegion(E, Pred->getLocationContext()); // The callee stack frame context used to create the 'this' // parameter region. const StackFrameContext *SFC = AMgr.getStackFrame(CD, Pred->getLocationContext(), E, Builder->getBlock(), Builder->getIndex()); // Create the 'this' region. const CXXThisRegion *ThisR = getCXXThisRegion(E->getConstructor()->getParent(), SFC); CallEnter Loc(E, SFC, Pred->getLocationContext()); for (ExplodedNodeSet::iterator NI = argsEvaluated.begin(), NE = argsEvaluated.end(); NI != NE; ++NI) { const GRState *state = GetState(*NI); // Setup 'this' region, so that the ctor is evaluated on the object pointed // by 'Dest'. state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest)); if (ExplodedNode *N = Builder->generateNode(Loc, state, *NI)) destNodes.Add(N); } } #endif // Default semantics: invalidate all regions passed as arguments. llvm::SmallVector<const MemRegion*, 10> regionsToInvalidate; // FIXME: We can have collisions on the conjured symbol if the // expression *I also creates conjured symbols. We probably want // to identify conjured symbols by an expression pair: the enclosing // expression (the context) and the expression itself. This should // disambiguate conjured symbols. unsigned blockCount = Builder->getCurrentBlockCount(); // NOTE: Even if RegionsToInvalidate is empty, we must still invalidate // global variables. ExplodedNodeSet destCall; for (ExplodedNodeSet::iterator i = destPreVisit.begin(), e = destPreVisit.end(); i != e; ++i) { ExplodedNode *Pred = *i; const GRState *state = GetState(Pred); // Accumulate list of regions that are invalidated. for (CXXConstructExpr::const_arg_iterator ai = E->arg_begin(), ae = E->arg_end(); ai != ae; ++ai) { SVal val = state->getSVal(*ai); if (const MemRegion *region = val.getAsRegion()) regionsToInvalidate.push_back(region); } // Invalidate the regions. state = state->invalidateRegions(regionsToInvalidate.data(), regionsToInvalidate.data() + regionsToInvalidate.size(), E, blockCount, 0, /* invalidateGlobals = */ true); Builder->MakeNode(destCall, E, Pred, state); } // Do the post visit. getCheckerManager().runCheckersForPostStmt(destNodes, destCall, E, *this); }
void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, const MemRegion *Dest, ExplodedNode *Pred, ExplodedNodeSet &destNodes) { const CXXConstructorDecl *CD = E->getConstructor(); assert(CD); #if 0 if (!(CD->doesThisDeclarationHaveABody() && AMgr.shouldInlineCall())) // FIXME: invalidate the object. return; #endif // Evaluate other arguments. ExplodedNodeSet argsEvaluated; const FunctionProtoType *FnType = CD->getType()->getAs<FunctionProtoType>(); evalArguments(E->arg_begin(), E->arg_end(), FnType, Pred, argsEvaluated); #if 0 // Is the constructor elidable? if (E->isElidable()) { VisitAggExpr(E->getArg(0), destNodes, Pred, Dst); // FIXME: this is here to force propagation if VisitAggExpr doesn't if (destNodes.empty()) destNodes.Add(Pred); return; } #endif // Perform the previsit of the constructor. ExplodedNodeSet destPreVisit; getCheckerManager().runCheckersForPreStmt(destPreVisit, argsEvaluated, E, *this); // Evaluate the constructor. Currently we don't now allow checker-specific // implementations of specific constructors (as we do with ordinary // function calls. We can re-evaluate this in the future. #if 0 // Inlining currently isn't fully implemented. if (AMgr.shouldInlineCall()) { if (!Dest) Dest = svalBuilder.getRegionManager().getCXXTempObjectRegion(E, Pred->getLocationContext()); // The callee stack frame context used to create the 'this' // parameter region. const StackFrameContext *SFC = AMgr.getStackFrame(CD, Pred->getLocationContext(), E, currentBuilderContext->getBlock(), currentStmtIdx); // Create the 'this' region. const CXXThisRegion *ThisR = getCXXThisRegion(E->getConstructor()->getParent(), SFC); CallEnter Loc(E, SFC, Pred->getLocationContext()); StmtNodeBuilder Bldr(argsEvaluated, destNodes, *currentBuilderContext); for (ExplodedNodeSet::iterator NI = argsEvaluated.begin(), NE = argsEvaluated.end(); NI != NE; ++NI) { const ProgramState *state = (*NI)->getState(); // Setup 'this' region, so that the ctor is evaluated on the object pointed // by 'Dest'. state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest)); Bldr.generateNode(Loc, *NI, state); } } #endif // Default semantics: invalidate all regions passed as arguments. ExplodedNodeSet destCall; { StmtNodeBuilder Bldr(destPreVisit, destCall, *currentBuilderContext); for (ExplodedNodeSet::iterator i = destPreVisit.begin(), e = destPreVisit.end(); i != e; ++i) { ExplodedNode *Pred = *i; const LocationContext *LC = Pred->getLocationContext(); const ProgramState *state = Pred->getState(); state = invalidateArguments(state, CallOrObjCMessage(E, state, LC), LC); Bldr.generateNode(E, Pred, state); } } // Do the post visit. getCheckerManager().runCheckersForPostStmt(destNodes, destCall, E, *this); }
void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, ExplodedNode *Pred, ExplodedNodeSet &destNodes) { const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); SVal Target = UnknownVal(); if (Optional<SVal> ElidedTarget = getObjectUnderConstruction(State, CE, LCtx)) { // We've previously modeled an elidable constructor by pretending that it in // fact constructs into the correct target. This constructor can therefore // be skipped. Target = *ElidedTarget; StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx); State = finishObjectConstruction(State, CE, LCtx); if (auto L = Target.getAs<Loc>()) State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType())); Bldr.generateNode(CE, Pred, State); return; } // FIXME: Handle arrays, which run the same constructor for every element. // For now, we just run the first constructor (which should still invalidate // the entire array). EvalCallOptions CallOpts; auto C = getCurrentCFGElement().getAs<CFGConstructor>(); assert(C || getCurrentCFGElement().getAs<CFGStmt>()); const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr; switch (CE->getConstructionKind()) { case CXXConstructExpr::CK_Complete: { std::tie(State, Target) = prepareForObjectConstruction(CE, State, LCtx, CC, CallOpts); break; } case CXXConstructExpr::CK_VirtualBase: // Make sure we are not calling virtual base class initializers twice. // Only the most-derived object should initialize virtual base classes. if (const Stmt *Outer = LCtx->getStackFrame()->getCallSite()) { const CXXConstructExpr *OuterCtor = dyn_cast<CXXConstructExpr>(Outer); if (OuterCtor) { switch (OuterCtor->getConstructionKind()) { case CXXConstructExpr::CK_NonVirtualBase: case CXXConstructExpr::CK_VirtualBase: // Bail out! destNodes.Add(Pred); return; case CXXConstructExpr::CK_Complete: case CXXConstructExpr::CK_Delegating: break; } } } LLVM_FALLTHROUGH; case CXXConstructExpr::CK_NonVirtualBase: // In C++17, classes with non-virtual bases may be aggregates, so they would // be initialized as aggregates without a constructor call, so we may have // a base class constructed directly into an initializer list without // having the derived-class constructor call on the previous stack frame. // Initializer lists may be nested into more initializer lists that // correspond to surrounding aggregate initializations. // FIXME: For now this code essentially bails out. We need to find the // correct target region and set it. // FIXME: Instead of relying on the ParentMap, we should have the // trigger-statement (InitListExpr in this case) passed down from CFG or // otherwise always available during construction. if (dyn_cast_or_null<InitListExpr>(LCtx->getParentMap().getParent(CE))) { MemRegionManager &MRMgr = getSValBuilder().getRegionManager(); Target = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(CE, LCtx)); CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; break; } LLVM_FALLTHROUGH; case CXXConstructExpr::CK_Delegating: { const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, LCtx->getStackFrame()); SVal ThisVal = State->getSVal(ThisPtr); if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) { Target = ThisVal; } else { // Cast to the base type. bool IsVirtual = (CE->getConstructionKind() == CXXConstructExpr::CK_VirtualBase); SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, CE->getType(), IsVirtual); Target = BaseVal; } break; } } if (State != Pred->getState()) { static SimpleProgramPointTag T("ExprEngine", "Prepare for object construction"); ExplodedNodeSet DstPrepare; StmtNodeBuilder BldrPrepare(Pred, DstPrepare, *currBldrCtx); BldrPrepare.generateNode(CE, Pred, State, &T, ProgramPoint::PreStmtKind); assert(DstPrepare.size() <= 1); if (DstPrepare.size() == 0) return; Pred = *BldrPrepare.begin(); } CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<CXXConstructorCall> Call = CEMgr.getCXXConstructorCall(CE, Target.getAsRegion(), State, LCtx); ExplodedNodeSet DstPreVisit; getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this); // FIXME: Is it possible and/or useful to do this before PreStmt? ExplodedNodeSet PreInitialized; { StmtNodeBuilder Bldr(DstPreVisit, PreInitialized, *currBldrCtx); for (ExplodedNodeSet::iterator I = DstPreVisit.begin(), E = DstPreVisit.end(); I != E; ++I) { ProgramStateRef State = (*I)->getState(); if (CE->requiresZeroInitialization()) { // FIXME: Once we properly handle constructors in new-expressions, we'll // need to invalidate the region before setting a default value, to make // sure there aren't any lingering bindings around. This probably needs // to happen regardless of whether or not the object is zero-initialized // to handle random fields of a placement-initialized object picking up // old bindings. We might only want to do it when we need to, though. // FIXME: This isn't actually correct for arrays -- we need to zero- // initialize the entire array, not just the first element -- but our // handling of arrays everywhere else is weak as well, so this shouldn't // actually make things worse. Placement new makes this tricky as well, // since it's then possible to be initializing one part of a multi- // dimensional array. State = State->bindDefaultZero(Target, LCtx); } Bldr.generateNode(CE, *I, State, /*tag=*/nullptr, ProgramPoint::PreStmtKind); } } ExplodedNodeSet DstPreCall; getCheckerManager().runCheckersForPreCall(DstPreCall, PreInitialized, *Call, *this); ExplodedNodeSet DstEvaluated; StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx); if (CE->getConstructor()->isTrivial() && CE->getConstructor()->isCopyOrMoveConstructor() && !CallOpts.IsArrayCtorOrDtor) { // FIXME: Handle other kinds of trivial constructors as well. for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); I != E; ++I) performTrivialCopy(Bldr, *I, *Call); } else { for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); I != E; ++I) defaultEvalCall(Bldr, *I, *Call, CallOpts); } // If the CFG was constructed without elements for temporary destructors // and the just-called constructor created a temporary object then // stop exploration if the temporary object has a noreturn constructor. // This can lose coverage because the destructor, if it were present // in the CFG, would be called at the end of the full expression or // later (for life-time extended temporaries) -- but avoids infeasible // paths when no-return temporary destructors are used for assertions. const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext(); if (!ADC->getCFGBuildOptions().AddTemporaryDtors) { const MemRegion *Target = Call->getCXXThisVal().getAsRegion(); if (Target && isa<CXXTempObjectRegion>(Target) && Call->getDecl()->getParent()->isAnyDestructorNoReturn()) { // If we've inlined the constructor, then DstEvaluated would be empty. // In this case we still want a sink, which could be implemented // in processCallExit. But we don't have that implemented at the moment, // so if you hit this assertion, see if you can avoid inlining // the respective constructor when analyzer-config cfg-temporary-dtors // is set to false. // Otherwise there's nothing wrong with inlining such constructor. assert(!DstEvaluated.empty() && "We should not have inlined this constructor!"); for (ExplodedNode *N : DstEvaluated) { Bldr.generateSink(CE, N, N->getState()); } // There is no need to run the PostCall and PostStmt checker // callbacks because we just generated sinks on all nodes in th // frontier. return; } } ExplodedNodeSet DstPostArgumentCleanup; for (auto I : DstEvaluated) finishArgumentConstruction(DstPostArgumentCleanup, I, *Call); // If there were other constructors called for object-type arguments // of this constructor, clean them up. ExplodedNodeSet DstPostCall; getCheckerManager().runCheckersForPostCall(DstPostCall, DstPostArgumentCleanup, *Call, *this); getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this); }
/// The call exit is simulated with a sequence of nodes, which occur between /// CallExitBegin and CallExitEnd. The following operations occur between the /// two program points: /// 1. CallExitBegin (triggers the start of call exit sequence) /// 2. Bind the return value /// 3. Run Remove dead bindings to clean up the dead symbols from the callee. /// 4. CallExitEnd (switch to the caller context) /// 5. PostStmt<CallExpr> void ExprEngine::processCallExit(ExplodedNode *CEBNode) { // Step 1 CEBNode was generated before the call. const StackFrameContext *calleeCtx = CEBNode->getLocationContext()->getCurrentStackFrame(); // The parent context might not be a stack frame, so make sure we // look up the first enclosing stack frame. const StackFrameContext *callerCtx = calleeCtx->getParent()->getCurrentStackFrame(); const Stmt *CE = calleeCtx->getCallSite(); ProgramStateRef state = CEBNode->getState(); // Find the last statement in the function and the corresponding basic block. const Stmt *LastSt = 0; const CFGBlock *Blk = 0; llvm::tie(LastSt, Blk) = getLastStmt(CEBNode); // Step 2: generate node with binded return value: CEBNode -> BindedRetNode. // If the callee returns an expression, bind its value to CallExpr. if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) { const LocationContext *LCtx = CEBNode->getLocationContext(); SVal V = state->getSVal(RS, LCtx); state = state->BindExpr(CE, callerCtx, V); } // Bind the constructed object value to CXXConstructExpr. if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) { loc::MemRegionVal This = svalBuilder.getCXXThis(CCE->getConstructor()->getParent(), calleeCtx); SVal ThisV = state->getSVal(This); // Always bind the region to the CXXConstructExpr. state = state->BindExpr(CCE, CEBNode->getLocationContext(), ThisV); } static SimpleProgramPointTag retValBindTag("ExprEngine : Bind Return Value"); PostStmt Loc(LastSt, calleeCtx, &retValBindTag); bool isNew; ExplodedNode *BindedRetNode = G.getNode(Loc, state, false, &isNew); BindedRetNode->addPredecessor(CEBNode, G); if (!isNew) return; // Step 3: BindedRetNode -> CleanedNodes // If we can find a statement and a block in the inlined function, run remove // dead bindings before returning from the call. This is important to ensure // that we report the issues such as leaks in the stack contexts in which // they occurred. ExplodedNodeSet CleanedNodes; if (LastSt && Blk) { NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode); currentBuilderContext = &Ctx; // Here, we call the Symbol Reaper with 0 statement and caller location // context, telling it to clean up everything in the callee's context // (and it's children). We use LastStmt as a diagnostic statement, which // which the PreStmtPurge Dead point will be associated. removeDead(BindedRetNode, CleanedNodes, 0, callerCtx, LastSt, ProgramPoint::PostStmtPurgeDeadSymbolsKind); currentBuilderContext = 0; } else { CleanedNodes.Add(CEBNode); } for (ExplodedNodeSet::iterator I = CleanedNodes.begin(), E = CleanedNodes.end(); I != E; ++I) { // Step 4: Generate the CallExit and leave the callee's context. // CleanedNodes -> CEENode CallExitEnd Loc(CE, callerCtx); bool isNew; ExplodedNode *CEENode = G.getNode(Loc, (*I)->getState(), false, &isNew); CEENode->addPredecessor(*I, G); if (!isNew) return; // Step 5: Perform the post-condition check of the CallExpr and enqueue the // result onto the work list. // CEENode -> Dst -> WorkList ExplodedNodeSet Dst; NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode); SaveAndRestore<const NodeBuilderContext*> NBCSave(currentBuilderContext, &Ctx); SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex()); getCheckerManager().runCheckersForPostStmt(Dst, CEENode, CE, *this, true); // Enqueue the next element in the block. for (ExplodedNodeSet::iterator PSI = Dst.begin(), PSE = Dst.end(); PSI != PSE; ++PSI) { Engine.getWorkList()->enqueue(*PSI, calleeCtx->getCallSiteBlock(), calleeCtx->getIndex()+1); } } }