From 1897cb3b9cb8f3c414fe9ccf1bf8cddf387953cb Mon Sep 17 00:00:00 2001 From: Will Dietz Date: Tue, 27 Nov 2012 15:01:55 +0000 Subject: [PATCH] Add -fsanitize=integer for reporting suspicious integer behaviors. Introduces new sanitizer "unsigned-integer-overflow". llvm-svn: 168701 --- clang/docs/UsersManual.html | 22 ++-- clang/include/clang/Basic/Sanitizers.def | 30 ++++-- clang/lib/CodeGen/CGExprScalar.cpp | 74 +++++++++---- clang/lib/Driver/SanitizerArgs.h | 2 +- clang/test/CodeGen/catch-undef-behavior.c | 2 +- clang/test/CodeGen/unsigned-overflow.c | 125 +++++++++++++++++++++ clang/test/CodeGen/unsigned-promotion.c | 143 +++++++++++++++++++++++++ clang/test/CodeGen/unsigned-trapv.c | 38 +++++++ clang/test/CodeGenCXX/catch-undef-behavior.cpp | 2 +- clang/test/Driver/fsanitize.c | 9 +- 10 files changed, 403 insertions(+), 44 deletions(-) create mode 100644 clang/test/CodeGen/unsigned-overflow.c create mode 100644 clang/test/CodeGen/unsigned-promotion.c create mode 100644 clang/test/CodeGen/unsigned-trapv.c diff --git a/clang/docs/UsersManual.html b/clang/docs/UsersManual.html index 35fc5dc..967d0db 100644 --- a/clang/docs/UsersManual.html +++ b/clang/docs/UsersManual.html @@ -875,21 +875,27 @@ likely to affect PCH files that reference a large number of headers.

-fsanitize=check1,check2: Turn on runtime checks -for various forms of undefined behavior.
+for various forms of undefined or suspicious behavior.
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:
  • -fsanitize=address: AddressSanitizer, a memory error detector.
  • +
  • -fsanitize=integer: + Enables checks for undefined or suspicious integer behavior.
  • -fsanitize=thread: ThreadSanitizer, an experimental data race detector. Not ready for widespread use.
  • -fsanitize=undefined: - Enables all the checks listed below.
  • + 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.
The following more fine-grained checks are also available: @@ -897,11 +903,13 @@ The following more fine-grained checks are also available:
  • -fsanitize=alignment: Use of a misaligned pointer or creation of a misaligned reference.
  • -
  • -fsanitize=divide-by-zero: - Division by zero.
  • -fsanitize=float-cast-overflow: Conversion to, from, or between floating-point types which would overflow the destination.
  • +
  • -fsanitize=float-divide-by-zero: + Floating point division by zero.
  • +
  • -fsanitize=integer-divide-by-zero: + Integer division by zero.
  • -fsanitize=null: Use of a null pointer or creation of a null reference.
  • -fsanitize=object-size: @@ -923,6 +931,8 @@ The following more fine-grained checks are also available: and checking for overflow in signed division (INT_MIN / -1).
  • -fsanitize=unreachable: If control flow reaches __builtin_unreachable.
  • +
  • -fsanitize=unsigned-integer-overflow: + Unsigned integer overflows.
  • -fsanitize=vla-bound: A variable-length array whose bound does not evaluate to a positive value.
  • -fsanitize=vptr: diff --git a/clang/include/clang/Basic/Sanitizers.def b/clang/include/clang/Basic/Sanitizers.def index 135832d..1085392 100644 --- a/clang/include/clang/Basic/Sanitizers.def +++ b/clang/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/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index ed8b9c6..298be92 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/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/clang/lib/Driver/SanitizerArgs.h b/clang/lib/Driver/SanitizerArgs.h index 85f81f4..ffc1493 100644 --- a/clang/lib/Driver/SanitizerArgs.h +++ b/clang/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/clang/test/CodeGen/catch-undef-behavior.c b/clang/test/CodeGen/catch-undef-behavior.c index 4198b62..7d50cea3 100644 --- a/clang/test/CodeGen/catch-undef-behavior.c +++ b/clang/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/clang/test/CodeGen/unsigned-overflow.c b/clang/test/CodeGen/unsigned-overflow.c new file mode 100644 index 0000000..341ea35 --- /dev/null +++ b/clang/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/clang/test/CodeGen/unsigned-promotion.c b/clang/test/CodeGen/unsigned-promotion.c new file mode 100644 index 0000000..c263c0c --- /dev/null +++ b/clang/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/clang/test/CodeGen/unsigned-trapv.c b/clang/test/CodeGen/unsigned-trapv.c new file mode 100644 index 0000000..b7aed03 --- /dev/null +++ b/clang/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/clang/test/CodeGenCXX/catch-undef-behavior.cpp b/clang/test/CodeGenCXX/catch-undef-behavior.cpp index fd9e3d7..9c2985c 100644 --- a/clang/test/CodeGenCXX/catch-undef-behavior.cpp +++ b/clang/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/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c index 9812c25..8b15864 100644 --- a/clang/test/Driver/fsanitize.c +++ b/clang/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 -- 2.7.4