Skip to content

Commit 73a92d1

Browse files
committed
[clang][UBSan] Add implicit conversion check for bitfields
This patch implements the implicit truncation and implicit sign change checks for bitfields. However, right now some unnecessary emits are generated which ideally would be removed in the future.
1 parent 8b76571 commit 73a92d1

File tree

5 files changed

+293
-9
lines changed

5 files changed

+293
-9
lines changed

clang/lib/CodeGen/CGExprScalar.cpp

Lines changed: 258 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "CGDebugInfo.h"
1616
#include "CGObjCRuntime.h"
1717
#include "CGOpenMPRuntime.h"
18+
#include "CGRecordLayout.h"
1819
#include "CodeGenFunction.h"
1920
#include "CodeGenModule.h"
2021
#include "ConstantEmitter.h"
@@ -324,6 +325,25 @@ class ScalarExprEmitter
324325
void EmitIntegerSignChangeCheck(Value *Src, QualType SrcType, Value *Dst,
325326
QualType DstType, SourceLocation Loc);
326327

328+
/// Emit a check that an [implicit] truncation of a bitfield does not
329+
/// discard any bits. It is not UB, so we use the value after truncation.
330+
void EmitBitfieldTruncationCheck(Value *Src, QualType SrcType, Value *Dst,
331+
QualType DstType, const CGBitFieldInfo &Info,
332+
SourceLocation Loc);
333+
334+
/// Emit a check that an [implicit] conversion of a bitfield does not change
335+
/// the sign of the value. It is not UB, so we use the value after conversion.
336+
/// NOTE: Src and Dst may be the exact same value! (point to the same thing)
337+
void EmitBitfieldSignChangeCheck(Value *Src, QualType SrcType, Value *Dst,
338+
QualType DstType, const CGBitFieldInfo &Info,
339+
SourceLocation Loc);
340+
341+
/// Emit a check that an [implicit] conversion of a bitfield. It is not UB,
342+
/// so we use the value after conversion.
343+
void EmitBitfieldConversionCheck(Value *Src, QualType SrcType, Value *Dst,
344+
QualType DstType, const CGBitFieldInfo &Info,
345+
SourceLocation Loc);
346+
327347
/// Emit a conversion from the specified type to the specified destination
328348
/// type, both of which are LLVM scalar types.
329349
struct ScalarConversionOpts {
@@ -1239,6 +1259,221 @@ void ScalarExprEmitter::EmitIntegerSignChangeCheck(Value *Src, QualType SrcType,
12391259
{Src, Dst});
12401260
}
12411261

