[clang] [analyzer] Handle [[assume(cond)]] as __builtin_assume(cond) (PR #116462)
Balazs Benics via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 18 03:27:41 PST 2024
https://github.com/steakhal updated https://github.com/llvm/llvm-project/pull/116462
>From daddb9e13db6ca8373dc7298d17aa36a03014aeb Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <32487576+vinay-deshmukh at users.noreply.github.com>
Date: Fri, 15 Nov 2024 07:37:17 -0500
Subject: [PATCH 1/8] [analyzer] Handle `[[assume(cond)]]` as
`__builtin_assume(cond)`
Resolves #100762
---
.../Core/PathSensitive/ExprEngine.h | 4 ++
clang/lib/Analysis/CFG.cpp | 43 +++++++++++++++++++
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 8 +++-
.../lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 27 ++++++++++++
clang/test/Analysis/out-of-bounds-new.cpp | 16 +++++++
5 files changed, 97 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 8c7493e27fcaa6..078a1d840d0516 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -498,6 +498,10 @@ class ExprEngine {
void VisitInitListExpr(const InitListExpr *E, ExplodedNode *Pred,
ExplodedNodeSet &Dst);
+ /// VisitAttributedStmt - Transfer function logic for AttributedStmt
+ void VisitAttributedStmt(const AttributedStmt *A, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst);
+
/// VisitLogicalExpr - Transfer function logic for '&&', '||'
void VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
ExplodedNodeSet &Dst);
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index f678ac6f2ff36a..fab10f51cf5cfc 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -456,6 +456,36 @@ reverse_children::reverse_children(Stmt *S) {
IE->getNumInits());
return;
}
+ case Stmt::AttributedStmtClass: {
+ AttributedStmt *attrStmt = cast<AttributedStmt>(S);
+ assert(attrStmt);
+
+ {
+ // for an attributed stmt, the "children()" returns only the NullStmt
+ // (;) but semantically the "children" are supposed to be the
+ // expressions _within_ i.e. the two square brackets i.e. [[ HERE ]]
+ // so we add the subexpressions first, _then_ add the "children"
+
+ for (const Attr *attr : attrStmt->getAttrs()) {
+
+ // i.e. one `assume()`
+ CXXAssumeAttr const *assumeAttr = llvm::dyn_cast<CXXAssumeAttr>(attr);
+ if (!assumeAttr) {
+ continue;
+ }
+ // Only handles [[ assume(<assumption>) ]] right now
+ Expr *assumption = assumeAttr->getAssumption();
+ childrenBuf.push_back(assumption);
+ }
+
+ // children() for an AttributedStmt is NullStmt(;)
+ llvm::append_range(childrenBuf, attrStmt->children());
+
+ // This needs to be done *after* childrenBuf has been populated.
+ children = childrenBuf;
+ }
+ return;
+ }
default:
break;
}
@@ -2475,6 +2505,14 @@ static bool isFallthroughStatement(const AttributedStmt *A) {
return isFallthrough;
}
+static bool isCXXAssumeAttr(const AttributedStmt *A) {
+ bool hasAssumeAttr = hasSpecificAttr<CXXAssumeAttr>(A->getAttrs());
+
+ assert((!hasAssumeAttr || isa<NullStmt>(A->getSubStmt())) &&
+ "expected [[assume]] not to have children");
+ return hasAssumeAttr;
+}
+
CFGBlock *CFGBuilder::VisitAttributedStmt(AttributedStmt *A,
AddStmtChoice asc) {
// AttributedStmts for [[likely]] can have arbitrary statements as children,
@@ -2490,6 +2528,11 @@ CFGBlock *CFGBuilder::VisitAttributedStmt(AttributedStmt *A,
appendStmt(Block, A);
}
+ if (isCXXAssumeAttr(A) && asc.alwaysAdd(*this, A)) {
+ autoCreateBlock();
+ appendStmt(Block, A);
+ }
+
return VisitChildren(A);
}
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 22eab9f66418d4..cbc83f1dbda145 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1946,7 +1946,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
// to be explicitly evaluated.
case Stmt::PredefinedExprClass:
case Stmt::AddrLabelExprClass:
- case Stmt::AttributedStmtClass:
case Stmt::IntegerLiteralClass:
case Stmt::FixedPointLiteralClass:
case Stmt::CharacterLiteralClass:
@@ -1977,6 +1976,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
break;
}
+ case Stmt::AttributedStmtClass: {
+ Bldr.takeNodes(Pred);
+ VisitAttributedStmt(cast<AttributedStmt>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+ }
+
case Stmt::CXXDefaultArgExprClass:
case Stmt::CXXDefaultInitExprClass: {
Bldr.takeNodes(Pred);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index f7020da2e6da20..1a211d1adc44f7 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -1200,3 +1200,30 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred,
// FIXME: Move all post/pre visits to ::Visit().
getCheckerManager().runCheckersForPostStmt(Dst, Tmp, LE, *this);
}
+
+void ExprEngine::VisitAttributedStmt(const AttributedStmt *A,
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) {
+
+ ExplodedNodeSet CheckerPreStmt;
+ getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, A, *this);
+
+ ExplodedNodeSet EvalSet;
+ StmtNodeBuilder Bldr(CheckerPreStmt, EvalSet, *currBldrCtx);
+
+ {
+ for (const auto *attr : A->getAttrs()) {
+
+ CXXAssumeAttr const *assumeAttr = llvm::dyn_cast<CXXAssumeAttr>(attr);
+ if (!assumeAttr) {
+ continue;
+ }
+ Expr *AssumeExpr = assumeAttr->getAssumption();
+
+ for (auto *node : CheckerPreStmt) {
+ Visit(AssumeExpr, node, EvalSet);
+ }
+ }
+ }
+
+ getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, A, *this);
+}
diff --git a/clang/test/Analysis/out-of-bounds-new.cpp b/clang/test/Analysis/out-of-bounds-new.cpp
index f541bdf810d79c..4db351b10055b1 100644
--- a/clang/test/Analysis/out-of-bounds-new.cpp
+++ b/clang/test/Analysis/out-of-bounds-new.cpp
@@ -180,3 +180,19 @@ int test_reference_that_might_be_after_the_end(int idx) {
return ref;
}
+// From: https://github.com/llvm/llvm-project/issues/100762
+extern int arr[10];
+void using_builtin(int x) {
+ __builtin_assume(x > 101); // CallExpr
+ arr[x] = 404; // expected-warning{{Out of bound access to memory}}
+}
+
+void using_assume_attr(int ax) {
+ [[assume(ax > 100)]]; // NullStmt with an attribute
+ arr[ax] = 405; // expected-warning{{Out of bound access to memory}}
+}
+
+void using_many_assume_attr(int yx) {
+ [[assume(yx > 104), assume(yx > 200), assume(yx < 300)]]; // NullStmt with an attribute
+ arr[yx] = 406; // expected-warning{{Out of bound access to memory}}
+}
>From 7f1e341e59a52017d9f73ab09e1be45444536731 Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Mon, 18 Nov 2024 11:33:42 +0100
Subject: [PATCH 2/8] NFC Simplify ExprEngine::VisitAttributedStmt
---
clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 16 +++-------------
1 file changed, 3 insertions(+), 13 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 1a211d1adc44f7..86d57a8dee52ea 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -1203,25 +1203,15 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred,
void ExprEngine::VisitAttributedStmt(const AttributedStmt *A,
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
-
ExplodedNodeSet CheckerPreStmt;
getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, A, *this);
ExplodedNodeSet EvalSet;
StmtNodeBuilder Bldr(CheckerPreStmt, EvalSet, *currBldrCtx);
- {
- for (const auto *attr : A->getAttrs()) {
-
- CXXAssumeAttr const *assumeAttr = llvm::dyn_cast<CXXAssumeAttr>(attr);
- if (!assumeAttr) {
- continue;
- }
- Expr *AssumeExpr = assumeAttr->getAssumption();
-
- for (auto *node : CheckerPreStmt) {
- Visit(AssumeExpr, node, EvalSet);
- }
+ if (const auto *AssumeAttr = getSpecificAttr<CXXAssumeAttr>(A->getAttrs())) {
+ for (ExplodedNode *N : CheckerPreStmt) {
+ Visit(AssumeAttr->getAssumption(), N, EvalSet);
}
}
>From f4307e9a8b8d8390477a8951adda665b0dc35ba7 Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Mon, 18 Nov 2024 11:34:25 +0100
Subject: [PATCH 3/8] NFC Simplify AttributedStmt handling in clang CFG
---
clang/lib/Analysis/CFG.cpp | 32 +++++++-------------------------
1 file changed, 7 insertions(+), 25 deletions(-)
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index fab10f51cf5cfc..dbdb08506bb622 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -457,34 +457,16 @@ reverse_children::reverse_children(Stmt *S) {
return;
}
case Stmt::AttributedStmtClass: {
- AttributedStmt *attrStmt = cast<AttributedStmt>(S);
- assert(attrStmt);
+ auto Attrs = cast<AttributedStmt>(S)->getAttrs();
- {
- // for an attributed stmt, the "children()" returns only the NullStmt
- // (;) but semantically the "children" are supposed to be the
- // expressions _within_ i.e. the two square brackets i.e. [[ HERE ]]
- // so we add the subexpressions first, _then_ add the "children"
-
- for (const Attr *attr : attrStmt->getAttrs()) {
-
- // i.e. one `assume()`
- CXXAssumeAttr const *assumeAttr = llvm::dyn_cast<CXXAssumeAttr>(attr);
- if (!assumeAttr) {
- continue;
- }
- // Only handles [[ assume(<assumption>) ]] right now
- Expr *assumption = assumeAttr->getAssumption();
- childrenBuf.push_back(assumption);
- }
-
- // children() for an AttributedStmt is NullStmt(;)
- llvm::append_range(childrenBuf, attrStmt->children());
-
- // This needs to be done *after* childrenBuf has been populated.
+ // We only handle `[[assume(...)]]` attributes for now.
+ if (const auto *A = getSpecificAttr<CXXAssumeAttr>(Attrs)) {
+ childrenBuf.push_back(A->getAssumption());
+ llvm::append_range(childrenBuf, S->children());
children = childrenBuf;
+ return;
}
- return;
+ break;
}
default:
break;
>From c2b5e71c2fbf0b2240914da068f1afe4dec790cc Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Mon, 18 Nov 2024 11:35:32 +0100
Subject: [PATCH 4/8] NFC Clarify test comment
---
clang/test/Analysis/out-of-bounds-new.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/Analysis/out-of-bounds-new.cpp b/clang/test/Analysis/out-of-bounds-new.cpp
index 4db351b10055b1..c2b70580a90c33 100644
--- a/clang/test/Analysis/out-of-bounds-new.cpp
+++ b/clang/test/Analysis/out-of-bounds-new.cpp
@@ -188,7 +188,7 @@ void using_builtin(int x) {
}
void using_assume_attr(int ax) {
- [[assume(ax > 100)]]; // NullStmt with an attribute
+ [[assume(ax > 100)]]; // NullStmt with an "assume" attribute.
arr[ax] = 405; // expected-warning{{Out of bound access to memory}}
}
>From 3d4953f8f2a9cc5cf8d47e9c728116911d5421ca Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Mon, 18 Nov 2024 11:35:51 +0100
Subject: [PATCH 5/8] NFC De-indent some tests
---
clang/test/Analysis/out-of-bounds-new.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/clang/test/Analysis/out-of-bounds-new.cpp b/clang/test/Analysis/out-of-bounds-new.cpp
index c2b70580a90c33..4896893a6a231b 100644
--- a/clang/test/Analysis/out-of-bounds-new.cpp
+++ b/clang/test/Analysis/out-of-bounds-new.cpp
@@ -183,16 +183,16 @@ int test_reference_that_might_be_after_the_end(int idx) {
// From: https://github.com/llvm/llvm-project/issues/100762
extern int arr[10];
void using_builtin(int x) {
- __builtin_assume(x > 101); // CallExpr
- arr[x] = 404; // expected-warning{{Out of bound access to memory}}
+ __builtin_assume(x > 101); // CallExpr
+ arr[x] = 404; // expected-warning{{Out of bound access to memory}}
}
void using_assume_attr(int ax) {
- [[assume(ax > 100)]]; // NullStmt with an "assume" attribute.
- arr[ax] = 405; // expected-warning{{Out of bound access to memory}}
+ [[assume(ax > 100)]]; // NullStmt with an "assume" attribute.
+ arr[ax] = 405; // expected-warning{{Out of bound access to memory}}
}
void using_many_assume_attr(int yx) {
- [[assume(yx > 104), assume(yx > 200), assume(yx < 300)]]; // NullStmt with an attribute
- arr[yx] = 406; // expected-warning{{Out of bound access to memory}}
+ [[assume(yx > 104), assume(yx > 200), assume(yx < 300)]]; // NullStmt with an attribute
+ arr[yx] = 406; // expected-warning{{Out of bound access to memory}}
}
>From 57751d1bcaa1517033abf0da4f33bc06ffe1e8a7 Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Mon, 18 Nov 2024 11:36:40 +0100
Subject: [PATCH 6/8] NFC Mention the size of the array in its name
---
clang/test/Analysis/out-of-bounds-new.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/test/Analysis/out-of-bounds-new.cpp b/clang/test/Analysis/out-of-bounds-new.cpp
index 4896893a6a231b..c4abc70a2d953d 100644
--- a/clang/test/Analysis/out-of-bounds-new.cpp
+++ b/clang/test/Analysis/out-of-bounds-new.cpp
@@ -181,18 +181,18 @@ int test_reference_that_might_be_after_the_end(int idx) {
}
// From: https://github.com/llvm/llvm-project/issues/100762
-extern int arr[10];
+extern int arrOf10[10];
void using_builtin(int x) {
__builtin_assume(x > 101); // CallExpr
- arr[x] = 404; // expected-warning{{Out of bound access to memory}}
+ arrOf10[x] = 404; // expected-warning{{Out of bound access to memory}}
}
void using_assume_attr(int ax) {
[[assume(ax > 100)]]; // NullStmt with an "assume" attribute.
- arr[ax] = 405; // expected-warning{{Out of bound access to memory}}
+ arrOf10[ax] = 405; // expected-warning{{Out of bound access to memory}}
}
void using_many_assume_attr(int yx) {
[[assume(yx > 104), assume(yx > 200), assume(yx < 300)]]; // NullStmt with an attribute
- arr[yx] = 406; // expected-warning{{Out of bound access to memory}}
+ arrOf10[yx] = 406; // expected-warning{{Out of bound access to memory}}
}
>From 75e964a4d45cfb0adf396f8b870ffa3bb3d65e35 Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Mon, 18 Nov 2024 12:25:06 +0100
Subject: [PATCH 7/8] Add broken test about sideeffects
---
clang/test/Analysis/out-of-bounds-new.cpp | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/clang/test/Analysis/out-of-bounds-new.cpp b/clang/test/Analysis/out-of-bounds-new.cpp
index c4abc70a2d953d..200ee11d714421 100644
--- a/clang/test/Analysis/out-of-bounds-new.cpp
+++ b/clang/test/Analysis/out-of-bounds-new.cpp
@@ -1,4 +1,9 @@
-// RUN: %clang_analyze_cc1 -std=c++11 -Wno-array-bounds -analyzer-checker=unix,core,alpha.security.ArrayBoundV2 -verify %s
+// RUN: %clang_analyze_cc1 -std=c++11 -Wno-array-bounds -verify %s \
+// RUN: -analyzer-checker=unix,core,alpha.security.ArrayBoundV2,debug.ExprInspection
+
+template <typename T> void clang_analyzer_dump(T);
+template <typename T> void clang_analyzer_value(T);
+void clang_analyzer_eval(bool);
// Tests doing an out-of-bounds access after the end of an array using:
// - constant integer index
@@ -194,5 +199,12 @@ void using_assume_attr(int ax) {
void using_many_assume_attr(int yx) {
[[assume(yx > 104), assume(yx > 200), assume(yx < 300)]]; // NullStmt with an attribute
- arrOf10[yx] = 406; // expected-warning{{Out of bound access to memory}}
+ arrOf10[yx] = 406; // expected-warning{{Out of bounsssd access to memory}}
+}
+
+int using_assume_attr_has_no_sideeffects(int y) {
+ // We should not apply sideeffects of the argument of [[assume(...)]].
+ [[assume(++y == 43)]]; // "y" should not get incremented
+ clang_analyzer_eval(y == 42); // expected-warning {{TRUE}} expected-warning {{FALSE}} FIXME: This should be only TRUE.
+ return y;
}
>From d55851b5af8967afb2edad2361f31081e8bd950f Mon Sep 17 00:00:00 2001
From: Balazs Benics <benicsbalazs at gmail.com>
Date: Mon, 18 Nov 2024 12:26:26 +0100
Subject: [PATCH 8/8] [clang] Generalize getSpecificAttr for const attributes
I'll upstream this commit separately to make getSpecificAttr work with
const attributes.
---
clang/include/clang/AST/AttrIterator.h | 16 +++++++++-------
clang/lib/CodeGen/CGLoopInfo.cpp | 2 +-
2 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/clang/include/clang/AST/AttrIterator.h b/clang/include/clang/AST/AttrIterator.h
index 66571e1cf0b8ec..7e2bb0381d4c8f 100644
--- a/clang/include/clang/AST/AttrIterator.h
+++ b/clang/include/clang/AST/AttrIterator.h
@@ -14,11 +14,13 @@
#define LLVM_CLANG_AST_ATTRITERATOR_H
#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/ADL.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
#include <cassert>
#include <cstddef>
#include <iterator>
+#include <type_traits>
namespace clang {
@@ -113,13 +115,13 @@ inline bool hasSpecificAttr(const Container& container) {
specific_attr_end<SpecificAttr>(container);
}
template <typename SpecificAttr, typename Container>
-inline SpecificAttr *getSpecificAttr(const Container& container) {
- specific_attr_iterator<SpecificAttr, Container> i =
- specific_attr_begin<SpecificAttr>(container);
- if (i != specific_attr_end<SpecificAttr>(container))
- return *i;
- else
- return nullptr;
+inline auto *getSpecificAttr(const Container &container) {
+ using ValueTy = llvm::detail::ValueOfRange<Container>;
+ using ValuePointeeTy = std::remove_pointer_t<ValueTy>;
+ using IterTy = std::conditional_t<std::is_const_v<ValuePointeeTy>,
+ const SpecificAttr, SpecificAttr>;
+ auto It = specific_attr_begin<IterTy>(container);
+ return It != specific_attr_end<IterTy>(container) ? *It : nullptr;
}
} // namespace clang
diff --git a/clang/lib/CodeGen/CGLoopInfo.cpp b/clang/lib/CodeGen/CGLoopInfo.cpp
index cdff7e50c4ee71..448571221ef81b 100644
--- a/clang/lib/CodeGen/CGLoopInfo.cpp
+++ b/clang/lib/CodeGen/CGLoopInfo.cpp
@@ -811,7 +811,7 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
// Identify loop attribute 'code_align' from Attrs.
// For attribute code_align:
// n - 'llvm.loop.align i32 n' metadata will be emitted.
- if (const auto *CodeAlign = getSpecificAttr<const CodeAlignAttr>(Attrs)) {
+ if (const auto *CodeAlign = getSpecificAttr<CodeAlignAttr>(Attrs)) {
const auto *CE = cast<ConstantExpr>(CodeAlign->getAlignment());
llvm::APSInt ArgVal = CE->getResultAsAPSInt();
setCodeAlign(ArgVal.getSExtValue());
More information about the cfe-commits
mailing list