[clang] [analyzer] Handle [[assume(cond)]] as __builtin_assume(cond) (PR #116462)
Vinay Deshmukh via cfe-commits
cfe-commits at lists.llvm.org
Tue Dec 17 06:14:46 PST 2024
https://github.com/vinay-deshmukh 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 01/11] [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 02/11] 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 03/11] 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 04/11] 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 05/11] 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 06/11] 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 07/11] 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 08/11] [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());
>From c288bdc428a85262500a929ea787dc186a44ca10 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <32487576+vinay-deshmukh at users.noreply.github.com>
Date: Thu, 28 Nov 2024 22:51:46 -0500
Subject: [PATCH 09/11] Ignore assumptions with side-effects
---
clang/lib/Analysis/CFG.cpp | 45 ++++++++++++++-----
.../lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 16 ++++++-
clang/test/Analysis/out-of-bounds-new.cpp | 42 +++++++++++++++--
3 files changed, 86 insertions(+), 17 deletions(-)
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index dbdb08506bb622..8d1db015907eba 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -431,9 +431,10 @@ namespace {
class reverse_children {
llvm::SmallVector<Stmt *, 12> childrenBuf;
ArrayRef<Stmt *> children;
+ ASTContext *astContext;
public:
- reverse_children(Stmt *S);
+ reverse_children(Stmt *S, ASTContext *astContext = nullptr);
using iterator = ArrayRef<Stmt *>::reverse_iterator;
@@ -443,7 +444,8 @@ class reverse_children {
} // namespace
-reverse_children::reverse_children(Stmt *S) {
+reverse_children::reverse_children(Stmt *S, ASTContext *AstC)
+ : astContext(AstC) {
if (CallExpr *CE = dyn_cast<CallExpr>(S)) {
children = CE->getRawSubExprs();
return;
@@ -457,16 +459,37 @@ reverse_children::reverse_children(Stmt *S) {
return;
}
case Stmt::AttributedStmtClass: {
- auto Attrs = cast<AttributedStmt>(S)->getAttrs();
+ assert(S->getStmtClass() == Stmt::AttributedStmtClass);
+ assert(this->astContext &&
+ "Attributes need the ast context to determine side-effects");
+ AttributedStmt *AS = 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 : AS->getAttrs()) {
+ // Only handles [[ assume(<assumption>) ]] right now
+ CXXAssumeAttr const *AssumeAttr = llvm::dyn_cast<CXXAssumeAttr>(Attr);
+ if (!AssumeAttr) {
+ continue;
+ }
+ Expr *AssumeExpr = AssumeAttr->getAssumption();
+ // If we skip adding the assumption expression to CFG,
+ // it doesn't get "branch"-ed by symbol analysis engine
+ // presumably because it's literally not in the CFG
- // 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;
+ if (AssumeExpr->HasSideEffects(*astContext)) {
+ continue;
+ }
+ childrenBuf.push_back(AssumeExpr);
}
- break;
+ // children() for an CXXAssumeAttr is NullStmt(;)
+ // for others, it will have existing behavior
+ llvm::append_range(childrenBuf, AS->children());
+ children = childrenBuf;
+ return;
}
default:
break;
@@ -2436,7 +2459,7 @@ CFGBlock *CFGBuilder::VisitChildren(Stmt *S) {
// Visit the children in their reverse order so that they appear in
// left-to-right (natural) order in the CFG.
- reverse_children RChildren(S);
+ reverse_children RChildren(S, Context);
for (Stmt *Child : RChildren) {
if (Child)
if (CFGBlock *R = Visit(Child))
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 86d57a8dee52ea..786820c61c41c6 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -10,16 +10,23 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/StmtCXX.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/ConstructionContext.h"
+#include "clang/Analysis/ProgramPoint.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/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Sequence.h"
#include <optional>
@@ -1209,9 +1216,14 @@ void ExprEngine::VisitAttributedStmt(const AttributedStmt *A,
ExplodedNodeSet EvalSet;
StmtNodeBuilder Bldr(CheckerPreStmt, EvalSet, *currBldrCtx);
- if (const auto *AssumeAttr = getSpecificAttr<CXXAssumeAttr>(A->getAttrs())) {
+ for (const auto *attr : A->getAttrs()) {
+ CXXAssumeAttr const *AssumeAttr = llvm::dyn_cast<CXXAssumeAttr>(attr);
+ if (!AssumeAttr) {
+ continue;
+ }
+ Expr *AssumeExpr = AssumeAttr->getAssumption();
for (ExplodedNode *N : CheckerPreStmt) {
- Visit(AssumeAttr->getAssumption(), N, EvalSet);
+ Visit(AssumeExpr, N, EvalSet);
}
}
diff --git a/clang/test/Analysis/out-of-bounds-new.cpp b/clang/test/Analysis/out-of-bounds-new.cpp
index 200ee11d714421..39a40eb10bea7d 100644
--- a/clang/test/Analysis/out-of-bounds-new.cpp
+++ b/clang/test/Analysis/out-of-bounds-new.cpp
@@ -4,6 +4,8 @@
template <typename T> void clang_analyzer_dump(T);
template <typename T> void clang_analyzer_value(T);
void clang_analyzer_eval(bool);
+template <typename T>
+void clang_analyzer_explain(T);
// Tests doing an out-of-bounds access after the end of an array using:
// - constant integer index
@@ -189,22 +191,54 @@ int test_reference_that_might_be_after_the_end(int idx) {
extern int arrOf10[10];
void using_builtin(int x) {
__builtin_assume(x > 101); // CallExpr
- arrOf10[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.
- arrOf10[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
- arrOf10[yx] = 406; // expected-warning{{Out of bounsssd access to memory}}
+ arrOf10[yx] = 406; // expected-warning{{Out of bound access to memory}}
}
+
+int using_builtin_assume_has_no_sideeffects(int y) {
+ // We should not apply sideeffects of the argument of [[assume(...)]].
+ // "y" should not get incremented;
+ __builtin_assume(++y == 43); // expected-warning {{assumption is ignored because it contains (potential) side-effects}}
+ clang_analyzer_eval(y == 42); // expected-warning {{FALSE}}
+ return y;
+}
+
+
+
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
+ // "y" should not get incremented;
+ [[assume(++y == 43)]]; // expected-warning {{assumption is ignored because it contains (potential) side-effects}}
+
clang_analyzer_eval(y == 42); // expected-warning {{TRUE}} expected-warning {{FALSE}} FIXME: This should be only TRUE.
+
+ clang_analyzer_eval(y == 43); // expected-warning {{FALSE}} expected-warning {{TRUE}} FIXME: This should be only FALSE.
+
return y;
}
+
+
+int using_builtinassume_has_no_sideeffects(int u) {
+ // We should not apply sideeffects of the argument of __builtin_assume(...)
+ // "u" should not get incremented;
+ __builtin_assume(++u == 43); // expected-warning {{assumption is ignored because it contains (potential) side-effects}}
+
+ // FIXME: evaluate this to true
+ clang_analyzer_eval(u == 42); // expected-warning {{FALSE}} current behavior
+
+ // FIXME: evaluate this to false
+ clang_analyzer_eval(u == 43); // expected-warning {{TRUE}} current behavior
+
+ return u;
+}
>From eabbef2be6a4f956171a21632d8cf07b4a48e162 Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <32487576+vinay-deshmukh at users.noreply.github.com>
Date: Tue, 10 Dec 2024 16:45:20 +0530
Subject: [PATCH 10/11] Add ternary test for assumption
Also, remove unnecessary member for `reverse_children`
---
clang/lib/Analysis/CFG.cpp | 8 ++-
clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 23 ++++++++-
.../lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 9 +++-
.../test/Analysis/cxx23-assume-attribute.cpp | 49 +++++++++++++++++++
4 files changed, 81 insertions(+), 8 deletions(-)
create mode 100644 clang/test/Analysis/cxx23-assume-attribute.cpp
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index 891c542aa55531..fb411043a8b1f5 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -431,7 +431,6 @@ namespace {
class reverse_children {
llvm::SmallVector<Stmt *, 12> childrenBuf;
ArrayRef<Stmt *> children;
- ASTContext *astContext;
public:
reverse_children(Stmt *S, ASTContext *astContext = nullptr);
@@ -444,8 +443,7 @@ class reverse_children {
} // namespace
-reverse_children::reverse_children(Stmt *S, ASTContext *AstC)
- : astContext(AstC) {
+reverse_children::reverse_children(Stmt *S, ASTContext *AstC) {
if (CallExpr *CE = dyn_cast<CallExpr>(S)) {
children = CE->getRawSubExprs();
return;
@@ -460,7 +458,7 @@ reverse_children::reverse_children(Stmt *S, ASTContext *AstC)
}
case Stmt::AttributedStmtClass: {
assert(S->getStmtClass() == Stmt::AttributedStmtClass);
- assert(this->astContext &&
+ assert(AstC &&
"Attributes need the ast context to determine side-effects");
AttributedStmt *AS = cast<AttributedStmt>(S);
assert(attrStmt);
@@ -480,7 +478,7 @@ reverse_children::reverse_children(Stmt *S, ASTContext *AstC)
// it doesn't get "branch"-ed by symbol analysis engine
// presumably because it's literally not in the CFG
- if (AssumeExpr->HasSideEffects(*astContext)) {
+ if (AssumeExpr->HasSideEffects(*AstC)) {
continue;
}
childrenBuf.push_back(AssumeExpr);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 7a900780384a91..046d33164070bc 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -785,7 +785,8 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex,
const Expr *R,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
- assert(L && R);
+ assert(L && "L does not exist!");
+ assert(R && "R does not exist!");
StmtNodeBuilder B(Pred, Dst, *currBldrCtx);
ProgramStateRef state = Pred->getState();
@@ -794,9 +795,10 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex,
// Find the predecessor block.
ProgramStateRef SrcState = state;
+
for (const ExplodedNode *N = Pred ; N ; N = *N->pred_begin()) {
ProgramPoint PP = N->getLocation();
- if (PP.getAs<PreStmtPurgeDeadSymbols>() || PP.getAs<BlockEntrance>()) {
+ if (PP.getAs<PreStmtPurgeDeadSymbols>() || PP.getAs<BlockEntrance>() || PP.getAs<StmtPoint>()) {
// If the state N has multiple predecessors P, it means that successors
// of P are all equivalent.
// In turn, that means that all nodes at P are equivalent in terms
@@ -804,6 +806,7 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex,
// FIXME: a more robust solution which does not walk up the tree.
continue;
}
+ assert(PP.getAs<BlockEdge>());
SrcBlock = PP.castAs<BlockEdge>().getSrc();
SrcState = N->getState();
break;
@@ -816,6 +819,22 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex,
bool hasValue = false;
SVal V;
+ llvm::errs() << "\nSrcBlock"; SrcBlock->dump();
+ llvm::errs() << "\nempty=" << SrcBlock->empty();
+ llvm::errs() << "\nsize=" << SrcBlock->size();
+ CFGBlock::const_iterator bb = SrcBlock->begin();
+ llvm::errs() << "\nbegin=|" ; bb->dump(); llvm::errs() << "|";
+ CFGElement const& xx = *bb;
+ llvm::errs() << "\nbegin-deref=|" ; xx.dump();llvm::errs() << "|";
+ // manual iteration
+ for(auto it = SrcBlock->begin(); it != SrcBlock->end() ; ++it) {
+ llvm::errs() << "\nit.kind=|" << it->getKind() << "|";
+ }
+
+ llvm::errs() << "\nend=" ; (SrcBlock->end()-1)->dump();
+ llvm::errs() << "\nrbegin=" << SrcBlock->rbegin();
+ llvm::errs() << "\nrend=" << SrcBlock->rend();
+
for (CFGElement CE : llvm::reverse(*SrcBlock)) {
if (std::optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {
const Expr *ValEx = cast<Expr>(CS->getStmt());
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 786820c61c41c6..88a96f422f846f 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -1223,7 +1223,14 @@ void ExprEngine::VisitAttributedStmt(const AttributedStmt *A,
}
Expr *AssumeExpr = AssumeAttr->getAssumption();
for (ExplodedNode *N : CheckerPreStmt) {
- Visit(AssumeExpr, N, EvalSet);
+ // Assume State to narrow down here?
+ // for now
+ // ?????? VINAY HERE, need to narrow it down here
+ ExplodedNode CloneWithAssume = *N;
+ auto stateWithAssume = CloneWithAssume.getState();
+ Bldr.generateNode(AssumeExpr, N, stateWithAssume);
+
+ Visit(AssumeExpr, &CloneWithAssume, EvalSet);
}
}
diff --git a/clang/test/Analysis/cxx23-assume-attribute.cpp b/clang/test/Analysis/cxx23-assume-attribute.cpp
new file mode 100644
index 00000000000000..c68a77aef42d84
--- /dev/null
+++ b/clang/test/Analysis/cxx23-assume-attribute.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_analyze_cc1 -std=c++11 -Wno-array-bounds -verify %s \
+// RUN: -analyzer-checker=unix,core,alpha.security.ArrayBoundV2,debug.ExprInspection
+
+// TODO: fix the checker arguments
+
+template <typename T> void clang_analyzer_dump(T);
+template <typename T> void clang_analyzer_value(T);
+
+int ternary_in_builtin_assume(int a, int b) {
+
+ __builtin_assume(a > 10 ? b == 4 : b == 10);
+
+ clang_analyzer_value(a); // we should have 2 dumps here. expected-warning{{[-2147483648, 10]}} expected-warning{{[11, 2147483647]}}
+ clang_analyzer_dump(b); // This should be either 4 or 10. expected-warning{{4}} expected-warning{{10}}
+ if (a > 20) {
+ clang_analyzer_dump(b + 100); // expecting 104 expected-warning{{104}}
+ return 2;
+ }
+ if (a > 10) {
+ clang_analyzer_dump(b + 200); // expecting 204 expected-warning{{204}}
+ return 1;
+ }
+ clang_analyzer_dump(b + 300); // expecting 310 expected-warning{{310}}
+ return 0;
+}
+
+// From: https://github.com/llvm/llvm-project/pull/116462#issuecomment-2517853226
+int ternary_in_assume(int a, int b) {
+ // FIXME notes
+ // Currently, if this test is run without the core.builtin.Builtin checker, the above function with the __builtin_assume behaves identically to the following test
+ // i.e. calls to `clang_analyzer_dump` result in "extraneous" prints of the SVal(s) `reg_$2<int > b ...`
+ // as opposed to 4 or 10
+ // which likely implies the Program State(s) did not get narrowed.
+ // A new checker is likely needed to be implemented to properly handle the expressions within `[[assume]]` to eliminate the states where `b` is not narrowed.
+
+ [[assume(a > 10 ? b == 4 : b == 10)]];
+ clang_analyzer_value(a); // we should have 2 dumps here. expected-warning{{[-2147483648, 10]}} expected-warning{{[11, 2147483647]}}
+ clang_analyzer_dump(b); // This should be either 4 or 10. expected-warning{{4}} expected-warning{{10}} FIXME: expected-warning{{reg_$2<int b>}}
+ if (a > 20) {
+ clang_analyzer_dump(b + 100); // expecting 104 expected-warning{{104}} FIXME: expected-warning{{(reg_$2<int b>) + 100}}
+ return 2;
+ }
+ if (a > 10) {
+ clang_analyzer_dump(b + 200); // expecting 204 expected-warning{{204}} FIXME: expected-warning{{(reg_$2<int b>) + 200}}
+ return 1;
+ }
+ clang_analyzer_dump(b + 300); // expecting 310 expected-warning{{310}} FIXME: expected-warning{{(reg_$2<int b>) + 300}}
+ return 0;
+}
>From c3d1c1a3c84521aefb9e2b0dfd2a7feabe45a62e Mon Sep 17 00:00:00 2001
From: Vinay Deshmukh <32487576+vinay-deshmukh at users.noreply.github.com>
Date: Tue, 17 Dec 2024 16:35:04 +0530
Subject: [PATCH 11/11] clean
---
clang/lib/Analysis/CFG.cpp | 2 +-
clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 16 ----------------
clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 9 +--------
3 files changed, 2 insertions(+), 25 deletions(-)
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index fb411043a8b1f5..569e063e301689 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -461,7 +461,7 @@ reverse_children::reverse_children(Stmt *S, ASTContext *AstC) {
assert(AstC &&
"Attributes need the ast context to determine side-effects");
AttributedStmt *AS = cast<AttributedStmt>(S);
- assert(attrStmt);
+ assert(AS);
// for an attributed stmt, the "children()" returns only the NullStmt
// (;) but semantically the "children" are supposed to be the
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 046d33164070bc..8848a8f1e16897 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -819,22 +819,6 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex,
bool hasValue = false;
SVal V;
- llvm::errs() << "\nSrcBlock"; SrcBlock->dump();
- llvm::errs() << "\nempty=" << SrcBlock->empty();
- llvm::errs() << "\nsize=" << SrcBlock->size();
- CFGBlock::const_iterator bb = SrcBlock->begin();
- llvm::errs() << "\nbegin=|" ; bb->dump(); llvm::errs() << "|";
- CFGElement const& xx = *bb;
- llvm::errs() << "\nbegin-deref=|" ; xx.dump();llvm::errs() << "|";
- // manual iteration
- for(auto it = SrcBlock->begin(); it != SrcBlock->end() ; ++it) {
- llvm::errs() << "\nit.kind=|" << it->getKind() << "|";
- }
-
- llvm::errs() << "\nend=" ; (SrcBlock->end()-1)->dump();
- llvm::errs() << "\nrbegin=" << SrcBlock->rbegin();
- llvm::errs() << "\nrend=" << SrcBlock->rend();
-
for (CFGElement CE : llvm::reverse(*SrcBlock)) {
if (std::optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {
const Expr *ValEx = cast<Expr>(CS->getStmt());
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 88a96f422f846f..786820c61c41c6 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -1223,14 +1223,7 @@ void ExprEngine::VisitAttributedStmt(const AttributedStmt *A,
}
Expr *AssumeExpr = AssumeAttr->getAssumption();
for (ExplodedNode *N : CheckerPreStmt) {
- // Assume State to narrow down here?
- // for now
- // ?????? VINAY HERE, need to narrow it down here
- ExplodedNode CloneWithAssume = *N;
- auto stateWithAssume = CloneWithAssume.getState();
- Bldr.generateNode(AssumeExpr, N, stateWithAssume);
-
- Visit(AssumeExpr, &CloneWithAssume, EvalSet);
+ Visit(AssumeExpr, N, EvalSet);
}
}
More information about the cfe-commits
mailing list