Skip to content

Commit a128cd4

Browse files
author
Eänolituri Lómitaurë
committed
[ubsan] Display correct runtime messages for negative _BitInt
Without this patch compiler-rt ubsan library has a bug displaying incorrect values for variables of the _BitInt (previousely called _ExtInt) type. This patch 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.
1 parent 5a23d31 commit a128cd4

File tree

4 files changed

+283
-13
lines changed

4 files changed

+283
-13
lines changed

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 51 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,20 @@ 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+
enum VariableTypeDescriptorKind : uint16_t {
72+
/// An integer type.
73+
TK_Integer = 0x0000,
74+
/// A floating-point type.
75+
TK_Float = 0x0001,
76+
/// An _BitInt(N) type.
77+
TK_BitInt = 0x0002,
78+
/// Any other type. The value representation is unspecified.
79+
TK_Unknown = 0xffff
80+
};
81+
6782
//===--------------------------------------------------------------------===//
6883
// Miscellaneous Helper Methods
6984
//===--------------------------------------------------------------------===//
@@ -3292,22 +3307,39 @@ LValue CodeGenFunction::EmitPredefinedLValue(const PredefinedExpr *E) {
32923307
/// { i16 TypeKind, i16 TypeInfo }
32933308
/// \endcode
32943309
///
3295-
/// followed by an array of i8 containing the type name. TypeKind is 0 for an
3296-
/// integer, 1 for a floating point value, and -1 for anything else.
3310+
/// followed by an array of i8 containing the type name with extra information
3311+
/// for BitInt. TypeKind is 0 for an integer, 1 for a floating point value, 2
3312+
/// for BitInt and -1 for anything else.
32973313
llvm::Constant *CodeGenFunction::EmitCheckTypeDescriptor(QualType T) {
32983314
// Only emit each type's descriptor once.
32993315
if (llvm::Constant *C = CGM.getTypeDescriptorFromMap(T))
33003316
return C;
33013317

3302-
uint16_t TypeKind = -1;
3318+
uint16_t TypeKind = TK_Unknown;
33033319
uint16_t TypeInfo = 0;
3320+
bool IsBitInt = false;
33043321

33053322
if (T->isIntegerType()) {
3306-
TypeKind = 0;
3323+
TypeKind = TK_Integer;
33073324
TypeInfo = (llvm::Log2_32(getContext().getTypeSize(T)) << 1) |
33083325
(T->isSignedIntegerType() ? 1 : 0);
3326+
// Follow suggestion from https://github.com/llvm/llvm-project/issues/64100
3327+
// So we can write the exact amount of bits in TypeName after '\0'
3328+
// making it <diagnostic-like type name>.'\0'.<32-bit width>.
3329+
if (T->isSignedIntegerType() && T->getAs<BitIntType>()) {
3330+
// Do a sanity checks as we are using 32-bit type to store bit length.
3331+
assert((getContext().getTypeSize(T) > 0) &&
3332+
" non positive amount of bits in __BitInt type");
3333+
assert((getContext().getTypeSize(T) <= 0xFFFFFFFF) &&
3334+
" too many bits in __BitInt type");
3335+
3336+
// Redefine TypeKind with the actual __BitInt type if we have signed
3337+
// BitInt.
3338+
TypeKind = TK_BitInt;
3339+
IsBitInt = true;
3340+
}
33093341
} else if (T->isFloatingType()) {
3310-
TypeKind = 1;
3342+
TypeKind = TK_Float;
33113343
TypeInfo = getContext().getTypeSize(T);
33123344
}
33133345

@@ -3318,6 +3350,20 @@ llvm::Constant *CodeGenFunction::EmitCheckTypeDescriptor(QualType T) {
33183350
DiagnosticsEngine::ak_qualtype, (intptr_t)T.getAsOpaquePtr(), StringRef(),
33193351
StringRef(), std::nullopt, Buffer, std::nullopt);
33203352

3353+
if (IsBitInt) {
3354+
// The Structure is: 0 to end the string, 32 bit insigned integer in target
3355+
// endianness, zero.
3356+
char s[6] = {'\0', '\0', '\0', '\0', '\0', '\0'};
3357+
const auto *EIT = T->getAs<BitIntType>();
3358+
uint32_t Bits = EIT->getNumBits();
3359+
llvm::support::endian::write32(s + 1, Bits,
3360+
getTarget().isBigEndian()
3361+
? llvm::endianness::big
3362+
: llvm::endianness::little);
3363+
StringRef str = StringRef(s, 6);
3364+
Buffer.append(str);
3365+
}
3366+
33213367
llvm::Constant *Components[] = {
33223368
Builder.getInt16(TypeKind), Builder.getInt16(TypeInfo),
33233369
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: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,14 @@ 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
108+
/// value. Remaining bits are log_2(bit_width). The value representation is
109+
/// the integer itself if it fits into a ValueHandle, and a pointer to the
110+
/// integer otherwise. TypeName contains the true width of the type for the
111+
/// signed _BitInt(N) type stored after zero bit after TypeName as 32-bit
112+
// unsigned integer.
113+
TK_BitInt = 0x0002,
106114
/// Any other type. The value representation is unspecified.
107115
TK_Unknown = 0xffff
108116
};
@@ -113,10 +121,15 @@ class TypeDescriptor {
113121
return static_cast<Kind>(TypeKind);
114122
}
115123

116-
bool isIntegerTy() const { return getKind() == TK_Integer; }
124+
bool isIntegerTy() const {
125+
return getKind() == TK_Integer || getKind() == TK_BitInt;
126+
}
127+
bool isBitIntTy() const { return getKind() == TK_BitInt; }
128+
117129
bool isSignedIntegerTy() const {
118130
return isIntegerTy() && (TypeInfo & 1);
119131
}
132+
bool isSignedBitIntTy() const { return isBitIntTy() && (TypeInfo & 1); }
120133
bool isUnsignedIntegerTy() const {
121134
return isIntegerTy() && !(TypeInfo & 1);
122135
}
@@ -125,6 +138,26 @@ class TypeDescriptor {
125138
return 1 << (TypeInfo >> 1);
126139
}
127140

141+
const char *getBitIntBitCountPointer() const {
142+
CHECK(isBitIntTy());
143+
CHECK(isSignedBitIntTy());
144+
// Scan Name for zero and return the next address
145+
const char *p = getTypeName();
146+
while (*p != '\0') {
147+
p++;
148+
}
149+
// Return the next address
150+
return p + 1;
151+
}
152+
153+
unsigned getIntegerBitCount() const {
154+
CHECK(isIntegerTy());
155+
if (isSignedBitIntTy())
156+
return *reinterpret_cast<const u32 *>(getBitIntBitCountPointer());
157+
else
158+
return getIntegerBitWidth();
159+
}
160+
128161
bool isFloatTy() const { return getKind() == TK_Float; }
129162
unsigned getFloatBitWidth() const {
130163
CHECK(isFloatTy());
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
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=CHECK-R
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=CHECK-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+
// CHECK-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+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:19: runtime error: inf is outside the range of representable values of type
12+
// CHECK-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+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:21: runtime error: division by zero
19+
// CHECK-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+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:9: runtime error: unsigned integer overflow:
29+
// CHECK-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+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:22: runtime error: pointer index expression with base
38+
uint32_t r = *(_BitInt(37) *)&y;
39+
// CHECK-R: {{.*}}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+
// CHECK-R: {{.*}}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 nullability_arg(_BitInt(37) *_Nonnull x)
50+
__attribute__((no_sanitize("address"))) {
51+
_BitInt(37) y = *(_BitInt(37) *)&x;
52+
return y;
53+
}
54+
55+
uint32_t unsigned_shift_base() {
56+
unsigned _BitInt(37) x = ~0U << 1;
57+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:32: runtime error: left shift of 4294967295 by 1 places cannot be represented in type
58+
return x;
59+
}
60+
61+
uint32_t array_bounds() {
62+
_BitInt(37) x[4];
63+
_BitInt(37) y = x[10];
64+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:19: runtime error: index 10 out of bounds for type
65+
// CHECK-IR: constant { i16, i16, [17 x i8] } { i16 -1, i16 0, [17 x i8] c"'_BitInt(37)[4]'\00" }
66+
return (uint32_t)y;
67+
}
68+
69+
uint32_t float_cast_overflow() {
70+
float a = 100000000.0f;
71+
_BitInt(7) b = (_BitInt(7))a;
72+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:18: runtime error: 1e+08 is outside the range of representable values of type
73+
// CHECK-IR: constant { i16, i16, [19 x i8] } { i16 2, i16 7, [19 x i8] c"'_BitInt(7)'\00\07\00\00\00\00\00" }
74+
return b;
75+
}
76+
77+
uint32_t implicit_integer_sign_change(unsigned _BitInt(37) x) {
78+
_BitInt(37) r = x;
79+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:19: runtime error: implicit conversion from type '{{[^']+}}' of value
80+
return r & 0xFFFFFFFF;
81+
}
82+
83+
_BitInt(13) implicit_signed_integer_truncation() {
84+
_BitInt(73) x = (_BitInt(73)) ~((~0UL) >> 1);
85+
return x;
86+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:10: runtime error: implicit conversion from type
87+
// CHECK-IR: constant { i16, i16, [20 x i8] } { i16 2, i16 {{([[:xdigit:]]{2})}}, [20 x i8] c"'_BitInt(73)'\00I\00\00\00\00\00" }
88+
// CHECK-IR: constant { i16, i16, [20 x i8] } { i16 2, i16 9, [20 x i8] c"'_BitInt(13)'\00\0D\00\00\00\00\00" }
89+
}
90+
91+
_BitInt(37) nonnull_attribute(__attribute__((nonnull)) _BitInt(37) * x)
92+
__attribute__((no_sanitize("address"))) {
93+
return *(_BitInt(37) *)&x;
94+
}
95+
96+
uint32_t nullability_assign(_BitInt(37) * x)
97+
__attribute__((no_sanitize("address"))) {
98+
_BitInt(37) *_Nonnull y = x;
99+
uint32_t r = *(_BitInt(37) *)&y;
100+
return r;
101+
}
102+
103+
_BitInt(37) shift_exponent() {
104+
_BitInt(37) x = 1 << (-1);
105+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:21: runtime error: shift exponent -1 is negative
106+
return x;
107+
}
108+
109+
_BitInt(37) shift_base() {
110+
_BitInt(37) x = (-1) << 1;
111+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:24: runtime error: left shift of negative value -1
112+
return x;
113+
}
114+
115+
uint32_t negative_shift1(unsigned _BitInt(37) x) {
116+
_BitInt(9) c = -2;
117+
return x >> c;
118+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:12: runtime error: shift exponent -2 is negative
119+
// CHECK-IR: constant { i16, i16, [19 x i8] } { i16 2, i16 9, [19 x i8] c"'_BitInt(9)'\00\09\00\00\00\00\00" }
120+
}
121+
122+
uint32_t negative_shift2(unsigned _BitInt(37) x) {
123+
_BitInt(17) c = -2;
124+
return x >> c;
125+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:12: runtime error: shift exponent -2 is negative
126+
// CHECK-IR: constant { i16, i16, [20 x i8] } { i16 2, i16 11, [20 x i8] c"'_BitInt(17)'\00\11\00\00\00\00\00" }
127+
}
128+
129+
uint32_t negative_shift3(unsigned _BitInt(37) x) {
130+
_BitInt(34) c = -2;
131+
return x >> c;
132+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:12: runtime error: shift exponent -2 is negative
133+
// CHECK-IR: constant { i16, i16, [20 x i8] } { i16 2, i16 13, [20 x i8] c"'_BitInt(34)'\00\22\00\00\00\00\00" }
134+
}
135+
136+
uint32_t negative_shift4(unsigned _BitInt(37) x) {
137+
int64_t c = -2;
138+
return x >> c;
139+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:12: runtime error: shift exponent -2 is negative
140+
}
141+
142+
uint32_t negative_shift5(unsigned _BitInt(37) x) {
143+
_BitInt(68) c = -2;
144+
return x >> c;
145+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:12: runtime error: shift exponent -2 is negative
146+
// 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" }
147+
}
148+
149+
uint32_t unsigned_integer_overflow() {
150+
unsigned _BitInt(37) x = ~0U;
151+
x++;
152+
return x;
153+
// CHECK-R: {{.*}}bit-int.c:[[@LINE-1]]:10: runtime error: implicit conversion from type
154+
}
155+
156+
uint32_t signed_integer_overflow() {
157+
_BitInt(37) x = (_BitInt(37)) ~((~0U) >> 1);
158+
x--;
159+
return x;
160+
}
161+
162+
int main(int argc, char **argv) {
163+
// clang-format off
164+
uint64_t result =
165+
1ULL +
166+
implicit_unsigned_integer_truncation() +
167+
pointer_overflow() +
168+
vla_bound(argc) +
169+
nullability_arg((_BitInt(37) *)argc) +
170+
unsigned_shift_base() +
171+
(uint32_t)array_bounds() +
172+
float_cast_overflow() +
173+
implicit_integer_sign_change((unsigned _BitInt(37))(argc - 2)) +
174+
(uint64_t)implicit_signed_integer_truncation() +
175+
((uint64_t)nonnull_attribute((_BitInt(37) *)argc) & 0xFFFFFFFF) +
176+
nullability_assign((_BitInt(37) *)argc) +
177+
shift_exponent() +
178+
(uint32_t)shift_base() +
179+
negative_shift1(5) +
180+
negative_shift2(5) +
181+
negative_shift3(5) +
182+
negative_shift4(5) +
183+
negative_shift5(5) +
184+
unsigned_integer_overflow() +
185+
signed_integer_overflow();
186+
// clang-format on
187+
printf("%u\n", (uint32_t)(result & 0xFFFFFFFF));
188+
}

0 commit comments

Comments
 (0)