[clang] [clang][analyzer] Correct SMT Layer for _BitInt cases refutations (PR #143310)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 11 06:52:23 PDT 2025
https://github.com/vabridgers updated https://github.com/llvm/llvm-project/pull/143310
>From 66fbfd292123136e87528d710db6aeeceed3cce2 Mon Sep 17 00:00:00 2001
From: Vince Bridgers <vince.a.bridgers at ericsson.com>
Date: Sun, 8 Jun 2025 15:48:04 +0200
Subject: [PATCH 1/7] [analyzer] Correct SMT Layer for _BitInt cases
refutations
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Since _BitInt was added later, SMT did not comprehend getting
a type by bitwidth that's not a power of 2. This led to unexpected
crashes using Z3 refutation during randomized testing. The assertion and
redacted and summarized crash stack is shown here.
clang: ../../clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h:103:
static llvm::SMTExprRef clang::ento::SMTConv::fromBinOp(llvm::SMTSolverRef &,
const llvm::SMTExprRef &, const BinaryOperator::Opcode, const llvm::SMTExprRef &, bool):
Assertion `*Solver->getSort(LHS) == *Solver->getSort(RHS) && "AST's must have the same sort!"' failed.
...
#9 <address> clang::ento::SMTConv::fromBinOp(std::shared_ptr<llvm::SMTSolver>&,
llvm::SMTExpr const* const&, clang::BinaryOperatorKind, llvm::SMTExpr const* const&,
bool) SMTConstraintManager.cpp
clang::ASTContext&, llvm::SMTExpr const* const&, clang::QualType,
clang::BinaryOperatorKind, llvm::SMTExpr const* const&, clang::QualType,
clang::QualType*) SMTConstraintManager.cpp
clang::ASTContext&, clang::ento::SymExpr const*, llvm::APSInt const&,
llvm::APSInt const&, bool) SMTConstraintManager.cpp
clang::ento::ExplodedNode const*, clang::ento::PathSensitiveBugReport&)
---
.../Core/PathSensitive/SMTConv.h | 27 ++++++++++++++++---
clang/test/Analysis/bitint-z3.c | 23 ++++++++++++++++
2 files changed, 47 insertions(+), 3 deletions(-)
create mode 100644 clang/test/Analysis/bitint-z3.c
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
index 580b49a38dc72..50c29a3a7f781 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
@@ -18,6 +18,8 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/Support/SMTAPI.h"
+#include <algorithm>
+
namespace clang {
namespace ento {
@@ -570,23 +572,42 @@ class SMTConv {
// TODO: Refactor to put elsewhere
static inline QualType getAPSIntType(ASTContext &Ctx,
const llvm::APSInt &Int) {
- return Ctx.getIntTypeForBitwidth(Int.getBitWidth(), Int.isSigned());
+ QualType Ty = Ctx.getIntTypeForBitwidth(Int.getBitWidth(), Int.isSigned());
+ // If Ty is Null, could be because the original type was a _BitInt.
+ // Get the bit size and round up to next power of 2, max char size
+ if (Ty.isNull()) {
+ unsigned CharTypeSize = Ctx.getTypeSize(Ctx.CharTy);
+ unsigned pow2DestWidth =
+ std::max(llvm::bit_ceil(Int.getBitWidth()), CharTypeSize);
+ Ty = Ctx.getIntTypeForBitwidth(pow2DestWidth, Int.isSigned());
+ }
+ return Ty;
+ }
+
+ static inline bool IsPower2(unsigned bits) {
+ return bits > 0 && (bits & (bits - 1)) == 0;
}
// Get the QualTy for the input APSInt, and fix it if it has a bitwidth of 1.
static inline std::pair<llvm::APSInt, QualType>
fixAPSInt(ASTContext &Ctx, const llvm::APSInt &Int) {
llvm::APSInt NewInt;
+ unsigned APSIntBitwidth = Int.getBitWidth();
+ QualType Ty = getAPSIntType(Ctx, Int);
// FIXME: This should be a cast from a 1-bit integer type to a boolean type,
// but the former is not available in Clang. Instead, extend the APSInt
// directly.
- if (Int.getBitWidth() == 1 && getAPSIntType(Ctx, Int).isNull()) {
+ if (APSIntBitwidth == 1 && Ty.isNull()) {
NewInt = Int.extend(Ctx.getTypeSize(Ctx.BoolTy));
+ Ty = getAPSIntType(Ctx, NewInt);
+ } else if (!IsPower2(APSIntBitwidth) && !getAPSIntType(Ctx, Int).isNull()) {
+ Ty = getAPSIntType(Ctx, Int);
+ NewInt = Int.extend(Ctx.getTypeSize(Ty));
} else
NewInt = Int;
- return std::make_pair(NewInt, getAPSIntType(Ctx, NewInt));
+ return std::make_pair(NewInt, Ty);
}
// Perform implicit type conversion on binary symbolic expressions.
diff --git a/clang/test/Analysis/bitint-z3.c b/clang/test/Analysis/bitint-z3.c
new file mode 100644
index 0000000000000..d50c93acdf117
--- /dev/null
+++ b/clang/test/Analysis/bitint-z3.c
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -w -DNO_CROSSCHECK -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -w -analyzer-config crosscheck-with-z3=true -verify %s
+// REQUIRES: z3
+
+// The SMTConv layer did not comprehend _BitInt types (because this was an
+// evolved feature) and was crashing due to needed updates in 2 places.
+// Analysis is expected to find these cases using _BitInt without crashing.
+
+_BitInt(35) a;
+int b;
+void c() {
+ int d;
+ if (a)
+ b = d; // expected-warning{{Assigned value is uninitialized [core.uninitialized.Assign]}}
+}
+
+int *d;
+_BitInt(3) e;
+void f() {
+ int g;
+ d = &g;
+ e ?: 0; // expected-warning{{Address of stack memory associated with local variable 'g' is still referred to by the global variable 'd' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}}
+}
>From 94db43307c6e173c9eae7eb5b1fde49a53c9086d Mon Sep 17 00:00:00 2001
From: einvbri <vince.a.bridgers at ericsson.com>
Date: Tue, 10 Jun 2025 19:47:03 +0200
Subject: [PATCH 2/7] Address review comments
---
.../StaticAnalyzer/Core/PathSensitive/SMTConv.h | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
index 50c29a3a7f781..8d0a82cb0984b 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
@@ -577,16 +577,16 @@ class SMTConv {
// Get the bit size and round up to next power of 2, max char size
if (Ty.isNull()) {
unsigned CharTypeSize = Ctx.getTypeSize(Ctx.CharTy);
- unsigned pow2DestWidth =
+ unsigned Pow2DestWidth =
std::max(llvm::bit_ceil(Int.getBitWidth()), CharTypeSize);
- Ty = Ctx.getIntTypeForBitwidth(pow2DestWidth, Int.isSigned());
+ Ty = Ctx.getIntTypeForBitwidth(Pow2DestWidth, Int.isSigned());
}
return Ty;
}
- static inline bool IsPower2(unsigned bits) {
- return bits > 0 && (bits & (bits - 1)) == 0;
- }
+ // static inline bool IsPower2(unsigned bits) {
+ // return bits > 0 && (bits & (bits - 1)) == 0;
+ // }
// Get the QualTy for the input APSInt, and fix it if it has a bitwidth of 1.
static inline std::pair<llvm::APSInt, QualType>
@@ -601,8 +601,8 @@ class SMTConv {
if (APSIntBitwidth == 1 && Ty.isNull()) {
NewInt = Int.extend(Ctx.getTypeSize(Ctx.BoolTy));
Ty = getAPSIntType(Ctx, NewInt);
- } else if (!IsPower2(APSIntBitwidth) && !getAPSIntType(Ctx, Int).isNull()) {
- Ty = getAPSIntType(Ctx, Int);
+ } else if (!llvm::isPowerOf2_32(APSIntBitwidth) &&
+ !getAPSIntType(Ctx, Int).isNull()) {
NewInt = Int.extend(Ctx.getTypeSize(Ty));
} else
NewInt = Int;
>From fa9760917b69b06ab9bee6b5e4878e64cae751f2 Mon Sep 17 00:00:00 2001
From: einvbri <vince.a.bridgers at ericsson.com>
Date: Tue, 10 Jun 2025 22:01:54 +0200
Subject: [PATCH 3/7] Really address the review comments I missed the first
time
:)
---
.../clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
index 8d0a82cb0984b..3238f56a8bb3a 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
@@ -584,10 +584,6 @@ class SMTConv {
return Ty;
}
- // static inline bool IsPower2(unsigned bits) {
- // return bits > 0 && (bits & (bits - 1)) == 0;
- // }
-
// Get the QualTy for the input APSInt, and fix it if it has a bitwidth of 1.
static inline std::pair<llvm::APSInt, QualType>
fixAPSInt(ASTContext &Ctx, const llvm::APSInt &Int) {
@@ -601,8 +597,7 @@ class SMTConv {
if (APSIntBitwidth == 1 && Ty.isNull()) {
NewInt = Int.extend(Ctx.getTypeSize(Ctx.BoolTy));
Ty = getAPSIntType(Ctx, NewInt);
- } else if (!llvm::isPowerOf2_32(APSIntBitwidth) &&
- !getAPSIntType(Ctx, Int).isNull()) {
+ } else if (!llvm::isPowerOf2_32(APSIntBitwidth) && !Ty.isNull()) {
NewInt = Int.extend(Ctx.getTypeSize(Ty));
} else
NewInt = Int;
>From 6d3e79bf1b7feda1795cbf33c87acd0246f4a990 Mon Sep 17 00:00:00 2001
From: einvbri <vince.a.bridgers at ericsson.com>
Date: Wed, 11 Jun 2025 12:48:56 +0200
Subject: [PATCH 4/7] Address latest round of comments, rebase to latest
origin/main
---
.../Core/PathSensitive/SMTConv.h | 30 +++++++++----------
clang/test/Analysis/bitint-z3.c | 23 +++++++-------
2 files changed, 25 insertions(+), 28 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
index 3238f56a8bb3a..e3f4dc13d54a0 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
@@ -572,15 +572,16 @@ class SMTConv {
// TODO: Refactor to put elsewhere
static inline QualType getAPSIntType(ASTContext &Ctx,
const llvm::APSInt &Int) {
- QualType Ty = Ctx.getIntTypeForBitwidth(Int.getBitWidth(), Int.isSigned());
+ QualType Ty;
+ if (!(Ty = Ctx.getIntTypeForBitwidth(Int.getBitWidth(), Int.isSigned()))
+ .isNull())
+ return Ty;
// If Ty is Null, could be because the original type was a _BitInt.
// Get the bit size and round up to next power of 2, max char size
- if (Ty.isNull()) {
- unsigned CharTypeSize = Ctx.getTypeSize(Ctx.CharTy);
- unsigned Pow2DestWidth =
- std::max(llvm::bit_ceil(Int.getBitWidth()), CharTypeSize);
- Ty = Ctx.getIntTypeForBitwidth(Pow2DestWidth, Int.isSigned());
- }
+ unsigned CharTypeSize = Ctx.getTypeSize(Ctx.CharTy);
+ unsigned Pow2DestWidth =
+ std::max(llvm::bit_ceil(Int.getBitWidth()), CharTypeSize);
+ Ty = Ctx.getIntTypeForBitwidth(Pow2DestWidth, Int.isSigned());
return Ty;
}
@@ -594,15 +595,12 @@ class SMTConv {
// FIXME: This should be a cast from a 1-bit integer type to a boolean type,
// but the former is not available in Clang. Instead, extend the APSInt
// directly.
- if (APSIntBitwidth == 1 && Ty.isNull()) {
- NewInt = Int.extend(Ctx.getTypeSize(Ctx.BoolTy));
- Ty = getAPSIntType(Ctx, NewInt);
- } else if (!llvm::isPowerOf2_32(APSIntBitwidth) && !Ty.isNull()) {
- NewInt = Int.extend(Ctx.getTypeSize(Ty));
- } else
- NewInt = Int;
-
- return std::make_pair(NewInt, Ty);
+ if (APSIntBitwidth == 1 && Ty.isNull())
+ return {Int.extend(Ctx.getTypeSize(Ctx.BoolTy)),
+ getAPSIntType(Ctx, NewInt)};
+ if (!llvm::isPowerOf2_32(APSIntBitwidth) && !Ty.isNull())
+ return {Int.extend(Ctx.getTypeSize(Ty)), Ty};
+ return {Int, Ty};
}
// Perform implicit type conversion on binary symbolic expressions.
diff --git a/clang/test/Analysis/bitint-z3.c b/clang/test/Analysis/bitint-z3.c
index d50c93acdf117..8bb97ca968f11 100644
--- a/clang/test/Analysis/bitint-z3.c
+++ b/clang/test/Analysis/bitint-z3.c
@@ -1,23 +1,22 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core -w -DNO_CROSSCHECK -verify %s
-// RUN: %clang_cc1 -analyze -analyzer-checker=core -w -analyzer-config crosscheck-with-z3=true -verify %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -w \
+// RUN: -analyzer-config crosscheck-with-z3=true -verify %s
// REQUIRES: z3
-// The SMTConv layer did not comprehend _BitInt types (because this was an
-// evolved feature) and was crashing due to needed updates in 2 places.
-// Analysis is expected to find these cases using _BitInt without crashing.
+// Previously these tests were crashing because the SMTConv layer did not
+// comprehend the _BitInt types.
-_BitInt(35) a;
-int b;
-void c() {
+void clang_analyzer_warnIfReached();
+
+void c(int b, _BitInt(35) a) {
int d;
if (a)
b = d; // expected-warning{{Assigned value is uninitialized [core.uninitialized.Assign]}}
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
}
-int *d;
-_BitInt(3) e;
-void f() {
+void f(int *d, _BitInt(3) e) {
int g;
d = &g;
- e ?: 0; // expected-warning{{Address of stack memory associated with local variable 'g' is still referred to by the global variable 'd' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}}
+ e ?: 0;
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
}
>From c3fa58f25fb41b5a39a8d0c22c433b6c913941c2 Mon Sep 17 00:00:00 2001
From: einvbri <vince.a.bridgers at ericsson.com>
Date: Wed, 11 Jun 2025 15:09:45 +0200
Subject: [PATCH 5/7] Address next round of comments
---
.../StaticAnalyzer/Core/PathSensitive/SMTConv.h | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
index e3f4dc13d54a0..6ed569eaa0a49 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
@@ -577,12 +577,14 @@ class SMTConv {
.isNull())
return Ty;
// If Ty is Null, could be because the original type was a _BitInt.
- // Get the bit size and round up to next power of 2, max char size
+ // Get the size of the _BitInt type (expressed in bits) and round it up to
+ // the next power of 2 that is at least the bit size of 'char' (usually 8).
unsigned CharTypeSize = Ctx.getTypeSize(Ctx.CharTy);
unsigned Pow2DestWidth =
std::max(llvm::bit_ceil(Int.getBitWidth()), CharTypeSize);
- Ty = Ctx.getIntTypeForBitwidth(Pow2DestWidth, Int.isSigned());
- return Ty;
+ return Ctx.getIntTypeForBitwidth(Pow2DestWidth, Int.isSigned());
+ // Ty = Ctx.getIntTypeForBitwidth(Pow2DestWidth, Int.isSigned());
+ // return Ty;
}
// Get the QualTy for the input APSInt, and fix it if it has a bitwidth of 1.
@@ -598,9 +600,9 @@ class SMTConv {
if (APSIntBitwidth == 1 && Ty.isNull())
return {Int.extend(Ctx.getTypeSize(Ctx.BoolTy)),
getAPSIntType(Ctx, NewInt)};
- if (!llvm::isPowerOf2_32(APSIntBitwidth) && !Ty.isNull())
- return {Int.extend(Ctx.getTypeSize(Ty)), Ty};
- return {Int, Ty};
+ if (llvm::isPowerOf2_32(APSIntBitwidth) || Ty.isNull())
+ return {Int, Ty};
+ return {Int.extend(Ctx.getTypeSize(Ty)), Ty};
}
// Perform implicit type conversion on binary symbolic expressions.
>From 86809d7315aaadcf1458778404d27c8861e7ccb0 Mon Sep 17 00:00:00 2001
From: einvbri <vince.a.bridgers at ericsson.com>
Date: Wed, 11 Jun 2025 15:47:19 +0200
Subject: [PATCH 6/7] address next round of comments
---
.../clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h | 7 ++-----
clang/test/Analysis/bitint-z3.c | 4 ++--
2 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
index 6ed569eaa0a49..69d1743842312 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
@@ -572,9 +572,8 @@ class SMTConv {
// TODO: Refactor to put elsewhere
static inline QualType getAPSIntType(ASTContext &Ctx,
const llvm::APSInt &Int) {
- QualType Ty;
- if (!(Ty = Ctx.getIntTypeForBitwidth(Int.getBitWidth(), Int.isSigned()))
- .isNull())
+ const QualType Ty = Ctx.getIntTypeForBitwidth(Int.getBitWidth(), Int.isSigned());
+ if (!Ty.isNull())
return Ty;
// If Ty is Null, could be because the original type was a _BitInt.
// Get the size of the _BitInt type (expressed in bits) and round it up to
@@ -583,8 +582,6 @@ class SMTConv {
unsigned Pow2DestWidth =
std::max(llvm::bit_ceil(Int.getBitWidth()), CharTypeSize);
return Ctx.getIntTypeForBitwidth(Pow2DestWidth, Int.isSigned());
- // Ty = Ctx.getIntTypeForBitwidth(Pow2DestWidth, Int.isSigned());
- // return Ty;
}
// Get the QualTy for the input APSInt, and fix it if it has a bitwidth of 1.
diff --git a/clang/test/Analysis/bitint-z3.c b/clang/test/Analysis/bitint-z3.c
index 8bb97ca968f11..4cb97f9de8299 100644
--- a/clang/test/Analysis/bitint-z3.c
+++ b/clang/test/Analysis/bitint-z3.c
@@ -8,9 +8,9 @@
void clang_analyzer_warnIfReached();
void c(int b, _BitInt(35) a) {
- int d;
+ int d = 0;
if (a)
- b = d; // expected-warning{{Assigned value is uninitialized [core.uninitialized.Assign]}}
+ b = d;
clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
}
>From f408a01ecd1834806e7c906fca2ce9675e15befa Mon Sep 17 00:00:00 2001
From: einvbri <vince.a.bridgers at ericsson.com>
Date: Wed, 11 Jun 2025 15:51:13 +0200
Subject: [PATCH 7/7] pretty format the code
---
.../include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
index 69d1743842312..70a7953918ace 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h
@@ -572,7 +572,8 @@ class SMTConv {
// TODO: Refactor to put elsewhere
static inline QualType getAPSIntType(ASTContext &Ctx,
const llvm::APSInt &Int) {
- const QualType Ty = Ctx.getIntTypeForBitwidth(Int.getBitWidth(), Int.isSigned());
+ const QualType Ty =
+ Ctx.getIntTypeForBitwidth(Int.getBitWidth(), Int.isSigned());
if (!Ty.isNull())
return Ty;
// If Ty is Null, could be because the original type was a _BitInt.
More information about the cfe-commits
mailing list