Skip to content

Commit

Permalink
[analyzer] Fix a crash from element region construction during `Array…
Browse files Browse the repository at this point in the history
…InitLoopExpr` analysis

This patch generalizes the way element regions are constructed when an `ArrayInitLoopExpr`
is being analyzed. Previously the base region of the `ElementRegion` was determined with
pattern matching, which led to crashes, when an unhandled pattern was encountered.

Fixes llvm#112813
  • Loading branch information
isuckatcs committed Oct 24, 2024
1 parent cd0373e commit a5fbcd5
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 57 deletions.
69 changes: 12 additions & 57 deletions clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -513,70 +513,25 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction(
static ProgramStateRef
bindRequiredArrayElementToEnvironment(ProgramStateRef State,
const ArrayInitLoopExpr *AILE,
const LocationContext *LCtx, SVal Idx) {
// The ctor in this case is guaranteed to be a copy ctor, otherwise we hit a
// compile time error.
//
// -ArrayInitLoopExpr <-- we're here
// |-OpaqueValueExpr
// | `-DeclRefExpr <-- match this
// `-CXXConstructExpr
// `-ImplicitCastExpr
// `-ArraySubscriptExpr
// |-ImplicitCastExpr
// | `-OpaqueValueExpr
// | `-DeclRefExpr
// `-ArrayInitIndexExpr
//
// The resulting expression might look like the one below in an implicit
// copy/move ctor.
//
// ArrayInitLoopExpr <-- we're here
// |-OpaqueValueExpr
// | `-MemberExpr <-- match this
// | (`-CXXStaticCastExpr) <-- move ctor only
// | `-DeclRefExpr
// `-CXXConstructExpr
// `-ArraySubscriptExpr
// |-ImplicitCastExpr
// | `-OpaqueValueExpr
// | `-MemberExpr
// | `-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();
const LocationContext *LCtx, NonLoc Idx) {
SValBuilder &SVB = State->getStateManager().getSValBuilder();
MemRegionManager &MRMgr = SVB.getRegionManager();
ASTContext &Ctx = SVB.getContext();

// 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 =
const Expr *SourceArray = AILE->getCommonExpr()->getSourceExpr();
const auto *Ctor =
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 = dyn_cast<DeclRefExpr>(OVESrc))
Base = State->getLValue(cast<VarDecl>(DRE->getDecl()), LCtx);
else
llvm_unreachable("ArrayInitLoopExpr contains unexpected source expression");

SVal NthElem = State->getLValue(CE->getType(), Idx, Base);
const SubRegion *SourceArrayRegion =
cast<SubRegion>(State->getSVal(SourceArray, LCtx).getAsRegion());
const ElementRegion *ElementRegion =
MRMgr.getElementRegion(Ctor->getType(), Idx, SourceArrayRegion, Ctx);

return State->BindExpr(CE->getArg(0), LCtx, NthElem);
return State->BindExpr(Ctor->getArg(0), LCtx,
loc::MemRegionVal(ElementRegion));
}

void ExprEngine::handleConstructor(const Expr *E,
Expand Down
44 changes: 44 additions & 0 deletions clang/test/Analysis/array-init-loop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,3 +330,47 @@ void no_crash() {
}

} // namespace crash

namespace array_subscript_initializer {

struct S
{
int x;
};

void no_crash() {
S arr[][2] = {{1, 2}};

const auto [a, b] = arr[0];

clang_analyzer_eval(a.x == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(b.x == 2); // expected-warning{{TRUE}}
}

} // namespace array_subscript_initializer

namespace iterator_initializer {

struct S
{
int x;
};

void no_crash() {
S arr[][2] = {{1, 2}, {3, 4}};

int i = 0;
for (const auto [a, b] : arr) {
if (i == 0) {
clang_analyzer_eval(a.x == 1); // expected-warning{{TRUE}}
clang_analyzer_eval(b.x == 2); // expected-warning{{TRUE}}
} else {
clang_analyzer_eval(a.x == 3); // expected-warning{{TRUE}}
clang_analyzer_eval(b.x == 4); // expected-warning{{TRUE}}
}

++i;
}
}

} // namespace iterator_initializer

0 comments on commit a5fbcd5

Please sign in to comment.