[clang] [compiler-rt] [UBSan] Fix _BitInt value display for unsigned types in runtime handlers (PR #194405)
Adam Magier via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 27 08:47:04 PDT 2026
https://github.com/AdamMagierFOSS created https://github.com/llvm/llvm-project/pull/194405
EmitCheckTypeDescriptor only emitted TK_BitInt (with the actual bit width appended to the type name) for signed _BitInt types. Unsigned _BitInt was left as TK_Integer, causing the runtime to compute the bit width via 1 << (TypeInfo >> 1), which gives the wrong power-of-2 storage width instead of the actual width. This produced wrong values and wrong bit widths in diagnostics, e.g. "32-bit type" instead of "27-bit type" for _BitInt(27).
Compiler:
- Remove the isSignedIntegerType() guard so TK_BitInt is emitted for both signed and unsigned _BitInt types.
Runtime:
- Remove isSignedBitIntTy() restriction from getBitIntBitCountPointer() and getIntegerBitCount() so the appended bit width is read for all _BitInt types.
- Use getIntegerBitCount() instead of getIntegerBitWidth() in isInlineInt() and in the shift-out-of-bounds and implicit-conversion diagnostic handlers.
Tests:
- Update CodeGen CHECK for unsigned _BitInt(37) to expect TK_BitInt.
- Add shift_exponent_too_big and shift_result_too_big runtime tests that verify correct bit width and value display for _BitInt operands.
- Tighten existing CHECKs for implicit_integer_sign_change and vla_bound to verify actual bit width and value are displayed.
Assisted-by: Kiro CLI / Claude Opus 4.6 (1M context)
>From 66f1c6c36b8c995b8be5e332e918cd696e536d21 Mon Sep 17 00:00:00 2001
From: Adam Magier <adam.a.magier.foss at gmail.com>
Date: Mon, 27 Apr 2026 17:06:35 +0200
Subject: [PATCH] [UBSan] Fix _BitInt value display for unsigned types in
runtime handlers
EmitCheckTypeDescriptor only emitted TK_BitInt (with the actual bit
width appended to the type name) for signed _BitInt types. Unsigned
_BitInt was left as TK_Integer, causing the runtime to compute the
bit width via 1 << (TypeInfo >> 1), which gives the wrong power-of-2
storage width instead of the actual width. This produced wrong values
and wrong bit widths in diagnostics, e.g. "32-bit type" instead of
"27-bit type" for _BitInt(27).
Compiler:
- Remove the isSignedIntegerType() guard so TK_BitInt is emitted for
both signed and unsigned _BitInt types.
Runtime:
- Remove isSignedBitIntTy() restriction from getBitIntBitCountPointer()
and getIntegerBitCount() so the appended bit width is read for all
_BitInt types.
- Use getIntegerBitCount() instead of getIntegerBitWidth() in
isInlineInt() and in the shift-out-of-bounds and implicit-conversion
diagnostic handlers.
Tests:
- Update CodeGen CHECK for unsigned _BitInt(37) to expect TK_BitInt.
- Add shift_exponent_too_big and shift_result_too_big runtime tests
that verify correct bit width and value display for _BitInt operands.
- Tighten existing CHECKs for implicit_integer_sign_change and
vla_bound to verify actual bit width and value are displayed.
Assisted-by: Kiro CLI / Claude Opus 4.6 (1M context)
---
clang/lib/CodeGen/CGExpr.cpp | 5 ++--
clang/test/CodeGen/bit-int-ubsan.c | 2 +-
compiler-rt/lib/ubsan/ubsan_handlers.cpp | 10 +++----
compiler-rt/lib/ubsan/ubsan_value.h | 5 ++--
.../test/ubsan/TestCases/Integer/bit-int.c | 28 +++++++++++++++++--
5 files changed, 36 insertions(+), 14 deletions(-)
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 987a96ba19178..c75b2c0a53d31 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -3938,15 +3938,14 @@ llvm::Constant *CodeGenFunction::EmitCheckTypeDescriptor(QualType T) {
// Follow suggestion from discussion of issue 64100.
// So we can write the exact amount of bits in TypeName after '\0'
// making it <diagnostic-like type name>.'\0'.<32-bit width>.
- if (T->isSignedIntegerType() && T->getAs<BitIntType>()) {
+ if (T->getAs<BitIntType>()) {
// Do a sanity checks as we are using 32-bit type to store bit length.
assert(getContext().getTypeSize(T) > 0 &&
" non positive amount of bits in __BitInt type");
assert(getContext().getTypeSize(T) <= 0xFFFFFFFF &&
" too many bits in __BitInt type");
- // Redefine TypeKind with the actual __BitInt type if we have signed
- // BitInt.
+ // Redefine TypeKind with the actual __BitInt type.
TypeKind = TK_BitInt;
IsBitInt = true;
}
diff --git a/clang/test/CodeGen/bit-int-ubsan.c b/clang/test/CodeGen/bit-int-ubsan.c
index 40e4fde1d1f41..eefa2a40b81ee 100644
--- a/clang/test/CodeGen/bit-int-ubsan.c
+++ b/clang/test/CodeGen/bit-int-ubsan.c
@@ -23,7 +23,7 @@ uint32_t implicit_unsigned_integer_truncation() {
x += float_divide_by_zero();
x += integer_divide_by_zero();
x = x + 0xFFFFFFFFFFFFFFFFULL;
- // CHECK: constant { i16, i16, [23 x i8] } { i16 0, i16 12, [23 x i8] c"'unsigned _BitInt(37)'\00" }
+ // CHECK: constant { i16, i16, [29 x i8] } { i16 2, i16 12, [29 x i8] c"'unsigned _BitInt(37)'\00%\00\00\00\00\00" }
uint32_t r = x & 0xFFFFFFFF;
return r;
}
diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.cpp b/compiler-rt/lib/ubsan/ubsan_handlers.cpp
index 63319f46734a4..f32ebd7a7a3b2 100644
--- a/compiler-rt/lib/ubsan/ubsan_handlers.cpp
+++ b/compiler-rt/lib/ubsan/ubsan_handlers.cpp
@@ -336,7 +336,7 @@ static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData *Data,
ErrorType ET;
if (RHSVal.isNegative() ||
- RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth())
+ RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitCount())
ET = ErrorType::InvalidShiftExponent;
else
ET = ErrorType::InvalidShiftBase;
@@ -352,7 +352,7 @@ static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData *Data,
else
Diag(Loc, DL_Error, ET,
"shift exponent %0 is too large for %1-bit type %2")
- << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType;
+ << RHSVal << Data->LHSType.getIntegerBitCount() << Data->LHSType;
} else {
if (LHSVal.isNegative())
Diag(Loc, DL_Error, ET, "left shift of negative value %0") << LHSVal;
@@ -621,16 +621,16 @@ static void handleImplicitConversion(ImplicitConversionData *Data,
Diag(Loc, DL_Error, ET,
"implicit conversion from type %0 of value %1 (%2-bit, %3signed) to "
"type %4 changed the value to %5 (%6-bit bitfield, %7signed)")
- << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth()
+ << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitCount()
<< (SrcSigned ? "" : "un") << DstTy << Value(DstTy, Dst)
<< Data->BitfieldBits << (DstSigned ? "" : "un");
else
Diag(Loc, DL_Error, ET,
"implicit conversion from type %0 of value %1 (%2-bit, %3signed) to "
"type %4 changed the value to %5 (%6-bit, %7signed)")
- << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth()
+ << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitCount()
<< (SrcSigned ? "" : "un") << DstTy << Value(DstTy, Dst)
- << DstTy.getIntegerBitWidth() << (DstSigned ? "" : "un");
+ << DstTy.getIntegerBitCount() << (DstSigned ? "" : "un");
}
void __ubsan::__ubsan_handle_implicit_conversion(ImplicitConversionData *Data,
diff --git a/compiler-rt/lib/ubsan/ubsan_value.h b/compiler-rt/lib/ubsan/ubsan_value.h
index ee523cf5ddda5..840fbae2beeec 100644
--- a/compiler-rt/lib/ubsan/ubsan_value.h
+++ b/compiler-rt/lib/ubsan/ubsan_value.h
@@ -139,7 +139,6 @@ class TypeDescriptor {
const char *getBitIntBitCountPointer() const {
DCHECK(isBitIntTy());
- DCHECK(isSignedBitIntTy());
// Scan Name for zero and return the next address
const char *p = getTypeName();
while (*p != '\0')
@@ -150,7 +149,7 @@ class TypeDescriptor {
unsigned getIntegerBitCount() const {
DCHECK(isIntegerTy());
- if (isSignedBitIntTy()) {
+ if (isBitIntTy()) {
u32 BitCountValue;
internal_memcpy(&BitCountValue, getBitIntBitCountPointer(),
sizeof(BitCountValue));
@@ -188,7 +187,7 @@ class Value {
bool isInlineInt() const {
CHECK(getType().isIntegerTy());
const unsigned InlineBits = sizeof(ValueHandle) * 8;
- const unsigned Bits = getType().getIntegerBitWidth();
+ const unsigned Bits = getType().getIntegerBitCount();
return Bits <= InlineBits;
}
diff --git a/compiler-rt/test/ubsan/TestCases/Integer/bit-int.c b/compiler-rt/test/ubsan/TestCases/Integer/bit-int.c
index e15c6e6e1756a..5a254d3c975d1 100644
--- a/compiler-rt/test/ubsan/TestCases/Integer/bit-int.c
+++ b/compiler-rt/test/ubsan/TestCases/Integer/bit-int.c
@@ -39,7 +39,7 @@ uint32_t pointer_overflow() __attribute__((no_sanitize("address"))) {
uint32_t vla_bound(_BitInt(37) x) {
_BitInt(37) a[x - 1];
- // CHECK: {{.*}}bit-int.c:[[#@LINE-1]]:17: runtime error: variable length array bound evaluates to non-positive value
+ // CHECK: {{.*}}bit-int.c:[[#@LINE-1]]:17: runtime error: variable length array bound evaluates to non-positive value 0
return 0;
}
@@ -65,7 +65,7 @@ uint32_t float_cast_overflow() {
uint32_t implicit_integer_sign_change(unsigned _BitInt(37) x) {
_BitInt(37) r = x;
- // CHECK: {{.*}}bit-int.c:[[#@LINE-1]]:19: runtime error: implicit conversion from type '{{[^']+}}' of value
+ // CHECK: {{.*}}bit-int.c:[[#@LINE-1]]:19: runtime error: implicit conversion from type '{{[^']+}}' of value {{[0-9]+}} (37-bit, unsigned)
return r & 0xFFFFFFFF;
}
@@ -131,6 +131,27 @@ uint32_t negative_shift5(unsigned _BitInt(37) x)
// CHECK: {{.*}}bit-int.c:[[#@LINE-1]]:12: runtime error: shift exponent -2 is negative
}
+uint32_t shift_exponent_too_big1(_BitInt(27) x)
+ __attribute__((no_sanitize("memory"))) {
+ unsigned _BitInt(24) c = 27;
+ return (uint32_t)(x << c) & 0xFF;
+ // CHECK: {{.*}}bit-int.c:[[#@LINE-1]]:23: runtime error: shift exponent 27 is too large for 27-bit type '_BitInt(27)'
+}
+
+uint32_t shift_exponent_too_big2(_BitInt(27) x)
+ __attribute__((no_sanitize("memory"))) {
+ _BitInt(24) c = 29;
+ return (uint32_t)(x << c) & 0xFF;
+ // CHECK: {{.*}}bit-int.c:[[#@LINE-1]]:23: runtime error: shift exponent 29 is too large for 27-bit type '_BitInt(27)'
+}
+
+uint32_t shift_result_too_big1(_BitInt(27) x)
+ __attribute__((no_sanitize("memory"))) {
+ _BitInt(24) c = 26;
+ return (uint32_t)(x << c) & 0xFF;
+ // CHECK: {{.*}}bit-int.c:[[#@LINE-1]]:23: runtime error: left shift of 5 by 26 places cannot be represented in type '_BitInt(27)'
+}
+
uint32_t unsigned_integer_overflow() __attribute__((no_sanitize("memory"))) {
unsigned _BitInt(37) x = ~0U;
++x;
@@ -165,6 +186,9 @@ int main(int argc, char **argv) {
negative_shift3(5) +
negative_shift4(5) +
negative_shift5(5) +
+ shift_exponent_too_big1(5) +
+ shift_exponent_too_big2(5) +
+ shift_result_too_big1(5) +
unsigned_integer_overflow() +
signed_integer_overflow();
// clang-format on
More information about the cfe-commits
mailing list