Skip to content

Commit abf62ab

Browse files
earnolVladislav AranovAaronBallman
authored andcommitted
[ubsan] Display correct runtime messages for negative _BitInt (llvm#93612)
Without this patch compiler-rt ubsan library has a bug displaying incorrect values for variables of the _BitInt (previously called _ExtInt) type. This patch affects affects both: generation of metadata inside code generator and runtime part. The runtime part provided only for i386 and x86_64 runtimes. Other runtimes should be updated to take full benefit of this patch. The patch is constructed the way to be backward compatible and int and float type runtime diagnostics should be unaffected for not yet updated runtimes. This patch fixes issue: llvm#64100. Co-authored-by: Vladislav Aranov <[email protected]> Co-authored-by: Aaron Ballman <[email protected]>
1 parent a833b11 commit abf62ab

File tree

5 files changed

+305
-13
lines changed

5 files changed

+305
-13
lines changed

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "llvm/IR/MatrixBuilder.h"
4242
#include "llvm/Passes/OptimizationLevel.h"
4343
#include "llvm/Support/ConvertUTF.h"
44+
#include "llvm/Support/Endian.h"
4445
#include "llvm/Support/MathExtras.h"
4546
#include "llvm/Support/Path.h"
4647
#include "llvm/Support/SaveAndRestore.h"
@@ -64,6 +65,22 @@ static llvm::cl::opt<bool> ClSanitizeGuardChecks(
6465
"ubsan-guard-checks", llvm::cl::Optional,
6566
llvm::cl::desc("Guard UBSAN checks with `llvm.allow.ubsan.check()`."));
6667

68+
//===--------------------------------------------------------------------===//
69+
// Defines for metadata
70+
//===--------------------------------------------------------------------===//
71+
72+
// Those values are crucial to be the SAME as in ubsan runtime library.
73+
enum VariableTypeDescriptorKind : uint16_t {
74+
/// An integer type.
75+
TK_Integer = 0x0000,
76+
/// A floating-point type.
77+
TK_Float = 0x0001,
78+
/// An _BitInt(N) type.
79+
TK_BitInt = 0x0002,
80+
/// Any other type. The value representation is unspecified.
81+
TK_Unknown = 0xffff
82+
};
83+
6784
//===--------------------------------------------------------------------===//
6885
// Miscellaneous Helper Methods
6986
//===--------------------------------------------------------------------===//
@@ -3298,22 +3315,40 @@ LValue CodeGenFunction::EmitPredefinedLValue(const PredefinedExpr *E) {
32983315
/// { i16 TypeKind, i16 TypeInfo }
32993316
/// \endcode
33003317
///
3301-
/// followed by an array of i8 containing the type name. TypeKind is 0 for an
3302-
/// integer, 1 for a floating point value, and -1 for anything else.
3318+
/// followed by an array of i8 containing the type name with extra information
3319+
/// for BitInt. TypeKind is TK_Integer(0) for an integer, TK_Float(1) for a
3320+
/// floating point value, TK_BitInt(2) for BitInt and TK_Unknown(0xFFFF) for
3321+
/// anything else.
33033322
llvm::Constant *CodeGenFunction::EmitCheckTypeDescriptor(QualType T) {
33043323
// Only emit each type's descriptor once.
33053324
if (llvm::Constant *C = CGM.getTypeDescriptorFromMap(T))
33063325
return C;
33073326

3308-
uint16_t TypeKind = -1;
3327+
uint16_t TypeKind = TK_Unknown;
33093328
uint16_t TypeInfo = 0;
3329+
bool IsBitInt = false;
33103330

33113331
if (T->isIntegerType()) {
3312-
TypeKind = 0;
3332+
TypeKind = TK_Integer;
33133333
TypeInfo = (llvm::Log2_32(getContext().getTypeSize(T)) << 1) |
33143334
(T->isSignedIntegerType() ? 1 : 0);
3335+
// Follow suggestion from https://github.com/llvm/llvm-project/issues/64100
3336+
// So we can write the exact amount of bits in TypeName after '\0'
3337+
// making it <diagnostic-like type name>.'\0'.<32-bit width>.
3338+
if (T->isSignedIntegerType() && T->getAs<BitIntType>()) {
3339+
// Do a sanity checks as we are using 32-bit type to store bit length.
3340+
assert((getContext().getTypeSize(T) > 0) &&
3341+
" non positive amount of bits in __BitInt type");
3342+
assert((getContext().getTypeSize(T) <= 0xFFFFFFFF) &&
3343+
" too many bits in __BitInt type");
3344+
3345+
// Redefine TypeKind with the actual __BitInt type if we have signed
3346+
// BitInt.
3347+
TypeKind = TK_BitInt;
3348+
IsBitInt = true;
3349+
}
33153350
} else if (T->isFloatingType()) {
3316-
TypeKind = 1;
3351+
TypeKind = TK_Float;
33173352
TypeInfo = getContext().getTypeSize(T);
33183353
}
33193354

@@ -3324,6 +3359,20 @@ llvm::Constant *CodeGenFunction::EmitCheckTypeDescriptor(QualType T) {
33243359
DiagnosticsEngine::ak_qualtype, (intptr_t)T.getAsOpaquePtr(), StringRef(),
33253360
StringRef(), std::nullopt, Buffer, std::nullopt);
33263361

3362+
if (IsBitInt) {
3363+
// The Structure is: 0 to end the string, 32 bit unsigned integer in target
3364+
// endianness, zero.
3365+
char S[6] = {'\0', '\0', '\0', '\0', '\0', '\0'};
3366+
const auto *EIT = T->castAs<BitIntType>();
3367+
uint32_t Bits = EIT->getNumBits();
3368+
llvm::support::endian::write32(S + 1, Bits,
3369+
getTarget().isBigEndian()
3370+
? llvm::endianness::big
3371+
: llvm::endianness::little);
3372+
StringRef str = StringRef(S, sizeof(S) / sizeof(decltype(S[0])));
3373+
Buffer.append(str);
3374+
}
3375+
33273376
llvm::Constant *Components[] = {
33283377
Builder.getInt16(TypeKind), Builder.getInt16(TypeInfo),
33293378
llvm::ConstantDataArray::getString(getLLVMContext(), Buffer)

compiler-rt/lib/ubsan/ubsan_value.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,21 @@ const char *__ubsan::getObjCClassName(ValueHandle Pointer) {
6767

6868
SIntMax Value::getSIntValue() const {
6969
CHECK(getType().isSignedIntegerTy());
70+
// Val was zero-extended to ValueHandle. Sign-extend from original width
71+
// to SIntMax.
72+
const unsigned ExtraBits =
73+
sizeof(SIntMax) * 8 - getType().getIntegerBitCount();
7074
if (isInlineInt()) {
71-
// Val was zero-extended to ValueHandle. Sign-extend from original width
72-
// to SIntMax.
73-
const unsigned ExtraBits =
74-
sizeof(SIntMax) * 8 - getType().getIntegerBitWidth();
7575
return SIntMax(UIntMax(Val) << ExtraBits) >> ExtraBits;
7676
}
77-
if (getType().getIntegerBitWidth() == 64)
78-
return *reinterpret_cast<s64*>(Val);
77+
if (getType().getIntegerBitWidth() == 64) {
78+
return SIntMax(UIntMax(*reinterpret_cast<s64 *>(Val)) << ExtraBits) >>
79+
ExtraBits;
80+
}
7981
#if HAVE_INT128_T
8082
if (getType().getIntegerBitWidth() == 128)
81-
return *reinterpret_cast<s128*>(Val);
83+
return SIntMax(UIntMax(*reinterpret_cast<s128 *>(Val)) << ExtraBits) >>
84+
ExtraBits;
8285
#else
8386
if (getType().getIntegerBitWidth() == 128)
8487
UNREACHABLE("libclang_rt.ubsan was built without __int128 support");

compiler-rt/lib/ubsan/ubsan_value.h

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ class TypeDescriptor {
103103
/// representation is that of bitcasting the floating-point value to an
104104
/// integer type.
105105
TK_Float = 0x0001,
106+
/// An _BitInt(N) type. Lowest bit is 1 for a signed value, 0 for an
107+
/// unsigned value. Remaining bits are log_2(bit_width). The value
108+
/// representation is the integer itself if it fits into a ValueHandle, and
109+
/// a pointer to the integer otherwise. TypeName contains the true width
110+
/// of the type for the signed _BitInt(N) type stored after zero bit after
111+
/// TypeName as 32-bit unsigned integer.
112+
TK_BitInt = 0x0002,
106113
/// Any other type. The value representation is unspecified.
107114
TK_Unknown = 0xffff
108115
};
@@ -113,10 +120,15 @@ class TypeDescriptor {
113120
return static_cast<Kind>(TypeKind);
114121
}
115122

116-
bool isIntegerTy() const { return getKind() == TK_Integer; }
123+
bool isIntegerTy() const {
124+
return getKind() == TK_Integer || getKind() == TK_BitInt;
125+
}
126+
bool isBitIntTy() const { return getKind() == TK_BitInt; }
127+
117128
bool isSignedIntegerTy() const {
118129
return isIntegerTy() && (TypeInfo & 1);
119130
}
131+
bool isSignedBitIntTy() const { return isBitIntTy() && (TypeInfo & 1); }
120132
bool isUnsignedIntegerTy() const {
121133
return isIntegerTy() && !(TypeInfo & 1);
122134
}
@@ -125,6 +137,26 @@ class TypeDescriptor {
125137
return 1 << (TypeInfo >> 1);
126138
}
127139

140+
const char *getBitIntBitCountPointer() const {
141+
CHECK(isBitIntTy());
142+
CHECK(isSignedBitIntTy());
143+
// Scan Name for zero and return the next address
144+
const char *p = getTypeName();
145+
while (*p != '\0') {
146+
++p;
147+
}
148+
// Return the next address
149+
return p + 1;
150+
}
151+
152+
unsigned getIntegerBitCount() const {
153+
CHECK(isIntegerTy());
154+
if (isSignedBitIntTy())
155+
return *reinterpret_cast<const u32 *>(getBitIntBitCountPointer());
156+
else
157+
return getIntegerBitWidth();
158+
}
159+
128160
bool isFloatTy() const { return getKind() == TK_Float; }
129161
unsigned getFloatBitWidth() const {
130162
CHECK(isFloatTy());
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %clang -Wno-constant-conversion -Wno-array-bounds -Wno-division-by-zero -Wno-shift-negative-value -Wno-shift-count-negative -Wno-int-to-pointer-cast -O0 -fsanitize=alignment,array-bounds,bool,float-cast-overflow,implicit-integer-sign-change,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation,integer-divide-by-zero,nonnull-attribute,null,nullability-arg,nullability-assign,nullability-return,pointer-overflow,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,unsigned-integer-overflow,unsigned-shift-base,vla-bound %s -o %t1 && %run %t1 2>&1 | FileCheck %s
2+
3+
#include <stdint.h>
4+
#include <stdio.h>
5+
6+
// In this test there is an expectation of assignment of _BitInt not producing any output.
7+
uint32_t nullability_arg(_BitInt(37) *_Nonnull x)
8+
__attribute__((no_sanitize("address"))) {
9+
_BitInt(37) y = *(_BitInt(37) *)&x;
10+
return y;
11+
}
12+
13+
// In this test there is an expectation of ubsan not triggeting on returning random address which is inside address space of the process.
14+
_BitInt(37) nonnull_attribute(__attribute__((nonnull)) _BitInt(37) * x)
15+
__attribute__((no_sanitize("address"))) {
16+
return *(_BitInt(37) *)&x;
17+
}
18+
19+
// In this test there is an expectation of assignment of uint32_t from "invalid" _BitInt is not producing any output.
20+
uint32_t nullability_assign(_BitInt(7) * x)
21+
__attribute__((no_sanitize("address"))) {
22+
_BitInt(7) *_Nonnull y = x;
23+
int32_t r = *(_BitInt(7) *)&y;
24+
return (r > 0) ? r : 0;
25+
}
26+
27+
// In those examples the file is expected to compile&run with no diagnostics
28+
// CHECK-NOT: runtime error:
29+
30+
int main(int argc, char **argv) {
31+
// clang-format off
32+
uint64_t result =
33+
1ULL +
34+
nullability_arg((_BitInt(37) *)argc) +
35+
((uint64_t)nonnull_attribute((_BitInt(37) *)argc) & 0xFFFFFFFF) +
36+
nullability_assign((_BitInt(7) *)argc);
37+
// clang-format on
38+
printf("%u\n", (uint32_t)(result & 0xFFFFFFFF));
39+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// RUN: %clang -Wno-constant-conversion -Wno-array-bounds -Wno-division-by-zero -Wno-shift-negative-value -Wno-shift-count-negative -Wno-int-to-pointer-cast -O0 -fsanitize=alignment,array-bounds,bool,float-cast-overflow,implicit-integer-sign-change,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation,integer-divide-by-zero,nonnull-attribute,null,nullability-arg,nullability-assign,nullability-return,pointer-overflow,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,unsigned-integer-overflow,unsigned-shift-base,vla-bound %s -o %t1 && %run %t1 2>&1 | FileCheck %s --check-prefix=RUNTIME
2+
// RUN: %clang -Wno-constant-conversion -Wno-array-bounds -Wno-division-by-zero -Wno-shift-negative-value -Wno-shift-count-negative -Wno-int-to-pointer-cast -fsanitize=array-bounds,enum,float-cast-overflow,integer-divide-by-zero,implicit-unsigned-integer-truncation,implicit-signed-integer-truncation,implicit-integer-sign-change,unsigned-integer-overflow,signed-integer-overflow,shift-base,shift-exponent -O0 -S -emit-llvm -o - %s | FileCheck %s --check-prefix=IR
3+
4+
#include <stdint.h>
5+
#include <stdio.h>
6+
7+
uint32_t float_divide_by_zero() {
8+
float f = 1.0f / 0.0f;
9+
// IR: constant { i16, i16, [8 x i8] } { i16 1, i16 32, [8 x i8] c"'float'\00" }
10+
_BitInt(37) r = (_BitInt(37))f;
11+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:19: runtime error: inf is outside the range of representable values of type
12+
// IR: constant { i16, i16, [20 x i8] } { i16 2, i16 13, [20 x i8] c"'_BitInt(37)'\00%\00\00\00\00\00" }
13+
return r;
14+
}
15+
16+
uint32_t integer_divide_by_zero() __attribute__((no_sanitize("memory"))) {
17+
_BitInt(37) x = 1 / 0;
18+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:21: runtime error: division by zero
19+
// IR: constant { i16, i16, [32 x i8] } { i16 0, i16 10, [32 x i8] c"'uint32_t' (aka 'unsigned int')\00" }
20+
return x;
21+
}
22+
23+
uint32_t implicit_unsigned_integer_truncation() {
24+
unsigned _BitInt(37) x = 2U;
25+
x += float_divide_by_zero();
26+
x += integer_divide_by_zero();
27+
x = x + 0xFFFFFFFFFFFFFFFFULL;
28+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:9: runtime error: unsigned integer overflow:
29+
// IR: constant { i16, i16, [23 x i8] } { i16 0, i16 12, [23 x i8] c"'unsigned _BitInt(37)'\00" }
30+
uint32_t r = x & 0xFFFFFFFF;
31+
return r;
32+
}
33+
34+
uint32_t pointer_overflow() __attribute__((no_sanitize("address"))) {
35+
_BitInt(37) *x = (_BitInt(37) *)1;
36+
_BitInt(37) *y = x - 1;
37+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:22: runtime error: pointer index expression with base
38+
uint32_t r = *(_BitInt(37) *)&y;
39+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:16: runtime error: implicit conversion from type
40+
return r;
41+
}
42+
43+
uint32_t vla_bound(_BitInt(37) x) {
44+
_BitInt(37) a[x - 1];
45+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:17: runtime error: variable length array bound evaluates to non-positive value
46+
return 0;
47+
}
48+
49+
uint32_t unsigned_shift_base() {
50+
unsigned _BitInt(37) x = ~0U << 1;
51+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:32: runtime error: left shift of 4294967295 by 1 places cannot be represented in type
52+
return x;
53+
}
54+
55+
uint32_t array_bounds() {
56+
_BitInt(37) x[4];
57+
_BitInt(37) y = x[10];
58+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:19: runtime error: index 10 out of bounds for type
59+
// IR: constant { i16, i16, [17 x i8] } { i16 -1, i16 0, [17 x i8] c"'_BitInt(37)[4]'\00" }
60+
return (uint32_t)y;
61+
}
62+
63+
uint32_t float_cast_overflow() {
64+
float a = 100000000.0f;
65+
_BitInt(7) b = (_BitInt(7))a;
66+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:18: runtime error: 1e+08 is outside the range of representable values of type
67+
// IR: constant { i16, i16, [19 x i8] } { i16 2, i16 7, [19 x i8] c"'_BitInt(7)'\00\07\00\00\00\00\00" }
68+
return b;
69+
}
70+
71+
uint32_t implicit_integer_sign_change(unsigned _BitInt(37) x) {
72+
_BitInt(37) r = x;
73+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:19: runtime error: implicit conversion from type '{{[^']+}}' of value
74+
return r & 0xFFFFFFFF;
75+
}
76+
77+
_BitInt(13) implicit_signed_integer_truncation() {
78+
_BitInt(73) x = (_BitInt(73)) ~((~0UL) >> 1);
79+
return x;
80+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:10: runtime error: implicit conversion from type
81+
// IR: constant { i16, i16, [20 x i8] } { i16 2, i16 {{([[:xdigit:]]{2})}}, [20 x i8] c"'_BitInt(73)'\00I\00\00\00\00\00" }
82+
// IR: constant { i16, i16, [20 x i8] } { i16 2, i16 9, [20 x i8] c"'_BitInt(13)'\00\0D\00\00\00\00\00" }
83+
}
84+
85+
_BitInt(37) shift_exponent() {
86+
_BitInt(37) x = 1 << (-1);
87+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:21: runtime error: shift exponent -1 is negative
88+
return x;
89+
}
90+
91+
_BitInt(37) shift_base() {
92+
_BitInt(37) x = (-1) << 1;
93+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:24: runtime error: left shift of negative value -1
94+
return x;
95+
}
96+
97+
uint32_t negative_shift1(unsigned _BitInt(37) x) {
98+
_BitInt(9) c = -2;
99+
return x >> c;
100+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:12: runtime error: shift exponent -2 is negative
101+
// IR: constant { i16, i16, [19 x i8] } { i16 2, i16 9, [19 x i8] c"'_BitInt(9)'\00\09\00\00\00\00\00" }
102+
}
103+
104+
uint32_t negative_shift2(unsigned _BitInt(37) x) {
105+
_BitInt(17) c = -2;
106+
return x >> c;
107+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:12: runtime error: shift exponent -2 is negative
108+
// IR: constant { i16, i16, [20 x i8] } { i16 2, i16 11, [20 x i8] c"'_BitInt(17)'\00\11\00\00\00\00\00" }
109+
}
110+
111+
uint32_t negative_shift3(unsigned _BitInt(37) x) {
112+
_BitInt(34) c = -2;
113+
return x >> c;
114+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:12: runtime error: shift exponent -2 is negative
115+
// IR: constant { i16, i16, [20 x i8] } { i16 2, i16 13, [20 x i8] c"'_BitInt(34)'\00\22\00\00\00\00\00" }
116+
}
117+
118+
uint32_t negative_shift4(unsigned _BitInt(37) x) {
119+
int64_t c = -2;
120+
return x >> c;
121+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:12: runtime error: shift exponent -2 is negative
122+
}
123+
124+
uint32_t negative_shift5(unsigned _BitInt(37) x) {
125+
_BitInt(68) c = -2;
126+
return x >> c;
127+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:12: runtime error: shift exponent -2 is negative
128+
// CHECK-IR: constant { i16, i16, [20 x i8] } { i16 2, i16 {{([[:xdigit:]]{2})}}, [20 x i8] c"'_BitInt(68)'\00D\00\00\00\00\00" }
129+
}
130+
131+
uint32_t unsigned_integer_overflow() {
132+
unsigned _BitInt(37) x = ~0U;
133+
++x;
134+
return x;
135+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:10: runtime error: implicit conversion from type
136+
}
137+
138+
// In this test no run-time overflow expected, so no diagnostics here, but should be a conversion error from the negative number on return.
139+
uint32_t signed_integer_overflow() {
140+
_BitInt(37) x = (_BitInt(37)) ~((0x8FFFFFFFFFFFFFFFULL) >> 1);
141+
--x;
142+
return x;
143+
// RUNTIME: {{.*}}bit-int.c:[[@LINE-1]]:10: runtime error: implicit conversion from type
144+
}
145+
146+
int main(int argc, char **argv) {
147+
// clang-format off
148+
uint64_t result =
149+
1ULL +
150+
implicit_unsigned_integer_truncation() +
151+
pointer_overflow() +
152+
vla_bound(argc) +
153+
unsigned_shift_base() +
154+
(uint32_t)array_bounds() +
155+
float_cast_overflow() +
156+
implicit_integer_sign_change((unsigned _BitInt(37))(argc - 2)) +
157+
(uint64_t)implicit_signed_integer_truncation() +
158+
shift_exponent() +
159+
(uint32_t)shift_base() +
160+
negative_shift1(5) +
161+
negative_shift2(5) +
162+
negative_shift3(5) +
163+
negative_shift4(5) +
164+
negative_shift5(5) +
165+
unsigned_integer_overflow() +
166+
signed_integer_overflow();
167+
// clang-format on
168+
printf("%u\n", (uint32_t)(result & 0xFFFFFFFF));
169+
}

0 commit comments

Comments
 (0)