1262+
// Should be called within CodeGenFunction::SanitizerScope RAII scope.
1263+
// Returns 'i1 false' when the truncation Src -> Dst was lossy.
1264+
static std::pair<ScalarExprEmitter::ImplicitConversionCheckKind,
1265+
std::pair<llvm::Value *, SanitizerMask>>
1266+
EmitBitfieldTruncationCheckHelper(Value *Src, QualType SrcType, Value *Dst,
1267+
QualType DstType, CGBuilderTy &Builder) {
1268+
1269+
llvm::Type *SrcTy = Src->getType();
1270+
llvm::Type *DstTy = Dst->getType();
1271+
(void)SrcTy; // Only used in assert()
1272+
(void)DstTy; // Only used in assert()
1273+
1274+
// This should be truncation of integral types.
1275+
assert(isa<llvm::IntegerType>(SrcTy) && isa<llvm::IntegerType>(DstTy) &&
1276+
"non-integer llvm type");
1277+
1278+
bool SrcSigned = SrcType->isSignedIntegerOrEnumerationType();
1279+
bool DstSigned = DstType->isSignedIntegerOrEnumerationType();
1280+
1281+
ScalarExprEmitter::ImplicitConversionCheckKind Kind;
1282+
SanitizerMask Mask;
1283+
if (!SrcSigned && !DstSigned) {
1284+
Kind = ScalarExprEmitter::ICCK_UnsignedIntegerTruncation;
1285+
Mask = SanitizerKind::ImplicitUnsignedIntegerTruncation;
1286+
} else {
1287+
Kind = ScalarExprEmitter::ICCK_SignedIntegerTruncation;
1288+
Mask = SanitizerKind::ImplicitSignedIntegerTruncation;
1289+
}
1290+
1291+
// Since Src already has the same type as Dst, we don't have
1292+
// to sign extend or truncate the Src value.
1293+
llvm::Value *CheckV = Builder.CreateICmpEQ(Dst, Src, "bf.truncheck");
1294+
// If the comparison result is 'i1 false', then the truncation was lossy.
1295+
1296+
return std::make_pair(Kind, std::make_pair(CheckV, Mask));
1297+
}
1298+
1299+
void ScalarExprEmitter::EmitBitfieldTruncationCheck(
1300+
Value *Src, QualType SrcType, Value *Dst, QualType DstType,
1301+
const CGBitFieldInfo &Info, SourceLocation Loc) {
1302+
1303+
if (!CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation))
1304+
return;
1305+
1306+
// TODO: Calculate src width to avoid emitting code
1307+
// for unecessary cases.
1308+
unsigned SrcBits = ConvertType(SrcType)->getScalarSizeInBits();
1309+
unsigned BitfieldWidth = Info.Size;
1310+
// This must be truncation. Else we do not care.
1311+
if (SrcBits <= BitfieldWidth)
1312+
return;
1313+
1314+
// If the integer sign change sanitizer is enabled,
1315+
// and we are truncating from larger unsigned type to smaller signed type,
1316+
// let that next sanitizer deal with it.
1317+
bool SrcSigned = SrcType->isSignedIntegerOrEnumerationType();
1318+
bool DstSigned = DstType->isSignedIntegerOrEnumerationType();
1319+
if (CGF.SanOpts.has(SanitizerKind::ImplicitIntegerSignChange) &&
1320+
(!SrcSigned && DstSigned))
1321+
return;
1322+
1323+
CodeGenFunction::SanitizerScope SanScope(&CGF);
1324+
1325+
std::pair<ScalarExprEmitter::ImplicitConversionCheckKind,
1326+
std::pair<llvm::Value *, SanitizerMask>>
1327+
Check = EmitBitfieldTruncationCheckHelper(Src, SrcType, Dst, DstType,
1328+
Builder);
1329+
// If the comparison result is 'i1 false', then the truncation was lossy.
1330+
1331+
llvm::Constant *StaticArgs[] = {
1332+
CGF.EmitCheckSourceLocation(Loc), CGF.EmitCheckTypeDescriptor(SrcType),
1333+
CGF.EmitCheckTypeDescriptor(DstType),
1334+
llvm::ConstantInt::get(Builder.getInt8Ty(), Check.first),
1335+
llvm::ConstantInt::get(Builder.getInt32Ty(), Info.Size)};
1336+
CGF.EmitCheck(Check.second, SanitizerHandler::ImplicitConversion, StaticArgs,
1337+
{Src, Dst});
1338+
}
1339+
1340+
// Should be called within CodeGenFunction::SanitizerScope RAII scope.
1341+
// Returns 'i1 false' when the conversion Src -> Dst changed the sign.
1342+
static std::pair<ScalarExprEmitter::ImplicitConversionCheckKind,
1343+
std::pair<llvm::Value *, SanitizerMask>>
1344+
EmitBitfieldSignChangeCheckHelper(Value *Src, QualType SrcType,
1345+
unsigned SrcBits, Value *Dst,
1346+
QualType DstType, unsigned DstBits,
1347+
CGBuilderTy &Builder) {
1348+
llvm::Type *SrcTy = Src->getType();
1349+
llvm::Type *DstTy = Dst->getType();
1350+
(void)SrcTy; // Only used in assert()
1351+
(void)DstTy; // Only used in assert()
1352+
1353+
// This should be truncation of integral types.
1354+
assert(isa<llvm::IntegerType>(SrcTy) && isa<llvm::IntegerType>(DstTy) &&
1355+
"non-integer llvm type");
1356+
1357+
bool SrcSigned = SrcType->isSignedIntegerOrEnumerationType();
1358+
bool DstSigned = DstType->isSignedIntegerOrEnumerationType();
1359+
1360+
assert(((SrcBits != DstBits) || (SrcSigned != DstSigned)) &&
1361+
"either the widths should be different, or the signednesses.");
1362+
1363+
// 1. Was the old Value negative?
1364+
llvm::Value *SrcIsNegative =
1365+
EmitIsNegativeTestHelper(Src, SrcType, "bf.src", Builder);
1366+
// 2. Is the new Value negative?
1367+
llvm::Value *DstIsNegative =
1368+
EmitIsNegativeTestHelper(Dst, DstType, "bf.dst", Builder);
1369+
// 3. Now, was the 'negativity status' preserved during the conversion?
1370+
// NOTE: conversion from negative to zero is considered to change the sign.
1371+
// (We want to get 'false' when the conversion changed the sign)
1372+
// So we should just equality-compare the negativity statuses.
1373+
llvm::Value *Check = nullptr;
1374+
Check =
1375+
Builder.CreateICmpEQ(SrcIsNegative, DstIsNegative, "bf.signchangecheck");
1376+
// If the comparison result is 'false', then the conversion changed the sign.
1377+
return std::make_pair(
1378+
ScalarExprEmitter::ICCK_IntegerSignChange,
1379+
std::make_pair(Check, SanitizerKind::ImplicitIntegerSignChange));
1380+
}
1381+
1382+
void ScalarExprEmitter::EmitBitfieldSignChangeCheck(
1383+
Value *Src, QualType SrcType, Value *Dst, QualType DstType,
1384+
const CGBitFieldInfo &Info, SourceLocation Loc) {
1385+
1386+
if (!CGF.SanOpts.has(SanitizerKind::ImplicitIntegerSignChange))
1387+
return;
1388+
1389+
bool SrcSigned = SrcType->isSignedIntegerOrEnumerationType();
1390+
bool DstSigned = DstType->isSignedIntegerOrEnumerationType();
1391+
unsigned SrcBits = ConvertType(SrcType)->getScalarSizeInBits();
1392+
unsigned DstBits = Info.Size;
1393+
1394+
// Now, we do not need to emit the check in *all* of the cases.
1395+
// We can avoid emitting it in some obvious cases where it would have been
1396+
// dropped by the opt passes (instcombine) always anyways.
1397+
// If it's a cast between effectively the same type, no check.
1398+
// NOTE: this is *not* equivalent to checking the canonical types.
1399+
if (SrcSigned == DstSigned && SrcBits == DstBits)
1400+
return;
1401+
// At least one of the values needs to have signed type.
1402+
// If both are unsigned, then obviously, neither of them can be negative.
1403+
if (!SrcSigned && !DstSigned)
1404+
return;
1405+
// If the conversion is to *larger* *signed* type, then no check is needed.
1406+
// Because either sign-extension happens (so the sign will remain),
1407+
// or zero-extension will happen (the sign bit will be zero.)
1408+
if ((DstBits > SrcBits) && DstSigned)
1409+
return;
1410+
if (CGF.SanOpts.has(SanitizerKind::ImplicitSignedIntegerTruncation) &&
1411+
(SrcBits > DstBits) && SrcSigned) {
1412+
// If the signed integer truncation sanitizer is enabled,
1413+
// and this is a truncation from signed type, then no check is needed.
1414+
// Because here sign change check is interchangeable with truncation check.
1415+
return;
1416+
}
1417+
// That's it. We can't rule out any more cases with the data we have.
1418+
1419+
CodeGenFunction::SanitizerScope SanScope(&CGF);
1420+
1421+
std::pair<ScalarExprEmitter::ImplicitConversionCheckKind,
1422+
std::pair<llvm::Value *, SanitizerMask>>
1423+
Check;
1424+
1425+
// Each of these checks needs to return 'false' when an issue was detected.
1426+
ImplicitConversionCheckKind CheckKind;
1427+
llvm::SmallVector<std::pair<llvm::Value *, SanitizerMask>, 2> Checks;
1428+
// So we can 'and' all the checks together, and still get 'false',
1429+
// if at least one of the checks detected an issue.
1430+
1431+
Check = EmitBitfieldSignChangeCheckHelper(Src, SrcType, SrcBits, Dst, DstType,
1432+
DstBits, Builder);
1433+
CheckKind = Check.first;
1434+
Checks.emplace_back(Check.second);
1435+
1436+
if (CGF.SanOpts.has(SanitizerKind::ImplicitSignedIntegerTruncation) &&
1437+
(SrcBits > DstBits) && !SrcSigned && DstSigned) {
1438+
// If the signed integer truncation sanitizer was enabled,
1439+
// and we are truncating from larger unsigned type to smaller signed type,
1440+
// let's handle the case we skipped in that check.
1441+
Check =
1442+
EmitBitfieldTruncationCheckHelper(Src, SrcType, Dst, DstType, Builder);
1443+
CheckKind = ICCK_SignedIntegerTruncationOrSignChange;
1444+
Checks.emplace_back(Check.second);
1445+
// If the comparison result is 'i1 false', then the truncation was lossy.
1446+
}
1447+
1448+
llvm::Constant *StaticArgs[] = {
1449+
CGF.EmitCheckSourceLocation(Loc), CGF.EmitCheckTypeDescriptor(SrcType),
1450+
CGF.EmitCheckTypeDescriptor(DstType),
1451+
llvm::ConstantInt::get(Builder.getInt8Ty(), CheckKind),
1452+
llvm::ConstantInt::get(Builder.getInt32Ty(), Info.Size)};
1453+
// EmitCheck() will 'and' all the checks together.
1454+
CGF.EmitCheck(Checks, SanitizerHandler::ImplicitConversion, StaticArgs,
1455+
{Src, Dst});
1456+
}
1457+
1458+
void ScalarExprEmitter::EmitBitfieldConversionCheck(
1459+
Value *Src, QualType SrcType, Value *Dst, QualType DstType,
1460+
const CGBitFieldInfo &Info, SourceLocation Loc) {
1461+
1462+
if (!CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitConversion))
1463+
return;
1464+
1465+
// We only care about int->int conversions here.
1466+
// We ignore conversions to/from pointer and/or bool.
1467+
if (!PromotionIsPotentiallyEligibleForImplicitIntegerConversionCheck(SrcType,
1468+
DstType))
1469+
return;
1470+
1471+
assert(!DstType->isBooleanType() && "we should not get here with booleans.");
1472+
1473+
EmitBitfieldTruncationCheck(Src, SrcType, Dst, DstType, Info, Loc);
1474+
EmitBitfieldSignChangeCheck(Src, SrcType, Dst, DstType, Info, Loc);
1475+
}
1476+
12421477
Value *ScalarExprEmitter::EmitScalarCast(Value *Src, QualType SrcType,
12431478
QualType DstType, llvm::Type *SrcTy,
12441479
llvm::Type *DstTy,
@@ -2848,9 +3083,12 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
28483083
}
28493084

