Skip to content

Commit d919115

Browse files
committed
[ubsan] Skip overflow checks on safe arithmetic (fixes PR32874)
Currently, ubsan emits overflow checks for arithmetic that is known to be safe at compile-time, e.g: 1 + 1 => CheckedAdd(1, 1) This leads to breakage when using the __builtin_prefetch intrinsic. LLVM expects the arguments to @llvm.prefetch to be constant integers, and when ubsan inserts unnecessary checks on the operands to the intrinsic, this contract is broken, leading to verifier failures (see PR32874). Instead of special-casing __builtin_prefetch for ubsan, this patch fixes the underlying problem, i.e that clang currently emits unnecessary overflow checks. Testing: I ran the check-clang and check-ubsan targets with a stage2, ubsan-enabled build of clang. I added a regression test for PR32874, and some extra checking to make sure we don't regress runtime checking for unsafe arithmetic. The existing ubsan-promoted-arithmetic.cpp test also provides coverage for this change. llvm-svn: 301988
1 parent 6773659 commit d919115

File tree

2 files changed

+139
-8
lines changed

2 files changed

+139
-8
lines changed

clang/lib/CodeGen/CGExprScalar.cpp

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,64 @@ struct BinOpInfo {
5151
BinaryOperator::Opcode Opcode; // Opcode of BinOp to perform
5252
FPOptions FPFeatures;
5353
const Expr *E; // Entire expr, for error unsupported. May not be binop.
54+
55+
/// Check if the binop can result in integer overflow.
56+
bool mayHaveIntegerOverflow() const {
57+
// Without constant input, we can't rule out overflow.
58+
const auto *LHSCI = dyn_cast<llvm::ConstantInt>(LHS);
59+
const auto *RHSCI = dyn_cast<llvm::ConstantInt>(RHS);
60+
if (!LHSCI || !RHSCI)
61+
return true;
62+
63+
// Assume overflow is possible, unless we can prove otherwise.
64+
bool Overflow = true;
65+
const auto &LHSAP = LHSCI->getValue();
66+
const auto &RHSAP = RHSCI->getValue();
67+
if (Opcode == BO_Add) {
68+
if (Ty->hasSignedIntegerRepresentation())
69+
(void)LHSAP.sadd_ov(RHSAP, Overflow);
70+
else
71+
(void)LHSAP.uadd_ov(RHSAP, Overflow);
72+
} else if (Opcode == BO_Sub) {
73+
if (Ty->hasSignedIntegerRepresentation())
74+
(void)LHSAP.ssub_ov(RHSAP, Overflow);
75+
else
76+
(void)LHSAP.usub_ov(RHSAP, Overflow);
77+
} else if (Opcode == BO_Mul) {
78+
if (Ty->hasSignedIntegerRepresentation())
79+
(void)LHSAP.smul_ov(RHSAP, Overflow);
80+
else
81+
(void)LHSAP.umul_ov(RHSAP, Overflow);
82+
} else if (Opcode == BO_Div || Opcode == BO_Rem) {
83+
if (Ty->hasSignedIntegerRepresentation() && !RHSCI->isZero())
84+
(void)LHSAP.sdiv_ov(RHSAP, Overflow);
85+
else
86+
return false;
87+
}
88+
return Overflow;
89+
}
90+
91+
/// Check if the binop computes a division or a remainder.
92+
bool isDivisionLikeOperation() const {
93+
return Opcode == BO_Div || Opcode == BO_Rem || Opcode == BO_DivAssign ||
94+
Opcode == BO_RemAssign;
95+
}
96+
97+
/// Check if the binop can result in an integer division by zero.
98+
bool mayHaveIntegerDivisionByZero() const {
99+
if (isDivisionLikeOperation())
100+
if (auto *CI = dyn_cast<llvm::ConstantInt>(RHS))
101+
return CI->isZero();
102+
return true;
103+
}
104+
105+
/// Check if the binop can result in a float division by zero.
106+
bool mayHaveFloatDivisionByZero() const {
107+
if (isDivisionLikeOperation())
108+
if (auto *CFP = dyn_cast<llvm::ConstantFP>(RHS))
109+
return CFP->isZero();
110+
return true;
111+
}
54112
};
55113

