Skip to content

[clang][analyzer] Fix crash in loop unrolling #82089

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Mar 14, 2024
4 changes: 4 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,10 @@ libclang
Static Analyzer
---------------

- Fixed crashing on loops if the loop variable was declared in switch blocks
but not under any case blocks if ``unroll-loops=true`` analyzer config is
set. (#GH68819)

New features
^^^^^^^^^^^^

Expand Down
27 changes: 22 additions & 5 deletions clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,17 @@ static bool isCapturedByReference(ExplodedNode *N, const DeclRefExpr *DR) {
return FD->getType()->isReferenceType();
}

static bool isFoundInStmt(const Stmt *S, const VarDecl *VD) {
if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
for (const Decl *D : DS->decls()) {
// Once we reach the declaration of the VD we can return.
if (D->getCanonicalDecl() == VD)
return true;
}
}
return false;
}

// A loop counter is considered escaped if:
// case 1: It is a global variable.
// case 2: It is a reference parameter or a reference capture.
Expand Down Expand Up @@ -219,13 +230,19 @@ static bool isPossiblyEscaped(ExplodedNode *N, const DeclRefExpr *DR) {
continue;
}

if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
for (const Decl *D : DS->decls()) {
// Once we reach the declaration of the VD we can return.
if (D->getCanonicalDecl() == VD)
return false;
if (isFoundInStmt(S, VD)) {
return false;
}

if (const auto *SS = dyn_cast<SwitchStmt>(S)) {
if (const auto *CST = dyn_cast<CompoundStmt>(SS->getBody())) {
for (const Stmt *CB : CST->body()) {
if (isFoundInStmt(CB, VD))
return false;
}
}
}

// Check the usage of the pass-by-ref function calls and adress-of operator
// on VD and reference initialized by VD.
ASTContext &ASTCtx =
Expand Down
12 changes: 12 additions & 0 deletions clang/test/Analysis/loop-unrolling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -547,3 +547,15 @@ void capture_implicitly_by_ref_as_loop_counter() {
}
};
}


void test_escaping_on_var_before_switch_case_no_crash(int c) {
// https://github.com/llvm/llvm-project/issues/68819
switch (c) {
int i; // no-crash: The declaration of `i` is found here.
case 0: {
for (i = 0; i < 16; i++) {}
break;
}
}
}