28503085
// Store the updated result through the lvalue.
2851-
if (LV.isBitField())
3086+
if (LV.isBitField()) {
3087+
Value *Src = value;
28523088
CGF.EmitStoreThroughBitfieldLValue(RValue::get(value), LV, &value);
2853-
else
3089+
EmitBitfieldConversionCheck(Src, E->getType(), value, E->getType(),
3090+
LV.getBitFieldInfo(), E->getExprLoc());
3091+
} else
28543092
CGF.EmitStoreThroughLValue(RValue::get(value), LV);
28553093

28563094
// If this is a postinc, return the value read from memory, otherwise use the
@@ -3375,9 +3613,17 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue(
33753613
// specially because the result is altered by the store, i.e., [C99 6.5.16p1]
33763614
// 'An assignment expression has the value of the left operand after the
33773615
// assignment...'.
3378-
if (LHSLV.isBitField())
3616+
if (LHSLV.isBitField()) {
3617+
Value *Src = Result;
3618+
QualType SrcType = E->getRHS()->getType();
3619+
QualType DstType = E->getLHS()->getType();
3620+
if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E->getRHS())) {
3621+
SrcType = ICE->getSubExpr()->getType();
3622+
}
33793623
CGF.EmitStoreThroughBitfieldLValue(RValue::get(Result), LHSLV, &Result);
3380-
else
3624+
EmitBitfieldConversionCheck(Src, SrcType, Result, DstType,
3625+
LHSLV.getBitFieldInfo(), E->getExprLoc());
3626+
} else
33813627
CGF.EmitStoreThroughLValue(RValue::get(Result), LHSLV);
33823628

