diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp')
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 171 |
1 files changed, 116 insertions, 55 deletions
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 04e00274b2a7..6eb37287b136 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -10,15 +10,17 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "clang/Analysis/ConstructionContext.h" #include "clang/AST/DeclCXX.h" -#include "clang/AST/StmtCXX.h" #include "clang/AST/ParentMap.h" +#include "clang/AST/StmtCXX.h" +#include "clang/Analysis/ConstructionContext.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include <optional> using namespace clang; using namespace ento; @@ -74,7 +76,7 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, // If the value being copied is not unknown, load from its location to get // an aggregate rvalue. - if (Optional<Loc> L = V.getAs<Loc>()) + if (std::optional<Loc> L = V.getAs<Loc>()) V = Pred->getState()->getSVal(*L); else assert(V.isUnknownOrUndef()); @@ -111,9 +113,15 @@ SVal ExprEngine::makeElementRegion(ProgramStateRef State, SVal LValue, return LValue; } +// In case when the prvalue is returned from the function (kind is one of +// SimpleReturnedValueKind, CXX17ElidedCopyReturnedValueKind), then +// it's materialization happens in context of the caller. +// We pass BldrCtx explicitly, as currBldrCtx always refers to callee's context. SVal ExprEngine::computeObjectUnderConstruction( - const Expr *E, ProgramStateRef State, const LocationContext *LCtx, - const ConstructionContext *CC, EvalCallOptions &CallOpts, unsigned Idx) { + const Expr *E, ProgramStateRef State, const NodeBuilderContext *BldrCtx, + const LocationContext *LCtx, const ConstructionContext *CC, + EvalCallOptions &CallOpts, unsigned Idx) { + SValBuilder &SVB = getSValBuilder(); MemRegionManager &MRMgr = SVB.getRegionManager(); ASTContext &ACtx = SVB.getContext(); @@ -171,13 +179,14 @@ SVal ExprEngine::computeObjectUnderConstruction( if (const SubRegion *MR = dyn_cast_or_null<SubRegion>(V.getAsRegion())) { if (NE->isArray()) { - // TODO: In fact, we need to call the constructor for every - // allocated element, not just the first one! CallOpts.IsArrayCtorOrDtor = true; - auto R = MRMgr.getElementRegion(NE->getType()->getPointeeType(), - svalBuilder.makeArrayIndex(Idx), MR, - SVB.getContext()); + auto Ty = NE->getType()->getPointeeType(); + while (const auto *AT = getContext().getAsArrayType(Ty)) + Ty = AT->getElementType(); + + auto R = MRMgr.getElementRegion(Ty, svalBuilder.makeArrayIndex(Idx), + MR, SVB.getContext()); return loc::MemRegionVal(R); } @@ -209,8 +218,11 @@ SVal ExprEngine::computeObjectUnderConstruction( CallerLCtx = CallerLCtx->getParent(); assert(!isa<BlockInvocationContext>(CallerLCtx)); } + + NodeBuilderContext CallerBldrCtx(getCoreEngine(), + SFC->getCallSiteBlock(), CallerLCtx); return computeObjectUnderConstruction( - cast<Expr>(SFC->getCallSite()), State, CallerLCtx, + cast<Expr>(SFC->getCallSite()), State, &CallerBldrCtx, CallerLCtx, RTC->getConstructionContext(), CallOpts); } else { // We are on the top frame of the analysis. We do not know where is the @@ -250,7 +262,7 @@ SVal ExprEngine::computeObjectUnderConstruction( EvalCallOptions PreElideCallOpts = CallOpts; SVal V = computeObjectUnderConstruction( - TCC->getConstructorAfterElision(), State, LCtx, + TCC->getConstructorAfterElision(), State, BldrCtx, LCtx, TCC->getConstructionContextAfterElision(), CallOpts); // FIXME: This definition of "copy elision has not failed" is unreliable. @@ -263,7 +275,7 @@ SVal ExprEngine::computeObjectUnderConstruction( // a simple temporary. CallOpts = PreElideCallOpts; CallOpts.IsElidableCtorThatHasNotBeenElided = true; - LLVM_FALLTHROUGH; + [[fallthrough]]; } case ConstructionContext::SimpleTemporaryObjectKind: { const auto *TCC = cast<TemporaryObjectConstructionContext>(CC); @@ -316,13 +328,13 @@ SVal ExprEngine::computeObjectUnderConstruction( unsigned Idx = ACC->getIndex(); CallEventManager &CEMgr = getStateManager().getCallEventManager(); - auto getArgLoc = [&](CallEventRef<> Caller) -> Optional<SVal> { + auto getArgLoc = [&](CallEventRef<> Caller) -> std::optional<SVal> { const LocationContext *FutureSFC = - Caller->getCalleeStackFrame(currBldrCtx->blockCount()); + Caller->getCalleeStackFrame(BldrCtx->blockCount()); // Return early if we are unable to reliably foresee // the future stack frame. if (!FutureSFC) - return None; + return std::nullopt; // This should be equivalent to Caller->getDecl() for now, but // FutureSFC->getDecl() is likely to support better stuff (like @@ -331,22 +343,22 @@ SVal ExprEngine::computeObjectUnderConstruction( // FIXME: Support for variadic arguments is not implemented here yet. if (CallEvent::isVariadic(CalleeD)) - return None; + return std::nullopt; // 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 TypedValueRegion *TVR = Caller->getParameterLocation( - *Caller->getAdjustedParameterIndex(Idx), currBldrCtx->blockCount()); + *Caller->getAdjustedParameterIndex(Idx), BldrCtx->blockCount()); if (!TVR) - return None; + return std::nullopt; return loc::MemRegionVal(TVR); }; if (const auto *CE = dyn_cast<CallExpr>(E)) { CallEventRef<> Caller = CEMgr.getSimpleCall(CE, State, LCtx); - if (Optional<SVal> V = getArgLoc(Caller)) + if (std::optional<SVal> V = getArgLoc(Caller)) return *V; else break; @@ -355,13 +367,13 @@ SVal ExprEngine::computeObjectUnderConstruction( // constructor because we won't need it. CallEventRef<> Caller = CEMgr.getCXXConstructorCall(CCE, /*Target=*/nullptr, State, LCtx); - if (Optional<SVal> V = getArgLoc(Caller)) + if (std::optional<SVal> V = getArgLoc(Caller)) return *V; else break; } else if (const auto *ME = dyn_cast<ObjCMessageExpr>(E)) { CallEventRef<> Caller = CEMgr.getObjCMethodCall(ME, State, LCtx); - if (Optional<SVal> V = getArgLoc(Caller)) + if (std::optional<SVal> V = getArgLoc(Caller)) return *V; else break; @@ -455,7 +467,7 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction( } // If we decided not to elide the constructor, proceed as if // it's a simple temporary. - LLVM_FALLTHROUGH; + [[fallthrough]]; } case ConstructionContext::SimpleTemporaryObjectKind: { const auto *TCC = cast<TemporaryObjectConstructionContext>(CC); @@ -524,16 +536,32 @@ bindRequiredArrayElementToEnvironment(ProgramStateRef State, // | `-DeclRefExpr // `-ArrayInitIndexExpr // + // The resulting expression for a multidimensional array. + // ArrayInitLoopExpr <-- we're here + // |-OpaqueValueExpr + // | `-DeclRefExpr <-- match this + // `-ArrayInitLoopExpr + // |-OpaqueValueExpr + // | `-ArraySubscriptExpr + // | |-ImplicitCastExpr + // | | `-OpaqueValueExpr + // | | `-DeclRefExpr + // | `-ArrayInitIndexExpr + // `-CXXConstructExpr <-- extract this + // ` ... + + const auto *OVESrc = AILE->getCommonExpr()->getSourceExpr(); + // HACK: There is no way we can put the index of the array element into the // CFG unless we unroll the loop, so we manually select and bind the required // parameter to the environment. - const auto *CE = cast<CXXConstructExpr>(AILE->getSubExpr()); - const auto *OVESrc = AILE->getCommonExpr()->getSourceExpr(); + const auto *CE = + cast<CXXConstructExpr>(extractElementInitializerFromNestedAILE(AILE)); SVal Base = UnknownVal(); if (const auto *ME = dyn_cast<MemberExpr>(OVESrc)) Base = State->getSVal(ME, LCtx); - else if (const auto *DRE = cast<DeclRefExpr>(OVESrc)) + else if (const auto *DRE = dyn_cast<DeclRefExpr>(OVESrc)) Base = State->getLValue(cast<VarDecl>(DRE->getDecl()), LCtx); else llvm_unreachable("ArrayInitLoopExpr contains unexpected source expression"); @@ -556,18 +584,18 @@ void ExprEngine::handleConstructor(const Expr *E, SVal Target = UnknownVal(); if (CE) { - if (Optional<SVal> ElidedTarget = + if (std::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; + // 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; } } @@ -589,6 +617,27 @@ void ExprEngine::handleConstructor(const Expr *E, unsigned Idx = 0; if (CE->getType()->isArrayType() || AILE) { + + auto isZeroSizeArray = [&] { + uint64_t Size = 1; + + if (const auto *CAT = dyn_cast<ConstantArrayType>(CE->getType())) + Size = getContext().getConstantArrayElementCount(CAT); + else if (AILE) + Size = getContext().getArrayInitLoopExprElementCount(AILE); + + return Size == 0; + }; + + // No element construction will happen in a 0 size array. + if (isZeroSizeArray()) { + StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx); + static SimpleProgramPointTag T{"ExprEngine", + "Skipping 0 size array construction"}; + Bldr.generateNode(CE, Pred, State, &T); + return; + } + Idx = getIndexOfElementToConstruct(State, CE, LCtx).value_or(0u); State = setIndexOfElementToConstruct(State, CE, LCtx, Idx + 1); } @@ -596,16 +645,17 @@ void ExprEngine::handleConstructor(const Expr *E, if (AILE) { // Only set this once even though we loop through it multiple times. if (!getPendingInitLoop(State, CE, LCtx)) - State = setPendingInitLoop(State, CE, LCtx, - AILE->getArraySize().getLimitedValue()); + State = setPendingInitLoop( + State, CE, LCtx, + getContext().getArrayInitLoopExprElementCount(AILE)); State = bindRequiredArrayElementToEnvironment( State, AILE, LCtx, svalBuilder.makeArrayIndex(Idx)); } // The target region is found from construction context. - std::tie(State, Target) = - handleConstructionContext(CE, State, LCtx, CC, CallOpts, Idx); + std::tie(State, Target) = handleConstructionContext( + CE, State, currBldrCtx, LCtx, CC, CallOpts, Idx); break; } case CXXConstructExpr::CK_VirtualBase: { @@ -620,7 +670,7 @@ void ExprEngine::handleConstructor(const Expr *E, ("This virtual base should have already been initialized by " "the most derived class!")); (void)OuterCtor; - LLVM_FALLTHROUGH; + [[fallthrough]]; } case CXXConstructExpr::CK_NonVirtualBase: // In C++17, classes with non-virtual bases may be aggregates, so they would @@ -640,7 +690,7 @@ void ExprEngine::handleConstructor(const Expr *E, CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; break; } - LLVM_FALLTHROUGH; + [[fallthrough]]; case CXXConstructExpr::CK_Delegating: { const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, @@ -905,6 +955,11 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, // skip it for now. ProgramStateRef State = I->getState(); SVal RetVal = State->getSVal(CNE, LCtx); + // [basic.stc.dynamic.allocation] (on the return value of an allocation + // function): + // "The order, contiguity, and initial value of storage allocated by + // successive calls to an allocation function are unspecified." + State = State->bindDefaultInitial(RetVal, UndefinedVal{}, LCtx); // If this allocation function is not declared as non-throwing, failures // /must/ be signalled by exceptions, and thus the return value will never @@ -1143,20 +1198,26 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, SVal InitVal; if (!FieldForCapture->hasCapturedVLAType()) { - Expr *InitExpr = *i; - - if (const auto AILE = dyn_cast<ArrayInitLoopExpr>(InitExpr)) { - // If the AILE initializes a POD array, we need to keep it as the - // InitExpr. - if (dyn_cast<CXXConstructExpr>(AILE->getSubExpr())) - InitExpr = AILE->getSubExpr(); - } + const Expr *InitExpr = *i; assert(InitExpr && "Capture missing initialization expression"); - if (dyn_cast<CXXConstructExpr>(InitExpr)) { - InitVal = *getObjectUnderConstruction(State, {LE, Idx}, LocCtxt); - InitVal = State->getSVal(InitVal.getAsRegion()); + // Capturing a 0 length array is a no-op, so we ignore it to get a more + // accurate analysis. If it's not ignored, it would set the default + // binding of the lambda to 'Unknown', which can lead to falsely detecting + // 'Uninitialized' values as 'Unknown' and not reporting a warning. + const auto FTy = FieldForCapture->getType(); + if (FTy->isConstantArrayType() && + getContext().getConstantArrayElementCount( + getContext().getAsConstantArrayType(FTy)) == 0) + continue; + + // With C++17 copy elision the InitExpr can be anything, so instead of + // pattern matching all cases, we simple check if the current field is + // under construction or not, regardless what it's InitExpr is. + if (const auto OUC = + getObjectUnderConstruction(State, {LE, Idx}, LocCtxt)) { + InitVal = State->getSVal(OUC->getAsRegion()); State = finishObjectConstruction(State, {LE, Idx}, LocCtxt); } else |