diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer')
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp | 5 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/CallEvent.cpp | 62 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 33 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 80 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 54 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp | 16 |
6 files changed, 221 insertions, 29 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 7d6358acbba..cff0f0a29b0 100644 --- a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -156,6 +156,11 @@ void NilArgChecker::warnIfNilArg(CheckerContext &C, if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue()) return; + // NOTE: We cannot throw non-fatal errors from warnIfNilExpr, + // because it's called multiple times from some callers, so it'd cause + // an unwanted state split if two or more non-fatal errors are thrown + // within the same checker callback. For now we don't want to, but + // it'll need to be fixed if we ever want to. if (ExplodedNode *N = C.generateErrorNode()) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index fe9260e32dd..b1688184d87 100644 --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -169,18 +169,27 @@ bool CallEvent::isGlobalCFunction(StringRef FunctionName) const { 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; + // TODO: For now we skip functions without definitions, even if we have + // our own getDecl(), because it's hard to find out which re-declaration + // is going to be used, and usually clients don't really care about this + // situation because there's a loss of precision anyway because we cannot + // inline the call. + RuntimeDefinition RD = getRuntimeDefinition(); + if (!RD.getDecl()) + return nullptr; + + AnalysisDeclContext *ADC = + LCtx->getAnalysisDeclContext()->getManager()->getContext(D); + + // TODO: For now we skip virtual functions, because this also rises + // the problem of which decl to use, but now it's across different classes. + if (RD.mayHaveOtherDefinitions() || RD.getDecl() != ADC->getDecl()) + return nullptr; - return LCtx->getAnalysisDeclContext()->getManager()->getContext(D); + return ADC; } const StackFrameContext *CallEvent::getCalleeStackFrame() const { @@ -218,7 +227,24 @@ const VarRegion *CallEvent::getParameterLocation(unsigned Index) const { if (!SFC) return nullptr; - const ParmVarDecl *PVD = parameters()[Index]; + // Retrieve parameters of the definition, which are different from + // CallEvent's parameters() because getDecl() isn't necessarily + // the definition. SFC contains the definition that would be used + // during analysis. + const Decl *D = SFC->getDecl(); + + // TODO: Refactor into a virtual method of CallEvent, like parameters(). + const ParmVarDecl *PVD = nullptr; + if (const auto *FD = dyn_cast<FunctionDecl>(D)) + PVD = FD->parameters()[Index]; + else if (const auto *BD = dyn_cast<BlockDecl>(D)) + PVD = BD->parameters()[Index]; + else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) + PVD = MD->parameters()[Index]; + else if (const auto *CD = dyn_cast<CXXConstructorDecl>(D)) + PVD = CD->parameters()[Index]; + assert(PVD && "Unexpected Decl kind!"); + const VarRegion *VR = State->getStateManager().getRegionManager().getVarRegion(PVD, SFC); @@ -285,6 +311,20 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount, // TODO: Factor this out + handle the lower level const pointers. ValuesToInvalidate.push_back(getArgSVal(Idx)); + + // If a function accepts an object by argument (which would of course be a + // temporary that isn't lifetime-extended), invalidate the object itself, + // not only other objects reachable from it. This is necessary because the + // destructor has access to the temporary object after the call. + // TODO: Support placement arguments once we start + // constructing them directly. + // TODO: This is unnecessary when there's no destructor, but that's + // currently hard to figure out. + if (getKind() != CE_CXXAllocator) + if (isArgumentConstructedDirectly(Idx)) + if (auto AdjIdx = getAdjustedParameterIndex(Idx)) + if (const VarRegion *VR = getParameterLocation(*AdjIdx)) + ValuesToInvalidate.push_back(loc::MemRegionVal(VR)); } // Invalidate designated regions using the batch invalidation API. @@ -433,6 +473,10 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx, const ParmVarDecl *ParamDecl = *I; assert(ParamDecl && "Formal parameter has no decl?"); + if (Call.getKind() != CE_CXXAllocator) + if (Call.isArgumentConstructedDirectly(Idx)) + continue; + SVal ArgVal = Call.getArgSVal(Idx); if (!ArgVal.isUnknown()) { Loc ParamLoc = SVB.makeLoc(MRMgr.getVarRegion(ParamDecl, CalleeCtx)); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 40259fb0c03..8469a601b06 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -2199,17 +2199,21 @@ void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC, void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, ExplodedNode *Pred, const ReturnStmt *RS) { + ProgramStateRef State = Pred->getState(); + + if (!Pred->getStackFrame()->inTopFrame()) + State = finishArgumentConstruction( + State, *getStateManager().getCallEventManager().getCaller( + Pred->getStackFrame(), Pred->getState())); + // FIXME: We currently cannot assert that temporaries are clear, because // lifetime extended temporaries are not always modelled correctly. In some // cases when we materialize the temporary, we do // createTemporaryRegionIfNeeded(), and the region changes, and also the // respective destructor becomes automatic from temporary. So for now clean up - // the state manually before asserting. Ideally, the code above the assertion - // should go away, but the assertion should remain. + // the state manually before asserting. Ideally, this braced block of code + // should go away. { - ExplodedNodeSet CleanUpObjects; - NodeBuilder Bldr(Pred, CleanUpObjects, BC); - ProgramStateRef State = Pred->getState(); const LocationContext *FromLC = Pred->getLocationContext(); const LocationContext *ToLC = FromLC->getStackFrame()->getParent(); const LocationContext *LC = FromLC; @@ -2228,15 +2232,20 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, } LC = LC->getParent(); } - if (State != Pred->getState()) { - Pred = Bldr.generateNode(Pred->getLocation(), State, Pred); - if (!Pred) { - // The node with clean temporaries already exists. We might have reached - // it on a path on which we initialize different temporaries. - return; - } + } + + // Perform the transition with cleanups. + if (State != Pred->getState()) { + ExplodedNodeSet PostCleanup; + NodeBuilder Bldr(Pred, PostCleanup, BC); + Pred = Bldr.generateNode(Pred->getLocation(), State, Pred); + if (!Pred) { + // The node with clean temporaries already exists. We might have reached + // it on a path on which we initialize different temporaries. + return; } } + assert(areAllObjectsFullyConstructed(Pred->getState(), Pred->getLocationContext(), Pred->getStackFrame()->getParent())); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 90dcf2f22e6..933d54e11c8 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -292,8 +292,75 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction( return std::make_pair(State, V); } case ConstructionContext::ArgumentKind: { - // Function argument constructors. Not implemented yet. - break; + // Arguments are technically temporaries. + CallOpts.IsTemporaryCtorOrDtor = true; + + const auto *ACC = cast<ArgumentConstructionContext>(CC); + const Expr *E = ACC->getCallLikeExpr(); + unsigned Idx = ACC->getIndex(); + const CXXBindTemporaryExpr *BTE = ACC->getCXXBindTemporaryExpr(); + + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + SVal V = UnknownVal(); + auto getArgLoc = [&](CallEventRef<> Caller) -> Optional<SVal> { + const LocationContext *FutureSFC = Caller->getCalleeStackFrame(); + // Return early if we are unable to reliably foresee + // the future stack frame. + if (!FutureSFC) + return None; + + // This should be equivalent to Caller->getDecl() for now, but + // FutureSFC->getDecl() is likely to support better stuff (like + // virtual functions) earlier. + const Decl *CalleeD = FutureSFC->getDecl(); + + // FIXME: Support for variadic arguments is not implemented here yet. + if (CallEvent::isVariadic(CalleeD)) + return None; + + // Operator arguments do not correspond to operator parameters + // because this-argument is implemented as a normal argument in + // operator call expressions but not in operator declarations. + const VarRegion *VR = Caller->getParameterLocation( + *Caller->getAdjustedParameterIndex(Idx)); + if (!VR) + return None; + + return loc::MemRegionVal(VR); + }; + + if (const auto *CE = dyn_cast<CallExpr>(E)) { + CallEventRef<> Caller = CEMgr.getSimpleCall(CE, State, LCtx); + if (auto OptV = getArgLoc(Caller)) + V = *OptV; + else + break; + State = addObjectUnderConstruction(State, {CE, Idx}, LCtx, V); + } else if (const auto *CCE = dyn_cast<CXXConstructExpr>(E)) { + // Don't bother figuring out the target region for the future + // constructor because we won't need it. + CallEventRef<> Caller = + CEMgr.getCXXConstructorCall(CCE, /*Target=*/nullptr, State, LCtx); + if (auto OptV = getArgLoc(Caller)) + V = *OptV; + else + break; + State = addObjectUnderConstruction(State, {CCE, Idx}, LCtx, V); + } else if (const auto *ME = dyn_cast<ObjCMessageExpr>(E)) { + CallEventRef<> Caller = CEMgr.getObjCMethodCall(ME, State, LCtx); + if (auto OptV = getArgLoc(Caller)) + V = *OptV; + else + break; + State = addObjectUnderConstruction(State, {ME, Idx}, LCtx, V); + } + + assert(!V.isUnknown()); + + if (BTE) + State = addObjectUnderConstruction(State, BTE, LCtx, V); + + return std::make_pair(State, V); } } } @@ -502,8 +569,15 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, } } + 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, DstEvaluated, + getCheckerManager().runCheckersForPostCall(DstPostCall, + DstPostArgumentCleanup, *Call, *this); getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this); } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 3ee67f3d688..6e78111b0cb 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -505,6 +505,49 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, *this); } +ProgramStateRef ExprEngine::finishArgumentConstruction(ProgramStateRef State, + const CallEvent &Call) { + const Expr *E = Call.getOriginExpr(); + // FIXME: Constructors to placement arguments of operator new + // are not supported yet. + if (!E || isa<CXXNewExpr>(E)) + return State; + + const LocationContext *LC = Call.getLocationContext(); + for (unsigned CallI = 0, CallN = Call.getNumArgs(); CallI != CallN; ++CallI) { + unsigned I = Call.getASTArgumentIndex(CallI); + if (Optional<SVal> V = + getObjectUnderConstruction(State, {E, I}, LC)) { + SVal VV = *V; + assert(cast<VarRegion>(VV.castAs<loc::MemRegionVal>().getRegion()) + ->getStackFrame()->getParent() + ->getStackFrame() == LC->getStackFrame()); + State = finishObjectConstruction(State, {E, I}, LC); + } + } + + return State; +} + +void ExprEngine::finishArgumentConstruction(ExplodedNodeSet &Dst, + ExplodedNode *Pred, + const CallEvent &Call) { + ProgramStateRef State = Pred->getState(); + ProgramStateRef CleanedState = finishArgumentConstruction(State, Call); + if (CleanedState == State) { + Dst.insert(Pred); + return; + } + + const Expr *E = Call.getOriginExpr(); + const LocationContext *LC = Call.getLocationContext(); + NodeBuilder B(Pred, Dst, *currBldrCtx); + static SimpleProgramPointTag Tag("ExprEngine", + "Finish argument construction"); + PreStmt PP(E, LC, &Tag); + B.generateNode(PP, CleanedState, Pred); +} + void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, const CallEvent &Call) { // WARNING: At this time, the state attached to 'Call' may be older than the @@ -516,7 +559,8 @@ void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, // Run any pre-call checks using the generic call interface. ExplodedNodeSet dstPreVisit; - getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred, Call, *this); + getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred, + Call, *this); // Actually evaluate the function call. We try each of the checkers // to see if the can evaluate the function call, and get a callback at @@ -525,8 +569,14 @@ void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, dstPreVisit, Call, *this); + // If there were other constructors called for object-type arguments + // of this call, clean them up. + ExplodedNodeSet dstArgumentCleanup; + for (auto I : dstCallEvaluated) + finishArgumentConstruction(dstArgumentCleanup, I, Call); + // Finally, run any post-call checks. - getCheckerManager().runCheckersForPostCall(Dst, dstCallEvaluated, + getCheckerManager().runCheckersForPostCall(Dst, dstArgumentCleanup, Call, *this); } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index d76b9cbcfac..d36d1fbd171 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -197,7 +197,8 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, // Receiver is definitely nil, so run ObjCMessageNil callbacks and return. if (nilState && !notNilState) { - StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + ExplodedNodeSet dstNil; + StmtNodeBuilder Bldr(Pred, dstNil, *currBldrCtx); bool HasTag = Pred->getLocation().getTag(); Pred = Bldr.generateNode(ME, Pred, nilState, nullptr, ProgramPoint::PreStmtKind); @@ -205,8 +206,12 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, (void)HasTag; if (!Pred) return; - getCheckerManager().runCheckersForObjCMessageNil(Dst, Pred, + + ExplodedNodeSet dstPostCheckers; + getCheckerManager().runCheckersForObjCMessageNil(dstPostCheckers, Pred, *Msg, *this); + for (auto I : dstPostCheckers) + finishArgumentConstruction(Dst, I, *Msg); return; } @@ -267,8 +272,13 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, defaultEvalCall(Bldr, Pred, *UpdatedMsg); } + // If there were constructors called for object-type arguments, clean them up. + ExplodedNodeSet dstArgCleanup; + for (auto I : dstEval) + finishArgumentConstruction(dstArgCleanup, I, *Msg); + ExplodedNodeSet dstPostvisit; - getCheckerManager().runCheckersForPostCall(dstPostvisit, dstEval, + getCheckerManager().runCheckersForPostCall(dstPostvisit, dstArgCleanup, *Msg, *this); // Finally, perform the post-condition check of the ObjCMessageExpr and store |