33833629
if (CGF.getLangOpts().OpenMP)
@@ -4513,7 +4759,15 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) {
45134759
// 'An assignment expression has the value of the left operand after
45144760
// the assignment...'.
45154761
if (LHS.isBitField()) {
4762+
Value *Src = RHS;
4763+
QualType SrcType = E->getRHS()->getType();
4764+
QualType DstType = E->getLHS()->getType();
4765+
if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E->getRHS())) {
4766+
SrcType = ICE->getSubExpr()->getType();
4767+
}
45164768
CGF.EmitStoreThroughBitfieldLValue(RValue::get(RHS), LHS, &RHS);
4769+
EmitBitfieldConversionCheck(Src, SrcType, RHS, DstType,
4770+
LHS.getBitFieldInfo(), E->getExprLoc());
45174771
} else {
45184772
CGF.EmitNullabilityCheck(LHS, RHS, E->getExprLoc());
45194773
CGF.EmitStoreThroughLValue(RValue::get(RHS), LHS);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %clang -fsanitize=implicit-integer-truncation -target x86_64-linux -S -emit-llvm -o - %s | FileCheck %s
2+
// RUN: %clang -fsanitize=implicit-integer-sign-change -target x86_64-linux -S -emit-llvm -o - %s | FileCheck %s
3+
typedef struct _xx {
4+
int x1:3;
5+
char x2:2;
6+
} xx, *pxx;
7+
8+
xx vxx;
9+
10+
// CHECK-LABEL: define{{.*}} void @foo1
11+
void foo1(int x) {
12+
vxx.x1 = x;
13+
// CHECK: call void @__ubsan_handle_implicit_conversion
14+
}
15+
16+
// CHECK: declare void @__ubsan_handle_implicit_conversion
17+
18+
// CHECK-LABEL: define{{.*}} void @foo2
19+
void foo2(int x) {
20+
vxx.x2 = x;
21+
// CHECK: call void @__ubsan_handle_implicit_conversion
22+
// TODO: Ideally we should only emit once (emit is generated
23+
// when evaluating RHS integer->char and when storing
24+
// value in bitfield)
25+
// CHECK: call void @__ubsan_handle_implicit_conversion
26+
}
27+

compiler-rt/lib/ubsan/ubsan_diag.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ class Diag {
177177
};
178178

179179
private:
180-
static const unsigned MaxArgs = 8;
180+
static const unsigned MaxArgs = 9;
181181
static const unsigned MaxRanges = 1;
182182

183183
/// The arguments which have been added to this diagnostic so far.

compiler-rt/lib/ubsan/ubsan_handlers.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -594,14 +594,16 @@ static void handleImplicitConversion(ImplicitConversionData *Data,
594594

595595
ScopedReport R(Opts, Loc, ET);
596596

597-
// FIXME: is it possible to dump the values as hex with fixed width?
597+
unsigned DstBits =
598+
Data->BitfieldBits ? Data->BitfieldBits : DstTy.getIntegerBitWidth();
598599

600+
// FIXME: is it possible to dump the values as hex with fixed width?
599601
Diag(Loc, DL_Error, ET,
600602
"implicit conversion from type %0 of value %1 (%2-bit, %3signed) to "
601-
"type %4 changed the value to %5 (%6-bit, %7signed)")
603+
"type %4 changed the value to %5 (%6-bit%7, %8signed)")
602604
<< SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth()
603-
<< (SrcSigned ? "" : "un") << DstTy << Value(DstTy, Dst)
604-
<< DstTy.getIntegerBitWidth() << (DstSigned ? "" : "un");
605+
<< (SrcSigned ? "" : "un") << DstTy << Value(DstTy, Dst) << DstBits
606+
<< (Data->BitfieldBits ? " bitfield" : "") << (DstSigned ? "" : "un");
605607
}
606608

607609
void __ubsan::__ubsan_handle_implicit_conversion(ImplicitConversionData *Data,

compiler-rt/lib/ubsan/ubsan_handlers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ struct ImplicitConversionData {
147147
const TypeDescriptor &FromType;
148148
const TypeDescriptor &ToType;
149149
/* ImplicitConversionCheckKind */ unsigned char Kind;
150+
unsigned int BitfieldBits;
150151
};
151152

152153
/// \brief Implict conversion that changed the value.

0 commit comments

Comments
 (0)