Skip to content

Commit

Permalink
[clang][analyzer] Fix crash in loop unrolling (llvm#82089)
Browse files Browse the repository at this point in the history
StaticAnalyzer didn't check if the variable is declared in
`CompoundStmt` under `SwitchStmt`, which make static analyzer reach root
without finding the declaration.

Fixes llvm#68819

---------

Co-authored-by: Balazs Benics <[email protected]>
  • Loading branch information
huang-me and steakhal authored Mar 14, 2024
1 parent 2582965 commit 8f68022
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 5 deletions.
4 changes: 4 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,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;
}
}
}

0 comments on commit 8f68022

Please sign in to comment.