56114
static bool MustVisitNullValue(const Expr *E) {
@@ -85,9 +143,17 @@ static bool CanElideOverflowCheck(const ASTContext &Ctx, const BinOpInfo &Op) {
85143
assert((isa<UnaryOperator>(Op.E) || isa<BinaryOperator>(Op.E)) &&
86144
"Expected a unary or binary operator");
87145

146+
// If the binop has constant inputs and we can prove there is no overflow,
147+
// we can elide the overflow check.
148+
if (!Op.mayHaveIntegerOverflow())
149+
return true;
150+
151+
// If a unary op has a widened operand, the op cannot overflow.
88152
if (const auto *UO = dyn_cast<UnaryOperator>(Op.E))
89153
return IsWidenedIntegerOp(Ctx, UO->getSubExpr());
90154

155+
// We usually don't need overflow checks for binops with widened operands.
156+
// Multiplication with promoted unsigned operands is a special case.
91157
const auto *BO = cast<BinaryOperator>(Op.E);
92158
auto OptionalLHSTy = getUnwidenedIntegerType(Ctx, BO->getLHS());
93159
if (!OptionalLHSTy)
@@ -100,14 +166,14 @@ static bool CanElideOverflowCheck(const ASTContext &Ctx, const BinOpInfo &Op) {
100166
QualType LHSTy = *OptionalLHSTy;
101167
QualType RHSTy = *OptionalRHSTy;
102168

103-
// We usually don't need overflow checks for binary operations with widened
104-
// operands. Multiplication with promoted unsigned operands is a special case.
169+
// This is the simple case: binops without unsigned multiplication, and with
170+
// widened operands. No overflow check is needed here.
105171
if ((Op.Opcode != BO_Mul && Op.Opcode != BO_MulAssign) ||
106172
!LHSTy->isUnsignedIntegerType() || !RHSTy->isUnsignedIntegerType())
107173
return true;
108174

109-
// The overflow check can be skipped if either one of the unpromoted types
110-
// are less than half the size of the promoted type.
175+
// For unsigned multiplication the overflow check can be elided if either one
176+
// of the unpromoted types are less than half the size of the promoted type.
111177
unsigned PromotedSize = Ctx.getTypeSize(Op.E->getType());
112178
return (2 * Ctx.getTypeSize(LHSTy)) < PromotedSize ||
113179
(2 * Ctx.getTypeSize(RHSTy)) < PromotedSize;
@@ -2377,7 +2443,8 @@ void ScalarExprEmitter::EmitUndefinedBehaviorIntegerDivAndRemCheck(
23772443
const auto *BO = cast<BinaryOperator>(Ops.E);
23782444
if (CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow) &&
23792445
Ops.Ty->hasSignedIntegerRepresentation() &&
2380-
!IsWidenedIntegerOp(CGF.getContext(), BO->getLHS())) {
2446+
!IsWidenedIntegerOp(CGF.getContext(), BO->getLHS()) &&
2447+
Ops.mayHaveIntegerOverflow()) {
23812448
llvm::IntegerType *Ty = cast<llvm::IntegerType>(Zero->getType());
23822449

23832450
llvm::Value *IntMin =
@@ -2400,11 +2467,13 @@ Value *ScalarExprEmitter::EmitDiv(const BinOpInfo &Ops) {
24002467
CodeGenFunction::SanitizerScope SanScope(&CGF);
24012468
if ((CGF.SanOpts.has(SanitizerKind::IntegerDivideByZero) ||
24022469
CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) &&
2403-
Ops.Ty->isIntegerType()) {
2470+
Ops.Ty->isIntegerType() &&
2471+
(Ops.mayHaveIntegerDivisionByZero() || Ops.mayHaveIntegerOverflow())) {
24042472
llvm::Value *Zero = llvm::Constant::getNullValue(ConvertType(Ops.Ty));
24052473
EmitUndefinedBehaviorIntegerDivAndRemCheck(Ops, Zero, true);
24062474
} else if (CGF.SanOpts.has(SanitizerKind::FloatDivideByZero) &&
2407-
Ops.Ty->isRealFloatingType()) {
2475+
Ops.Ty->isRealFloatingType() &&
2476+
Ops.mayHaveFloatDivisionByZero()) {
24082477
llvm::Value *Zero = llvm::Constant::getNullValue(ConvertType(Ops.Ty));
24092478
llvm::Value *NonZero = Builder.CreateFCmpUNE(Ops.RHS, Zero);
24102479
EmitBinOpCheck(std::make_pair(NonZero, SanitizerKind::FloatDivideByZero),
@@ -2439,7 +2508,8 @@ Value *ScalarExprEmitter::EmitRem(const BinOpInfo &Ops) {
24392508
// Rem in C can't be a floating point type: C99 6.5.5p2.
24402509
if ((CGF.SanOpts.has(SanitizerKind::IntegerDivideByZero) ||
24412510
CGF.SanOpts.has(SanitizerKind::SignedIntegerOverflow)) &&
2442-
Ops.Ty->isIntegerType()) {
2511+
Ops.Ty->isIntegerType() &&
2512+
(Ops.mayHaveIntegerDivisionByZero() || Ops.mayHaveIntegerOverflow())) {
24432513
CodeGenFunction::SanitizerScope SanScope(&CGF);
24442514
llvm::Value *Zero = llvm::Constant::getNullValue(ConvertType(Ops.Ty));
24452515
EmitUndefinedBehaviorIntegerDivAndRemCheck(Ops, Zero, false);

clang/test/CodeGen/PR32874.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// RUN: %clang_cc1 -x c -S -emit-llvm -o - -triple x86_64-apple-darwin10 %s \
2+
// RUN: -w -fsanitize=signed-integer-overflow,unsigned-integer-overflow,integer-divide-by-zero,float-divide-by-zero \
3+
// RUN: | FileCheck %s
4+
5+
// CHECK-LABEL: define void @foo
6+
// CHECK-NOT: !nosanitize
7+
void foo(const int *p) {
8+
// __builtin_prefetch expects its optional arguments to be constant integers.
9+
// Check that ubsan does not instrument any safe arithmetic performed in
10+
// operands to __builtin_prefetch. (A clang frontend check should reject
11+
// unsafe arithmetic in these operands.)
12+
13+
__builtin_prefetch(p, 0 + 1, 0 + 3);
14+
__builtin_prefetch(p, 1 - 0, 3 - 0);
15+
__builtin_prefetch(p, 1 * 1, 1 * 3);
16+
__builtin_prefetch(p, 1 / 1, 3 / 1);
17+
__builtin_prefetch(p, 3 % 2, 3 % 1);
18+
19+
__builtin_prefetch(p, 0U + 1U, 0U + 3U);
20+
__builtin_prefetch(p, 1U - 0U, 3U - 0U);
21+
__builtin_prefetch(p, 1U * 1U, 1U * 3U);
22+
__builtin_prefetch(p, 1U / 1U, 3U / 1U);
23+
__builtin_prefetch(p, 3U % 2U, 3U % 1U);
24+
}
25+
26+
// CHECK-LABEL: define void @ub_constant_arithmetic
27+
void ub_constant_arithmetic() {
28+
// Check that we still instrument unsafe arithmetic, even if it is known to
29+
// be unsafe at compile time.
30+
31+
int INT_MIN = 0xffffffff;
32+
int INT_MAX = 0x7fffffff;
33+
34+
// CHECK: call void @__ubsan_handle_add_overflow
35+
// CHECK: call void @__ubsan_handle_add_overflow
36+
INT_MAX + 1;
37+
INT_MAX + -1;
38+
39+
// CHECK: call void @__ubsan_handle_negate_overflow
40+
// CHECK: call void @__ubsan_handle_sub_overflow
41+
-INT_MIN;
42+
-INT_MAX - 2;
43+
44+
// CHECK: call void @__ubsan_handle_mul_overflow
45+
// CHECK: call void @__ubsan_handle_mul_overflow
46+
INT_MAX * INT_MAX;
47+
INT_MIN * INT_MIN;
48+
49+
// CHECK: call void @__ubsan_handle_divrem_overflow
50+
// CHECK: call void @__ubsan_handle_divrem_overflow
51+
1 / 0;
52+
INT_MIN / -1;
53+
54+
// CHECK: call void @__ubsan_handle_divrem_overflow
55+
// CHECK: call void @__ubsan_handle_divrem_overflow
56+
1 % 0;
57+
INT_MIN % -1;
58+
59+
// CHECK: call void @__ubsan_handle_divrem_overflow
60+
1.0 / 0.0;
61+
}

0 commit comments

Comments
 (0)