diff --git a/docs/UsersManual.html b/docs/UsersManual.html index 35fc5dca3315afa68d705b879bb6ae7198653b0e..967d0dbf7b16a7c9b93e1c448a473eb0a35a9a06 100644 --- a/docs/UsersManual.html +++ b/docs/UsersManual.html @@ -875,21 +875,27 @@ likely to affect PCH files that reference a large number of headers.</p> <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> <dl> <dt id="opt_fsanitize"><b>-fsanitize=check1,check2</b>: Turn on runtime checks -for various forms of undefined behavior.</dt> +for various forms of undefined or suspicious behavior.</dt> <dd>This option controls whether Clang adds runtime checks for various forms of -undefined behavior, and is disabled by default. If a check fails, a diagnostic -message is produced at runtime explaining the problem. The main checks are: +undefined or suspicious behavior, and is disabled by default. If a check +fails, a diagnostic message is produced at runtime explaining the problem. The +main checks are: <ul> <li id="opt_fsanitize_address"><tt>-fsanitize=address</tt>: <a href="AddressSanitizer.html">AddressSanitizer</a>, a memory error detector.</li> +<li id="opt_fsanitize_integer"><tt>-fsanitize=integer</tt>: + Enables checks for undefined or suspicious integer behavior.</li> <li id="opt_fsanitize_thread"><tt>-fsanitize=thread</tt>: <a href="ThreadSanitizer.html">ThreadSanitizer</a>, an <em>experimental</em> data race detector. Not ready for widespread use.</li> <li id="opt_fsanitize_undefined"><tt>-fsanitize=undefined</tt>: - Enables all the checks listed below.</li> + Fast and compatible undefined behavior checker. Enables the undefined behavior + checks that have small runtime cost and no impact on address space layout + or ABI. This includes all of the checks listed below other than unsigned + integer overflow.</li> </ul> The following more fine-grained checks are also available: @@ -897,11 +903,13 @@ The following more fine-grained checks are also available: <ul> <li id="opt_fsanitize_alignment"><tt>-fsanitize=alignment</tt>: Use of a misaligned pointer or creation of a misaligned reference.</li> -<li id="opt_fsanitize_divide-by-zero"><tt>-fsanitize=divide-by-zero</tt>: - Division by zero.</li> <li id="opt_fsanitize_float-cast-overflow"><tt>-fsanitize=float-cast-overflow</tt>: Conversion to, from, or between floating-point types which would overflow the destination.</li> +<li id="opt_fsanitize_float-divide-by-zero"><tt>-fsanitize=float-divide-by-zero</tt>: + Floating point division by zero.</li> +<li id="opt_fsanitize_integer-divide-by-zero"><tt>-fsanitize=integer-divide-by-zero</tt>: + Integer division by zero.</li> <li id="opt_fsanitize_null"><tt>-fsanitize=null</tt>: Use of a null pointer or creation of a null reference.</li> <li id="opt_fsanitize_object-size"><tt>-fsanitize=object-size</tt>: @@ -923,6 +931,8 @@ The following more fine-grained checks are also available: and checking for overflow in signed division (<tt>INT_MIN / -1</tt>).</li> <li id="opt_fsanitize_unreachable"><tt>-fsanitize=unreachable</tt>: If control flow reaches __builtin_unreachable.</li> +<li id="opt_fsanitize_unsigned-integer-overflow"><tt>-fsanitize=unsigned-integer-overflow</tt>: + Unsigned integer overflows.</li> <li id="opt_fsanitize_vla-bound"><tt>-fsanitize=vla-bound</tt>: A variable-length array whose bound does not evaluate to a positive value.</li> <li id="opt_fsanitize_vptr"><tt>-fsanitize=vptr</tt>: diff --git a/include/clang/Basic/Sanitizers.def b/include/clang/Basic/Sanitizers.def index 135832de911857884ecda580670a173c57e9f75f..108539291183c39cc090588c7c2e3804145b1123 100644 --- a/include/clang/Basic/Sanitizers.def +++ b/include/clang/Basic/Sanitizers.def @@ -45,26 +45,34 @@ SANITIZER("address", Address) SANITIZER("thread", Thread) // UndefinedBehaviorSanitizer -SANITIZER("signed-integer-overflow", SignedIntegerOverflow) -SANITIZER("divide-by-zero", DivideByZero) +SANITIZER("alignment", Alignment) +SANITIZER("bounds", Bounds) +SANITIZER("float-cast-overflow", FloatCastOverflow) +SANITIZER("float-divide-by-zero", FloatDivideByZero) +SANITIZER("integer-divide-by-zero", IntegerDivideByZero) +SANITIZER("null", Null) +SANITIZER("object-size", ObjectSize) +SANITIZER("return", Return) SANITIZER("shift", Shift) +SANITIZER("signed-integer-overflow", SignedIntegerOverflow) SANITIZER("unreachable", Unreachable) -SANITIZER("return", Return) SANITIZER("vla-bound", VLABound) -SANITIZER("alignment", Alignment) -SANITIZER("null", Null) SANITIZER("vptr", Vptr) -SANITIZER("object-size", ObjectSize) -SANITIZER("float-cast-overflow", FloatCastOverflow) -SANITIZER("bounds", Bounds) + +// IntegerSanitizer +SANITIZER("unsigned-integer-overflow", UnsignedIntegerOverflow) // -fsanitize=undefined (and its alias -fcatch-undefined-behavior). This should // include all the sanitizers which have low overhead, no ABI or address space // layout implications, and only catch undefined behavior. SANITIZER_GROUP("undefined", Undefined, - SignedIntegerOverflow | DivideByZero | Shift | Unreachable | - Return | VLABound | Alignment | Null | Vptr | ObjectSize | - FloatCastOverflow | Bounds) + Alignment | Bounds | FloatCastOverflow | FloatDivideByZero | + IntegerDivideByZero | Null | ObjectSize | Return | Shift | + SignedIntegerOverflow | Unreachable | VLABound | Vptr) + +SANITIZER_GROUP("integer", Integer, + SignedIntegerOverflow | UnsignedIntegerOverflow | Shift | + IntegerDivideByZero) #undef SANITIZER #undef SANITIZER_GROUP diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index ed8b9c69ecd7aedd29e39ce489cef6efdc365ac4..298be923cbb57ad06495f4691a87c55f03797cbf 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -414,6 +414,10 @@ public: } } + if (Ops.Ty->isUnsignedIntegerType() && + CGF.getLangOpts().SanitizeUnsignedIntegerOverflow) + return EmitOverflowCheckedBinOp(Ops); + if (Ops.LHS->getType()->isFPOrFPVectorTy()) return Builder.CreateFMul(Ops.LHS, Ops.RHS, "mul"); return Builder.CreateMul(Ops.LHS, Ops.RHS, "mul"); @@ -1472,11 +1476,23 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, // Note that signed integer inc/dec with width less than int can't // overflow because of promotion rules; we're just eliding a few steps here. - if (type->isSignedIntegerOrEnumerationType() && - value->getType()->getPrimitiveSizeInBits() >= - CGF.IntTy->getBitWidth()) + if (value->getType()->getPrimitiveSizeInBits() >= + CGF.IntTy->getBitWidth() && + type->isSignedIntegerOrEnumerationType()) { value = EmitAddConsiderOverflowBehavior(E, value, amt, isInc); - else + } else if (value->getType()->getPrimitiveSizeInBits() >= + CGF.IntTy->getBitWidth() && + type->isUnsignedIntegerType() && + CGF.getLangOpts().SanitizeUnsignedIntegerOverflow) { + BinOpInfo BinOp; + BinOp.LHS = value; + BinOp.RHS = llvm::ConstantInt::get(value->getType(), 1, false); + BinOp.Ty = E->getType(); + BinOp.Opcode = isInc ? BO_Add : BO_Sub; + BinOp.FPContractable = false; + BinOp.E = E; + value = EmitOverflowCheckedBinOp(BinOp); + } else value = Builder.CreateAdd(value, amt, isInc ? "inc" : "dec"); // Next most common: pointer increment. @@ -1926,7 +1942,7 @@ void ScalarExprEmitter::EmitUndefinedBehaviorIntegerDivAndRemCheck( const BinOpInfo &Ops, llvm::Value *Zero, bool isDiv) { llvm::Value *Cond = 0; - if (CGF.getLangOpts().SanitizeDivideByZero) + if (CGF.getLangOpts().SanitizeIntegerDivideByZero) Cond = Builder.CreateICmpNE(Ops.RHS, Zero); if (CGF.getLangOpts().SanitizeSignedIntegerOverflow && @@ -1948,16 +1964,17 @@ void ScalarExprEmitter::EmitUndefinedBehaviorIntegerDivAndRemCheck( } Value *ScalarExprEmitter::EmitDiv(const BinOpInfo &Ops) { - if (CGF.getLangOpts().SanitizeDivideByZero || - CGF.getLangOpts().SanitizeSignedIntegerOverflow) { + if ((CGF.getLangOpts().SanitizeIntegerDivideByZero || + CGF.getLangOpts().SanitizeSignedIntegerOverflow) && + Ops.Ty->isIntegerType()) { llvm::Value *Zero = llvm::Constant::getNullValue(ConvertType(Ops.Ty)); - - if (Ops.Ty->isIntegerType()) - EmitUndefinedBehaviorIntegerDivAndRemCheck(Ops, Zero, true); - else if (CGF.getLangOpts().SanitizeDivideByZero && - Ops.Ty->isRealFloatingType()) - EmitBinOpCheck(Builder.CreateFCmpUNE(Ops.RHS, Zero), Ops); + EmitUndefinedBehaviorIntegerDivAndRemCheck(Ops, Zero, true); + } else if (CGF.getLangOpts().SanitizeFloatDivideByZero && + Ops.Ty->isRealFloatingType()) { + llvm::Value *Zero = llvm::Constant::getNullValue(ConvertType(Ops.Ty)); + EmitBinOpCheck(Builder.CreateFCmpUNE(Ops.RHS, Zero), Ops); } + if (Ops.LHS->getType()->isFPOrFPVectorTy()) { llvm::Value *Val = Builder.CreateFDiv(Ops.LHS, Ops.RHS, "div"); if (CGF.getLangOpts().OpenCL) { @@ -1978,10 +1995,10 @@ Value *ScalarExprEmitter::EmitDiv(const BinOpInfo &Ops) { Value *ScalarExprEmitter::EmitRem(const BinOpInfo &Ops) { // Rem in C can't be a floating point type: C99 6.5.5p2. - if (CGF.getLangOpts().SanitizeDivideByZero) { + if (CGF.getLangOpts().SanitizeIntegerDivideByZero) { llvm::Value *Zero = llvm::Constant::getNullValue(ConvertType(Ops.Ty)); - if (Ops.Ty->isIntegerType()) + if (Ops.Ty->isIntegerType()) EmitUndefinedBehaviorIntegerDivAndRemCheck(Ops, Zero, false); } @@ -1995,27 +2012,32 @@ Value *ScalarExprEmitter::EmitOverflowCheckedBinOp(const BinOpInfo &Ops) { unsigned IID; unsigned OpID = 0; + bool isSigned = Ops.Ty->isSignedIntegerOrEnumerationType(); switch (Ops.Opcode) { case BO_Add: case BO_AddAssign: OpID = 1; - IID = llvm::Intrinsic::sadd_with_overflow; + IID = isSigned ? llvm::Intrinsic::sadd_with_overflow : + llvm::Intrinsic::uadd_with_overflow; break; case BO_Sub: case BO_SubAssign: OpID = 2; - IID = llvm::Intrinsic::ssub_with_overflow; + IID = isSigned ? llvm::Intrinsic::ssub_with_overflow : + llvm::Intrinsic::usub_with_overflow; break; case BO_Mul: case BO_MulAssign: OpID = 3; - IID = llvm::Intrinsic::smul_with_overflow; + IID = isSigned ? llvm::Intrinsic::smul_with_overflow : + llvm::Intrinsic::umul_with_overflow; break; default: llvm_unreachable("Unsupported operation for overflow detection"); } OpID <<= 1; - OpID |= 1; + if (isSigned) + OpID |= 1; llvm::Type *opTy = CGF.CGM.getTypes().ConvertType(Ops.Ty); @@ -2031,7 +2053,7 @@ Value *ScalarExprEmitter::EmitOverflowCheckedBinOp(const BinOpInfo &Ops) { if (handlerName->empty()) { // If the signed-integer-overflow sanitizer is enabled, emit a call to its // runtime. Otherwise, this is a -ftrapv check, so just emit a trap. - if (CGF.getLangOpts().SanitizeSignedIntegerOverflow) + if (!isSigned || CGF.getLangOpts().SanitizeSignedIntegerOverflow) EmitBinOpCheck(Builder.CreateNot(overflow), Ops); else CGF.EmitTrapvCheck(Builder.CreateNot(overflow)); @@ -2256,7 +2278,11 @@ Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &op) { return EmitOverflowCheckedBinOp(op); } } - + + if (op.Ty->isUnsignedIntegerType() && + CGF.getLangOpts().SanitizeUnsignedIntegerOverflow) + return EmitOverflowCheckedBinOp(op); + if (op.LHS->getType()->isFPOrFPVectorTy()) { // Try to form an fmuladd. if (Value *FMulAdd = tryEmitFMulAdd(op, CGF, Builder)) @@ -2283,7 +2309,11 @@ Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) { return EmitOverflowCheckedBinOp(op); } } - + + if (op.Ty->isUnsignedIntegerType() && + CGF.getLangOpts().SanitizeUnsignedIntegerOverflow) + return EmitOverflowCheckedBinOp(op); + if (op.LHS->getType()->isFPOrFPVectorTy()) { // Try to form an fmuladd. if (Value *FMulAdd = tryEmitFMulAdd(op, CGF, Builder, true)) diff --git a/lib/Driver/SanitizerArgs.h b/lib/Driver/SanitizerArgs.h index 85f81f4bd93b566a54b63bdbe9df493bee03f913..ffc149374d9b03a02e9f314a05aeb423312e01d4 100644 --- a/lib/Driver/SanitizerArgs.h +++ b/lib/Driver/SanitizerArgs.h @@ -30,7 +30,7 @@ class SanitizerArgs { #include "clang/Basic/Sanitizers.def" NeedsAsanRt = Address, NeedsTsanRt = Thread, - NeedsUbsanRt = Undefined ^ Bounds + NeedsUbsanRt = (Undefined & ~Bounds) | Integer }; unsigned Kind; diff --git a/test/CodeGen/catch-undef-behavior.c b/test/CodeGen/catch-undef-behavior.c index 4198b62ea56cd766c10c90f243007574b056faec..7d50cea39a55bbde57ad8cca07acee925cbe0598 100644 --- a/test/CodeGen/catch-undef-behavior.c +++ b/test/CodeGen/catch-undef-behavior.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,divide-by-zero -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s +// RUN: %clang_cc1 -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s // RUN: %clang_cc1 -fsanitize=null -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-NULL // RUN: %clang_cc1 -fsanitize=signed-integer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-OVERFLOW diff --git a/test/CodeGen/unsigned-overflow.c b/test/CodeGen/unsigned-overflow.c new file mode 100644 index 0000000000000000000000000000000000000000..341ea355636b0f6a86b910f28242cd147e84872c --- /dev/null +++ b/test/CodeGen/unsigned-overflow.c @@ -0,0 +1,125 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsanitize=unsigned-integer-overflow %s -emit-llvm -o - | FileCheck %s +// Verify checked operations are emitted for integers and longs. +// unsigned short/char's tested in unsigned-promotion.c + +unsigned long li, lj, lk; +unsigned int ii, ij, ik; + +extern void opaquelong(unsigned long); +extern void opaqueint(unsigned int); + +// CHECK: define void @testlongadd() +void testlongadd() { + + // CHECK: [[T1:%.*]] = load i64* @lj + // CHECK-NEXT: [[T2:%.*]] = load i64* @lk + // CHECK-NEXT: [[T3:%.*]] = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 [[T1]], i64 [[T2]]) + // CHECK-NEXT: [[T4:%.*]] = extractvalue { i64, i1 } [[T3]], 0 + // CHECK-NEXT: [[T5:%.*]] = extractvalue { i64, i1 } [[T3]], 1 + // CHECK: call void @__ubsan_handle_add_overflow + li = lj + lk; +} + +// CHECK: define void @testlongsub() +void testlongsub() { + + // CHECK: [[T1:%.*]] = load i64* @lj + // CHECK-NEXT: [[T2:%.*]] = load i64* @lk + // CHECK-NEXT: [[T3:%.*]] = call { i64, i1 } @llvm.usub.with.overflow.i64(i64 [[T1]], i64 [[T2]]) + // CHECK-NEXT: [[T4:%.*]] = extractvalue { i64, i1 } [[T3]], 0 + // CHECK-NEXT: [[T5:%.*]] = extractvalue { i64, i1 } [[T3]], 1 + // CHECK: call void @__ubsan_handle_sub_overflow + li = lj - lk; +} + +// CHECK: define void @testlongmul() +void testlongmul() { + + // CHECK: [[T1:%.*]] = load i64* @lj + // CHECK-NEXT: [[T2:%.*]] = load i64* @lk + // CHECK-NEXT: [[T3:%.*]] = call { i64, i1 } @llvm.umul.with.overflow.i64(i64 [[T1]], i64 [[T2]]) + // CHECK-NEXT: [[T4:%.*]] = extractvalue { i64, i1 } [[T3]], 0 + // CHECK-NEXT: [[T5:%.*]] = extractvalue { i64, i1 } [[T3]], 1 + // CHECK: call void @__ubsan_handle_mul_overflow + li = lj * lk; +} + +// CHECK: define void @testlongpostinc() +void testlongpostinc() { + opaquelong(li++); + + // CHECK: [[T1:%.*]] = load i64* @li + // CHECK-NEXT: [[T2:%.*]] = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 [[T1]], i64 1) + // CHECK-NEXT: [[T3:%.*]] = extractvalue { i64, i1 } [[T2]], 0 + // CHECK-NEXT: [[T4:%.*]] = extractvalue { i64, i1 } [[T2]], 1 + // CHECK: call void @__ubsan_handle_add_overflow +} + +// CHECK: define void @testlongpreinc() +void testlongpreinc() { + opaquelong(++li); + + // CHECK: [[T1:%.*]] = load i64* @li + // CHECK-NEXT: [[T2:%.*]] = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 [[T1]], i64 1) + // CHECK-NEXT: [[T3:%.*]] = extractvalue { i64, i1 } [[T2]], 0 + // CHECK-NEXT: [[T4:%.*]] = extractvalue { i64, i1 } [[T2]], 1 + // CHECK: call void @__ubsan_handle_add_overflow +} + +// CHECK: define void @testintadd() +void testintadd() { + + // CHECK: [[T1:%.*]] = load i32* @ij + // CHECK-NEXT: [[T2:%.*]] = load i32* @ik + // CHECK-NEXT: [[T3:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[T1]], i32 [[T2]]) + // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T3]], 0 + // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T3]], 1 + // CHECK: call void @__ubsan_handle_add_overflow + ii = ij + ik; +} + +// CHECK: define void @testintsub() +void testintsub() { + + // CHECK: [[T1:%.*]] = load i32* @ij + // CHECK-NEXT: [[T2:%.*]] = load i32* @ik + // CHECK-NEXT: [[T3:%.*]] = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 [[T1]], i32 [[T2]]) + // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T3]], 0 + // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T3]], 1 + // CHECK: call void @__ubsan_handle_sub_overflow + ii = ij - ik; +} + +// CHECK: define void @testintmul() +void testintmul() { + + // CHECK: [[T1:%.*]] = load i32* @ij + // CHECK-NEXT: [[T2:%.*]] = load i32* @ik + // CHECK-NEXT: [[T3:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[T1]], i32 [[T2]]) + // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T3]], 0 + // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T3]], 1 + // CHECK: call void @__ubsan_handle_mul_overflow + ii = ij * ik; +} + +// CHECK: define void @testintpostinc() +void testintpostinc() { + opaqueint(ii++); + + // CHECK: [[T1:%.*]] = load i32* @ii + // CHECK-NEXT: [[T2:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[T1]], i32 1) + // CHECK-NEXT: [[T3:%.*]] = extractvalue { i32, i1 } [[T2]], 0 + // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T2]], 1 + // CHECK: call void @__ubsan_handle_add_overflow +} + +// CHECK: define void @testintpreinc() +void testintpreinc() { + opaqueint(++ii); + + // CHECK: [[T1:%.*]] = load i32* @ii + // CHECK-NEXT: [[T2:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[T1]], i32 1) + // CHECK-NEXT: [[T3:%.*]] = extractvalue { i32, i1 } [[T2]], 0 + // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T2]], 1 + // CHECK: call void @__ubsan_handle_add_overflow +} diff --git a/test/CodeGen/unsigned-promotion.c b/test/CodeGen/unsigned-promotion.c new file mode 100644 index 0000000000000000000000000000000000000000..c263c0c946b0ce0ee0f7b19c985f51f397596b30 --- /dev/null +++ b/test/CodeGen/unsigned-promotion.c @@ -0,0 +1,143 @@ +// Check -fsanitize=signed-integer-overflow and +// -fsanitize=unsigned-integer-overflow with promoted unsigned types +// +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s \ +// RUN: -fsanitize=signed-integer-overflow | FileCheck %s --check-prefix=CHECKS +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s \ +// RUN: -fsanitize=unsigned-integer-overflow | FileCheck %s --check-prefix=CHECKU + +unsigned short si, sj, sk; +unsigned char ci, cj, ck; + +extern void opaqueshort(unsigned short); +extern void opaquechar(unsigned char); + +// CHECKS: define void @testshortadd() +// CHECKU: define void @testshortadd() +void testshortadd() { + // CHECKS: load i16* @sj + // CHECKS: load i16* @sk + // CHECKS: [[T1:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]]) + // CHECKS-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0 + // CHECKS-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1 + // CHECKS: call void @__ubsan_handle_add_overflow + // + // CHECKU: [[T1:%.*]] = load i16* @sj + // CHECKU: [[T2:%.*]] = zext i16 [[T1]] + // CHECKU: [[T3:%.*]] = load i16* @sk + // CHECKU: [[T4:%.*]] = zext i16 [[T3]] + // CHECKU-NOT: llvm.sadd + // CHECKU-NOT: llvm.uadd + // CHECKU: [[T5:%.*]] = add nsw i32 [[T2]], [[T4]] + + si = sj + sk; +} + +// CHECKS: define void @testshortsub() +// CHECKU: define void @testshortsub() +void testshortsub() { + + // CHECKS: load i16* @sj + // CHECKS: load i16* @sk + // CHECKS: [[T1:%.*]] = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]]) + // CHECKS-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0 + // CHECKS-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1 + // CHECKS: call void @__ubsan_handle_sub_overflow + // + // CHECKU: [[T1:%.*]] = load i16* @sj + // CHECKU: [[T2:%.*]] = zext i16 [[T1]] + // CHECKU: [[T3:%.*]] = load i16* @sk + // CHECKU: [[T4:%.*]] = zext i16 [[T3]] + // CHECKU-NOT: llvm.ssub + // CHECKU-NOT: llvm.usub + // CHECKU: [[T5:%.*]] = sub nsw i32 [[T2]], [[T4]] + + si = sj - sk; +} + +// CHECKS: define void @testshortmul() +// CHECKU: define void @testshortmul() +void testshortmul() { + + // CHECKS: load i16* @sj + // CHECKS: load i16* @sk + // CHECKS: [[T1:%.*]] = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]]) + // CHECKS-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0 + // CHECKS-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1 + // CHECKS: call void @__ubsan_handle_mul_overflow + // + // CHECKU: [[T1:%.*]] = load i16* @sj + // CHECKU: [[T2:%.*]] = zext i16 [[T1]] + // CHECKU: [[T3:%.*]] = load i16* @sk + // CHECKU: [[T4:%.*]] = zext i16 [[T3]] + // CHECKU-NOT: llvm.smul + // CHECKU-NOT: llvm.umul + // CHECKU: [[T5:%.*]] = mul nsw i32 [[T2]], [[T4]] + si = sj * sk; +} + +// CHECKS: define void @testcharadd() +// CHECKU: define void @testcharadd() +void testcharadd() { + + // CHECKS: load i8* @cj + // CHECKS: load i8* @ck + // CHECKS: [[T1:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]]) + // CHECKS-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0 + // CHECKS-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1 + // CHECKS: call void @__ubsan_handle_add_overflow + // + // CHECKU: [[T1:%.*]] = load i8* @cj + // CHECKU: [[T2:%.*]] = zext i8 [[T1]] + // CHECKU: [[T3:%.*]] = load i8* @ck + // CHECKU: [[T4:%.*]] = zext i8 [[T3]] + // CHECKU-NOT: llvm.sadd + // CHECKU-NOT: llvm.uadd + // CHECKU: [[T5:%.*]] = add nsw i32 [[T2]], [[T4]] + + ci = cj + ck; +} + +// CHECKS: define void @testcharsub() +// CHECKU: define void @testcharsub() +void testcharsub() { + + // CHECKS: load i8* @cj + // CHECKS: load i8* @ck + // CHECKS: [[T1:%.*]] = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]]) + // CHECKS-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0 + // CHECKS-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1 + // CHECKS: call void @__ubsan_handle_sub_overflow + // + // CHECKU: [[T1:%.*]] = load i8* @cj + // CHECKU: [[T2:%.*]] = zext i8 [[T1]] + // CHECKU: [[T3:%.*]] = load i8* @ck + // CHECKU: [[T4:%.*]] = zext i8 [[T3]] + // CHECKU-NOT: llvm.ssub + // CHECKU-NOT: llvm.usub + // CHECKU: [[T5:%.*]] = sub nsw i32 [[T2]], [[T4]] + + ci = cj - ck; +} + +// CHECKS: define void @testcharmul() +// CHECKU: define void @testcharmul() +void testcharmul() { + + // CHECKS: load i8* @cj + // CHECKS: load i8* @ck + // CHECKS: [[T1:%.*]] = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]]) + // CHECKS-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0 + // CHECKS-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1 + // CHECKS: call void @__ubsan_handle_mul_overflow + // + // CHECKU: [[T1:%.*]] = load i8* @cj + // CHECKU: [[T2:%.*]] = zext i8 [[T1]] + // CHECKU: [[T3:%.*]] = load i8* @ck + // CHECKU: [[T4:%.*]] = zext i8 [[T3]] + // CHECKU-NOT: llvm.smul + // CHECKU-NOT: llvm.umul + // CHECKU: [[T5:%.*]] = mul nsw i32 [[T2]], [[T4]] + + ci = cj * ck; +} diff --git a/test/CodeGen/unsigned-trapv.c b/test/CodeGen/unsigned-trapv.c new file mode 100644 index 0000000000000000000000000000000000000000..b7aed03d888300bd2e58445db14c9596c23a564e --- /dev/null +++ b/test/CodeGen/unsigned-trapv.c @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - -fsanitize=unsigned-integer-overflow | FileCheck %s --check-prefix=UNSIGNED +// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - -ftrapv | FileCheck %s --check-prefix=TRAPV +// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - -fsanitize=unsigned-integer-overflow -ftrapv | FileCheck %s --check-prefix=BOTH +// Verify that -ftrapv and -fsanitize=unsigned-integer-overflow +// work together as expected + + +// UNSIGNED: @test_signed +// TRAPV: @test_signed +// BOTH: @test_signed +void test_signed() { + extern volatile int a, b, c; + // UNSIGNED: add nsw i32 + // UNSIGNED-NOT: overflow + // TRAPV: sadd.with.overflow.i32 + // TRAPV-NOT: ubsan + // TRAPV: llvm.trap + // BOTH: sadd.with.overflow.i32 + // BOTH-NOT: ubsan + // BOTH: llvm.trap + a = b + c; +} + +// UNSIGNED: @test_unsigned +// TRAPV: @test_unsigned +// BOTH: @test_unsigned +void test_unsigned() { + extern volatile unsigned x, y, z; + // UNSIGNED: uadd.with.overflow.i32 + // UNSIGNED-NOT: llvm.trap + // UNSIGNED: ubsan + // TRAPV-NOT: overflow + // TRAPV-NOT: llvm.trap + // BOTH: uadd.with.overflow.i32 + // BOTH: ubsan + // BOTH-NOT: llvm.trap + x = y + z; +} diff --git a/test/CodeGenCXX/catch-undef-behavior.cpp b/test/CodeGenCXX/catch-undef-behavior.cpp index fd9e3d7278a7c1d4fe91da682fc8667fbe76ad9e..9c2985c198c64004d4813614edfb38e1332b0a59 100644 --- a/test/CodeGenCXX/catch-undef-behavior.cpp +++ b/test/CodeGenCXX/catch-undef-behavior.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsanitize=signed-integer-overflow,divide-by-zero,shift,unreachable,return,vla-bound,alignment,null,vptr,object-size,float-cast-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s +// RUN: %clang_cc1 -fsanitize=signed-integer-overflow,integer-divide-by-zero,float-divide-by-zero,shift,unreachable,return,vla-bound,alignment,null,vptr,object-size,float-cast-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s // CHECK: @_Z17reference_binding void reference_binding(int *p) { diff --git a/test/Driver/fsanitize.c b/test/Driver/fsanitize.c index 9812c25f77a21fe9f51ad5f68cd5b6ba3867b251..8b1586489d995ceea12a3ae84a8ba135ef5b2387 100644 --- a/test/Driver/fsanitize.c +++ b/test/Driver/fsanitize.c @@ -1,9 +1,14 @@ // RUN: %clang -target x86_64-linux-gnu -fcatch-undefined-behavior %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED // RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED -// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|bounds),?){12}"}} // RUN: %clang -target x86_64-linux-gnu -fsanitize=thread,undefined -fno-thread-sanitizer -fno-sanitize=float-cast-overflow,vptr %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-UNDEFINED -// CHECK-PARTIAL-UNDEFINED: "-fsanitize={{((signed-integer-overflow|divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|object-size|bounds),?){10}"}} +// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|bounds),?){13}"}} +// +// RUN: %clang -target x86_64-linux-gnu -fsanitize=integer %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INTEGER +// CHECK-INTEGER: "-fsanitize={{((signed-integer-overflow|unsigned-integer-overflow|integer-divide-by-zero|shift),?){4}"}} + +// RUN: %clang -target x86_64-linux-gnu -fsanitize=thread,undefined -fno-thread-sanitizer -fno-sanitize=float-cast-overflow,vptr %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-UNDEFINED +// CHECK-PARTIAL-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|object-size|bounds),?){11}"}} // RUN: %clang -target x86_64-linux-gnu -fsanitize=vptr -fno-rtti %s -c -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-VPTR-NO-RTTI // RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fno-rtti %s -c -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-VPTR-NO-RTTI