Skip to content

Commit 75cb7de

Browse files
earnolEänolituri LómitaurëAaronBallmanilovepi
authored
[ubsan] Display correct runtime messages for negative _BitInt (#96240)
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 #64100. Co-authored-by: Eänolituri Lómitaurë <[email protected]> Co-authored-by: Aaron Ballman <[email protected]> Co-authored-by: Paul Kirth <[email protected]>
1 parent aaab4fc commit 75cb7de

File tree

6 files changed

+404
-13
lines changed

6 files changed

+404
-13
lines changed

clang/lib/CodeGen/CGExpr.cpp

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

69+
//===--------------------------------------------------------------------===//
70+
// Defines for metadata
71+
//===--------------------------------------------------------------------===//
72+
73+
// Those values are crucial to be the SAME as in ubsan runtime library.
74+
enum VariableTypeDescriptorKind : uint16_t {
75+
/// An integer type.
76+
TK_Integer = 0x0000,
77+
/// A floating-point type.
78+
TK_Float = 0x0001,
79+
/// An _BitInt(N) type.
80+
TK_BitInt = 0x0002,
81+
/// Any other type. The value representation is unspecified.
82+
TK_Unknown = 0xffff
83+
};
84+
6885
//===--------------------------------------------------------------------===//
6986
// Miscellaneous Helper Methods
7087
//===--------------------------------------------------------------------===//
@@ -3288,22 +3305,40 @@ LValue CodeGenFunction::EmitPredefinedLValue(const PredefinedExpr *E) {
32883305
/// { i16 TypeKind, i16 TypeInfo }
32893306
/// \endcode
32903307
///
3291-
/// followed by an array of i8 containing the type name. TypeKind is 0 for an
3292-
/// integer, 1 for a floating point value, and -1 for anything else.
3308+
/// followed by an array of i8 containing the type name with extra information
3309+
/// for BitInt. TypeKind is TK_Integer(0) for an integer, TK_Float(1) for a
3310+
/// floating point value, TK_BitInt(2) for BitInt and TK_Unknown(0xFFFF) for
3311+
/// anything else.
32933312
llvm::Constant *CodeGenFunction::EmitCheckTypeDescriptor(QualType T) {
32943313
// Only emit each type's descriptor once.
32953314
if (llvm::Constant *C = CGM.getTypeDescriptorFromMap(T))
32963315
return C;
32973316

3298-
uint16_t TypeKind = -1;
3317+
uint16_t TypeKind = TK_Unknown;
32993318
uint16_t TypeInfo = 0;
3319+
bool IsBitInt = false;
33003320

33013321
if (T->isIntegerType()) {
3302-
TypeKind = 0;
3322+
TypeKind = TK_Integer;
33033323
TypeInfo = (llvm::Log2_32(getContext().getTypeSize(T)) << 1) |
33043324
(T->isSignedIntegerType() ? 1 : 0);
3325+
// Follow suggestion from discussion of issue 64100.
3326+
// So we can write the exact amount of bits in TypeName after '\0'
3327+
// making it <diagnostic-like type name>.'\0'.<32-bit width>.
3328+
if (T->isSignedIntegerType() && T->getAs<BitIntType>()) {
3329+
// Do a sanity checks as we are using 32-bit type to store bit length.
3330+
assert(getContext().getTypeSize(T) > 0 &&
3331+
" non positive amount of bits in __BitInt type");
3332+
assert(getContext().getTypeSize(T) <= 0xFFFFFFFF &&
3333+
" too many bits in __BitInt type");
3334+
3335+
// Redefine TypeKind with the actual __BitInt type if we have signed
3336+
// BitInt.
3337+
TypeKind = TK_BitInt;
3338+
IsBitInt = true;
3339+
}
33053340
} else if (T->isFloatingType()) {
3306-
TypeKind = 1;
3341+
TypeKind = TK_Float;
33073342
TypeInfo = getContext().getTypeSize(T);
33083343
}
33093344

@@ -3314,6 +3349,20 @@ llvm::Constant *CodeGenFunction::EmitCheckTypeDescriptor(QualType T) {
33143349
DiagnosticsEngine::ak_qualtype, (intptr_t)T.getAsOpaquePtr(), StringRef(),
33153350
StringRef(), std::nullopt, Buffer, std::nullopt);
33163351

3352+
if (IsBitInt) {
3353+
// The Structure is: 0 to end the string, 32 bit unsigned integer in target
3354+
// endianness, zero.
3355+
char S[6] = {'\0', '\0', '\0', '\0', '\0', '\0'};
3356+
const auto *EIT = T->castAs<BitIntType>();
3357+
uint32_t Bits = EIT->getNumBits();
3358+
llvm::support::endian::write32(S + 1, Bits,
3359+
getTarget().isBigEndian()
3360+
? llvm::endianness::big
3361+
: llvm::endianness::little);
3362+
StringRef Str = StringRef(S, sizeof(S) / sizeof(decltype(S[0])));
3363+
Buffer.append(Str);
3364+
}
3365+
33173366
llvm::Constant *Components[] = {
33183367
Builder.getInt16(TypeKind), Builder.getInt16(TypeInfo),
33193368
llvm::ConstantDataArray::getString(getLLVMContext(), Buffer)

clang/test/CodeGen/bit-int-ubsan.c

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// REQUIRES: x86-registered-target
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
3+
4+
// The runtime test checking the _BitInt ubsan feature is located in compiler-rt/test/ubsan/TestCases/Integer/bit-int.c
5+
6+
#include <stdint.h>
7+
#include <stdio.h>
8+
9+
uint32_t float_divide_by_zero() {
10+
float f = 1.0f / 0.0f;
11+
// CHECK: constant { i16, i16, [8 x i8] } { i16 1, i16 32, [8 x i8] c"'float'\00" }
12+
_BitInt(37) r = (_BitInt(37))f;
13+
// CHECK: constant { i16, i16, [20 x i8] } { i16 2, i16 13, [20 x i8] c"'_BitInt(37)'\00%\00\00\00\00\00" }
14+
return r;
15+
}
16+
17+
uint32_t integer_divide_by_zero() __attribute__((no_sanitize("memory"))) {
18+
_BitInt(37) x = 1 / 0;
19+
// CHECK: 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: constant { i16, i16, [23 x i8] } { i16 0, i16 12, [23 x i8] c"'unsigned _BitInt(37)'\00" }
29+
uint32_t r = x & 0xFFFFFFFF;
30+
return r;
31+
}
32+
33+
uint32_t array_bounds() {
34+
_BitInt(37) x[4];
35+
_BitInt(37) y = x[10];
36+
// CHECK: constant { i16, i16, [17 x i8] } { i16 -1, i16 0, [17 x i8] c"'_BitInt(37)[4]'\00" }
37+
return (uint32_t)y;
38+
}
39+
40+
uint32_t float_cast_overflow() {
41+
float a = 100000000.0f;
42+
_BitInt(7) b = (_BitInt(7))a;
43+
// CHECK: constant { i16, i16, [19 x i8] } { i16 2, i16 7, [19 x i8] c"'_BitInt(7)'\00\07\00\00\00\00\00" }
44+
return b;
45+
}
46+
47+
_BitInt(13) implicit_signed_integer_truncation() {
48+
_BitInt(73) x = (_BitInt(73)) ~((~0UL) >> 1);
49+
return x;
50+
// CHECK: constant { i16, i16, [20 x i8] } { i16 2, i16 {{([[:xdigit:]]{2})}}, [20 x i8] c"'_BitInt(73)'\00I\00\00\00\00\00" }
51+
// CHECK: constant { i16, i16, [20 x i8] } { i16 2, i16 9, [20 x i8] c"'_BitInt(13)'\00\0D\00\00\00\00\00" }
52+
}
53+
54+
uint32_t negative_shift1(unsigned _BitInt(37) x)
55+
__attribute__((no_sanitize("memory"))) {
56+
_BitInt(9) c = -2;
57+
return x >> c;
58+
// CHECK: constant { i16, i16, [19 x i8] } { i16 2, i16 9, [19 x i8] c"'_BitInt(9)'\00\09\00\00\00\00\00" }
59+
}
60+
61+
uint32_t negative_shift2(unsigned _BitInt(37) x)
62+
__attribute__((no_sanitize("memory"))) {
63+
_BitInt(17) c = -2;
64+
return x >> c;
65+
// CHECK: constant { i16, i16, [20 x i8] } { i16 2, i16 11, [20 x i8] c"'_BitInt(17)'\00\11\00\00\00\00\00" }
66+
}
67+
68+
uint32_t negative_shift3(unsigned _BitInt(37) x)
69+
__attribute__((no_sanitize("memory"))) {
70+
_BitInt(34) c = -2;
71+
return x >> c;
72+
// CHECK: constant { i16, i16, [20 x i8] } { i16 2, i16 13, [20 x i8] c"'_BitInt(34)'\00\22\00\00\00\00\00" }
73+
}
74+
75+
uint32_t negative_shift5(unsigned _BitInt(37) x)
76+
__attribute__((no_sanitize("memory"))) {
77+
_BitInt(68) c = -2;
78+
return x >> c;
79+
// CHECK: constant { i16, i16, [20 x i8] } { i16 2, i16 {{([[:xdigit:]]{2})}}, [20 x i8] c"'_BitInt(68)'\00D\00\00\00\00\00" }
80+
}
81+
82+
int main(int argc, char **argv) {
83+
// clang-format off
84+
uint64_t result =
85+
1ULL +
86+
implicit_unsigned_integer_truncation() +
87+
(uint32_t)array_bounds() +
88+
float_cast_overflow() +
89+
(uint64_t)implicit_signed_integer_truncation() +
90+
negative_shift1(5) +
91+
negative_shift2(5) +
92+
negative_shift3(5) +
93+
negative_shift5(5);
94+
// clang-format on
95+
printf("%u\n", (uint32_t)(result & 0xFFFFFFFF));
96+
}

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: 32 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,25 @@ class TypeDescriptor {
125137
return 1 << (TypeInfo >> 1);
126138
}
127139

140+
const char *getBitIntBitCountPointer() const {
141+
DCHECK(isBitIntTy());
142+
DCHECK(isSignedBitIntTy());
143+
// Scan Name for zero and return the next address
144+
const char *p = getTypeName();
145+
while (*p != '\0')
146+
++p;
147+
// Return the next address
148+
return p + 1;
149+
}
150+
151+
unsigned getIntegerBitCount() const {
152+
DCHECK(isIntegerTy());
153+
if (isSignedBitIntTy())
154+
return *reinterpret_cast<const u32 *>(getBitIntBitCountPointer());
155+
else
156+
return getIntegerBitWidth();
157+
}
158+
128159
bool isFloatTy() const { return getKind() == TK_Float; }
129160
unsigned getFloatBitWidth() const {
130161
CHECK(isFloatTy());
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
__attribute__((no_sanitize("memory"))) {
10+
_BitInt(37) y = *(_BitInt(37) *)&x;
11+
return (y > 0) ? y : 0;
12+
}
13+
14+
// In this test there is an expectation of ubsan not triggeting on returning random address which is inside address space of the process.
15+
_BitInt(37) nonnull_attribute(__attribute__((nonnull)) _BitInt(37) * x)
16+
__attribute__((no_sanitize("address")))
17+
__attribute__((no_sanitize("memory"))) {
18+
return *(_BitInt(37) *)&x;
19+
}
20+
21+
// In this test there is an expectation of assignment of uint32_t from "invalid" _BitInt is not producing any output.
22+
uint32_t nullability_assign(_BitInt(7) * x)
23+
__attribute__((no_sanitize("address")))
24+
__attribute__((no_sanitize("memory"))) {
25+
_BitInt(7) *_Nonnull y = x;
26+
int32_t r = *(_BitInt(7) *)&y;
27+
return (r > 0) ? r : 0;
28+
}
29+
30+
// In those examples the file is expected to compile & run with no diagnostics
31+
// CHECK-NOT: runtime error:
32+
33+
int main(int argc, char **argv) {
34+
// clang-format off
35+
uint64_t result =
36+
1ULL +
37+
nullability_arg((_BitInt(37) *)argc) +
38+
((uint64_t)nonnull_attribute((_BitInt(37) *)argc) & 0xFFFFFFFF) +
39+
nullability_assign((_BitInt(7) *)argc);
40+
// clang-format on
41+
printf("%u\n", (uint32_t)(result & 0xFFFFFFFF));
42+
}

0 commit comments

Comments
 (0)