Undefined Behavior Sanitizer (UBSan)
------------------------------------
+* The Implicit Conversion Sanitizer (``-fsanitize=implicit-conversion``) group
+ was extended. One more type of issues is caught - implicit integer sign change.
+ (``-fsanitize=implicit-integer-sign-change``).
+ This makes the Implicit Conversion Sanitizer feature-complete,
+ with only missing piece being bitfield handling.
+ While there is a ``-Wsign-conversion`` diagnostic group that catches this kind
+ of issues, it is both noisy, and does not catch **all** the cases.
+
+ .. code-block:: c++
+
+ bool consume(unsigned int val);
+
+ void test(int val) {
+ (void)consume(val); // If the value was negative, it is now large positive.
+ (void)consume((unsigned int)val); // OK, the conversion is explicit.
+ }
+
+ Like some other ``-fsanitize=integer`` checks, these issues are **not**
+ undefined behaviour. But they are not *always* intentional, and are somewhat
+ hard to track down. This group is **not** enabled by ``-fsanitize=undefined``,
+ but the ``-fsanitize=implicit-integer-sign-change`` check
+ is enabled by ``-fsanitize=integer``.
+ (as is ``-fsanitize=implicit-integer-truncation`` check)
Core Analysis Improvements
==========================
conversions - when either one, or both of the types are signed.
Issues caught by these sanitizers are not undefined behavior,
but are often unintentional.
+ - ``-fsanitize=implicit-integer-sign-change``: Implicit conversion between
+ integer types, if that changes the sign of the value. That is, if the the
+ original value was negative and the new value is positive (or zero),
+ or the original value was positive, and the new value is negative.
+ Issues caught by this sanitizer are not undefined behavior,
+ but are often unintentional.
- ``-fsanitize=integer-divide-by-zero``: Integer division by zero.
- ``-fsanitize=nonnull-attribute``: Passing null pointer as a function
parameter which is declared to never be null.
``nullability-*`` group of checks.
- ``-fsanitize=undefined-trap``: Deprecated alias of
``-fsanitize=undefined``.
+ - ``-fsanitize=implicit-integer-truncation``: Catches lossy integral
+ conversions. Enables ``implicit-signed-integer-truncation`` and
+ ``implicit-unsigned-integer-truncation``.
+ - ``-fsanitize=implicit-integer-arithmetic-value-change``: Catches implicit
+ conversions that change the arithmetic value of the integer. Enables
+ ``implicit-signed-integer-truncation`` and ``implicit-integer-sign-change``.
+ - ``-fsanitize=implicit-conversion``: Checks for suspicious
+ behaviour of implicit conversions. Enables
+ ``implicit-unsigned-integer-truncation``,
+ ``implicit-signed-integer-truncation`` and
+ ``implicit-integer-sign-change``.
- ``-fsanitize=integer``: Checks for undefined or suspicious integer
behavior (e.g. unsigned integer overflow).
Enables ``signed-integer-overflow``, ``unsigned-integer-overflow``,
- ``shift``, ``integer-divide-by-zero``, and ``implicit-integer-truncation``.
- - ``fsanitize=implicit-integer-truncation``: Checks for implicit integral
- conversions that result in data loss.
- Enables ``implicit-unsigned-integer-truncation`` and
- ``implicit-signed-integer-truncation``.
- - ``-fsanitize=implicit-conversion``: Checks for suspicious behaviours of
- implicit conversions.
- Currently, only ``-fsanitize=implicit-integer-truncation`` is implemented.
+ ``shift``, ``integer-divide-by-zero``,
+ ``implicit-unsigned-integer-truncation``,
+ ``implicit-signed-integer-truncation`` and
+ ``implicit-integer-sign-change``.
- ``-fsanitize=nullability``: Enables ``nullability-arg``,
``nullability-assign``, and ``nullability-return``. While violating
nullability does not have undefined behavior, it is often unintentional,
ImplicitUnsignedIntegerTruncation |
ImplicitSignedIntegerTruncation)
+SANITIZER("implicit-integer-sign-change", ImplicitIntegerSignChange)
+
+SANITIZER_GROUP("implicit-integer-arithmetic-value-change",
+ ImplicitIntegerArithmeticValueChange,
+ ImplicitIntegerSignChange | ImplicitSignedIntegerTruncation)
+
+// FIXME:
+//SANITIZER_GROUP("implicit-integer-conversion", ImplicitIntegerConversion,
+// ImplicitIntegerArithmeticValueChange |
+// ImplicitUnsignedIntegerTruncation)
+//SANITIZER_GROUP("implicit-conversion", ImplicitConversion,
+// ImplicitIntegerConversion)
+
SANITIZER_GROUP("implicit-conversion", ImplicitConversion,
- ImplicitIntegerTruncation)
+ ImplicitIntegerArithmeticValueChange |
+ ImplicitUnsignedIntegerTruncation)
SANITIZER_GROUP("integer", Integer,
- ImplicitIntegerTruncation | IntegerDivideByZero | Shift |
+ ImplicitConversion | IntegerDivideByZero | Shift |
SignedIntegerOverflow | UnsignedIntegerOverflow)
SANITIZER("local-bounds", LocalBounds)
ICCK_IntegerTruncation = 0, // Legacy, was only used by clang 7.
ICCK_UnsignedIntegerTruncation = 1,
ICCK_SignedIntegerTruncation = 2,
+ ICCK_IntegerSignChange = 3,
+ ICCK_SignedIntegerTruncationOrSignChange = 4,
};
/// Emit a check that an [implicit] truncation of an integer does not
void EmitIntegerTruncationCheck(Value *Src, QualType SrcType, Value *Dst,
QualType DstType, SourceLocation Loc);
+ /// Emit a check that an [implicit] conversion of an integer does not change
+ /// the sign of the value. It is not UB, so we use the value after conversion.
+ /// NOTE: Src and Dst may be the exact same value! (point to the same thing)
+ void EmitIntegerSignChangeCheck(Value *Src, QualType SrcType, Value *Dst,
+ QualType DstType, SourceLocation Loc);
+
/// Emit a conversion from the specified type to the specified destination
/// type, both of which are LLVM scalar types.
struct ScalarConversionOpts {
bool TreatBooleanAsSigned;
bool EmitImplicitIntegerTruncationChecks;
+ bool EmitImplicitIntegerSignChangeChecks;
ScalarConversionOpts()
: TreatBooleanAsSigned(false),
- EmitImplicitIntegerTruncationChecks(false) {}
+ EmitImplicitIntegerTruncationChecks(false),
+ EmitImplicitIntegerSignChangeChecks(false) {}
};
Value *
EmitScalarConversion(Value *Src, QualType SrcTy, QualType DstTy,
SanitizerHandler::FloatCastOverflow, StaticArgs, OrigSrc);
}
+// Should be called within CodeGenFunction::SanitizerScope RAII scope.
+// Returns 'i1 false' when the truncation Src -> Dst was lossy.
+static std::pair<ScalarExprEmitter::ImplicitConversionCheckKind,
+ std::pair<llvm::Value *, SanitizerMask>>
+EmitIntegerTruncationCheckHelper(Value *Src, QualType SrcType, Value *Dst,
+ QualType DstType, CGBuilderTy &Builder) {
+ llvm::Type *SrcTy = Src->getType();
+ llvm::Type *DstTy = Dst->getType();
+
+ // This should be truncation of integral types.
+ assert(Src != Dst);
+ assert(SrcTy->getScalarSizeInBits() > Dst->getType()->getScalarSizeInBits());
+ assert(isa<llvm::IntegerType>(SrcTy) && isa<llvm::IntegerType>(DstTy) &&
+ "non-integer llvm type");
+
+ bool SrcSigned = SrcType->isSignedIntegerOrEnumerationType();
+ bool DstSigned = DstType->isSignedIntegerOrEnumerationType();
+
+ // If both (src and dst) types are unsigned, then it's an unsigned truncation.
+ // Else, it is a signed truncation.
+ ScalarExprEmitter::ImplicitConversionCheckKind Kind;
+ SanitizerMask Mask;
+ if (!SrcSigned && !DstSigned) {
+ Kind = ScalarExprEmitter::ICCK_UnsignedIntegerTruncation;
+ Mask = SanitizerKind::ImplicitUnsignedIntegerTruncation;
+ } else {
+ Kind = ScalarExprEmitter::ICCK_SignedIntegerTruncation;
+ Mask = SanitizerKind::ImplicitSignedIntegerTruncation;
+ }
+
+ llvm::Value *Check = nullptr;
+ // 1. Extend the truncated value back to the same width as the Src.
+ Check = Builder.CreateIntCast(Dst, SrcTy, DstSigned, "anyext");
+ // 2. Equality-compare with the original source value
+ Check = Builder.CreateICmpEQ(Check, Src, "truncheck");
+ // If the comparison result is 'i1 false', then the truncation was lossy.
+ return std::make_pair(Kind, std::make_pair(Check, Mask));
+}
+
void ScalarExprEmitter::EmitIntegerTruncationCheck(Value *Src, QualType SrcType,
Value *Dst, QualType DstType,
SourceLocation Loc) {
if (!CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation))
return;
- llvm::Type *SrcTy = Src->getType();
- llvm::Type *DstTy = Dst->getType();
-
// We only care about int->int conversions here.
// We ignore conversions to/from pointer and/or bool.
if (!(SrcType->isIntegerType() && DstType->isIntegerType()))
return;
- assert(isa<llvm::IntegerType>(SrcTy) && isa<llvm::IntegerType>(DstTy) &&
- "clang integer type lowered to non-integer llvm type");
-
- unsigned SrcBits = SrcTy->getScalarSizeInBits();
- unsigned DstBits = DstTy->getScalarSizeInBits();
+ unsigned SrcBits = Src->getType()->getScalarSizeInBits();
+ unsigned DstBits = Dst->getType()->getScalarSizeInBits();
// This must be truncation. Else we do not care.
if (SrcBits <= DstBits)
return;
assert(!DstType->isBooleanType() && "we should not get here with booleans.");
+ // If the integer sign change sanitizer is enabled,
+ // and we are truncating from larger unsigned type to smaller signed type,
+ // let that next sanitizer deal with it.
bool SrcSigned = SrcType->isSignedIntegerOrEnumerationType();
bool DstSigned = DstType->isSignedIntegerOrEnumerationType();
+ if (CGF.SanOpts.has(SanitizerKind::ImplicitIntegerSignChange) &&
+ (!SrcSigned && DstSigned))
+ return;
- // If both (src and dst) types are unsigned, then it's an unsigned truncation.
- // Else, it is a signed truncation.
- ImplicitConversionCheckKind Kind;
- SanitizerMask Mask;
- if (!SrcSigned && !DstSigned) {
- Kind = ICCK_UnsignedIntegerTruncation;
- Mask = SanitizerKind::ImplicitUnsignedIntegerTruncation;
- } else {
- Kind = ICCK_SignedIntegerTruncation;
- Mask = SanitizerKind::ImplicitSignedIntegerTruncation;
- }
+ CodeGenFunction::SanitizerScope SanScope(&CGF);
+
+ std::pair<ScalarExprEmitter::ImplicitConversionCheckKind,
+ std::pair<llvm::Value *, SanitizerMask>>
+ Check =
+ EmitIntegerTruncationCheckHelper(Src, SrcType, Dst, DstType, Builder);
+ // If the comparison result is 'i1 false', then the truncation was lossy.
// Do we care about this type of truncation?
- if (!CGF.SanOpts.has(Mask))
+ if (!CGF.SanOpts.has(Check.second.second))
return;
- CodeGenFunction::SanitizerScope SanScope(&CGF);
+ llvm::Constant *StaticArgs[] = {
+ CGF.EmitCheckSourceLocation(Loc), CGF.EmitCheckTypeDescriptor(SrcType),
+ CGF.EmitCheckTypeDescriptor(DstType),
+ llvm::ConstantInt::get(Builder.getInt8Ty(), Check.first)};
+ CGF.EmitCheck(Check.second, SanitizerHandler::ImplicitConversion, StaticArgs,
+ {Src, Dst});
+}
+
+// Should be called within CodeGenFunction::SanitizerScope RAII scope.
+// Returns 'i1 false' when the conversion Src -> Dst changed the sign.
+static std::pair<ScalarExprEmitter::ImplicitConversionCheckKind,
+ std::pair<llvm::Value *, SanitizerMask>>
+EmitIntegerSignChangeCheckHelper(Value *Src, QualType SrcType, Value *Dst,
+ QualType DstType, CGBuilderTy &Builder) {
+ llvm::Type *SrcTy = Src->getType();
+ llvm::Type *DstTy = Dst->getType();
+
+ assert(isa<llvm::IntegerType>(SrcTy) && isa<llvm::IntegerType>(DstTy) &&
+ "non-integer llvm type");
+ bool SrcSigned = SrcType->isSignedIntegerOrEnumerationType();
+ bool DstSigned = DstType->isSignedIntegerOrEnumerationType();
+ unsigned SrcBits = SrcTy->getScalarSizeInBits();
+ unsigned DstBits = DstTy->getScalarSizeInBits();
+ (void)SrcBits; // Only used in assert()
+ (void)DstBits; // Only used in assert()
+
+ assert(((SrcBits != DstBits) || (SrcSigned != DstSigned)) &&
+ "either the widths should be different, or the signednesses.");
+
+ // NOTE: zero value is considered to be non-negative.
+ auto EmitIsNegativeTest = [&Builder](Value *V, QualType VType,
+ const char *Name) -> Value * {
+ // Is this value a signed type?
+ bool VSigned = VType->isSignedIntegerOrEnumerationType();
+ llvm::Type *VTy = V->getType();
+ if (!VSigned) {
+ // If the value is unsigned, then it is never negative.
+ // FIXME: can we encounter non-scalar VTy here?
+ return llvm::ConstantInt::getFalse(VTy->getContext());
+ }
+ // Get the zero of the same type with which we will be comparing.
+ llvm::Constant *Zero = llvm::ConstantInt::get(VTy, 0);
+ // %V.isnegative = icmp slt %V, 0
+ // I.e is %V *strictly* less than zero, does it have negative value?
+ return Builder.CreateICmp(llvm::ICmpInst::ICMP_SLT, V, Zero,
+ llvm::Twine(Name) + "." + V->getName() +
+ ".negativitycheck");
+ };
+
+ // 1. Was the old Value negative?
+ llvm::Value *SrcIsNegative = EmitIsNegativeTest(Src, SrcType, "src");
+ // 2. Is the new Value negative?
+ llvm::Value *DstIsNegative = EmitIsNegativeTest(Dst, DstType, "dst");
+ // 3. Now, was the 'negativity status' preserved during the conversion?
+ // NOTE: conversion from negative to zero is considered to change the sign.
+ // (We want to get 'false' when the conversion changed the sign)
+ // So we should just equality-compare the negativity statuses.
llvm::Value *Check = nullptr;
+ Check = Builder.CreateICmpEQ(SrcIsNegative, DstIsNegative, "signchangecheck");
+ // If the comparison result is 'false', then the conversion changed the sign.
+ return std::make_pair(
+ ScalarExprEmitter::ICCK_IntegerSignChange,
+ std::make_pair(Check, SanitizerKind::ImplicitIntegerSignChange));
+}
- // 1. Extend the truncated value back to the same width as the Src.
- Check = Builder.CreateIntCast(Dst, SrcTy, DstSigned, "anyext");
- // 2. Equality-compare with the original source value
- Check = Builder.CreateICmpEQ(Check, Src, "truncheck");
- // If the comparison result is 'i1 false', then the truncation was lossy.
+void ScalarExprEmitter::EmitIntegerSignChangeCheck(Value *Src, QualType SrcType,
+ Value *Dst, QualType DstType,
+ SourceLocation Loc) {
+ if (!CGF.SanOpts.has(SanitizerKind::ImplicitIntegerSignChange))
+ return;
+
+ llvm::Type *SrcTy = Src->getType();
+ llvm::Type *DstTy = Dst->getType();
+
+ // We only care about int->int conversions here.
+ // We ignore conversions to/from pointer and/or bool.
+ if (!(SrcType->isIntegerType() && DstType->isIntegerType()))
+ return;
+
+ bool SrcSigned = SrcType->isSignedIntegerOrEnumerationType();
+ bool DstSigned = DstType->isSignedIntegerOrEnumerationType();
+ unsigned SrcBits = SrcTy->getScalarSizeInBits();
+ unsigned DstBits = DstTy->getScalarSizeInBits();
+
+ // Now, we do not need to emit the check in *all* of the cases.
+ // We can avoid emitting it in some obvious cases where it would have been
+ // dropped by the opt passes (instcombine) always anyways.
+ // If it's a cast between the same type, just differently-sugared. no check.
+ QualType CanonSrcType = CGF.getContext().getCanonicalType(SrcType);
+ QualType CanonDstType = CGF.getContext().getCanonicalType(DstType);
+ if (CanonSrcType == CanonDstType)
+ return;
+ // At least one of the values needs to have signed type.
+ // If both are unsigned, then obviously, neither of them can be negative.
+ if (!SrcSigned && !DstSigned)
+ return;
+ // If the conversion is to *larger* *signed* type, then no check is needed.
+ // Because either sign-extension happens (so the sign will remain),
+ // or zero-extension will happen (the sign bit will be zero.)
+ if ((DstBits > SrcBits) && DstSigned)
+ return;
+ if (CGF.SanOpts.has(SanitizerKind::ImplicitSignedIntegerTruncation) &&
+ (SrcBits > DstBits) && SrcSigned) {
+ // If the signed integer truncation sanitizer is enabled,
+ // and this is a truncation from signed type, then no check is needed.
+ // Because here sign change check is interchangeable with truncation check.
+ return;
+ }
+ // That's it. We can't rule out any more cases with the data we have.
+
+ CodeGenFunction::SanitizerScope SanScope(&CGF);
+
+ std::pair<ScalarExprEmitter::ImplicitConversionCheckKind,
+ std::pair<llvm::Value *, SanitizerMask>>
+ Check;
+
+ // Each of these checks needs to return 'false' when an issue was detected.
+ ImplicitConversionCheckKind CheckKind;
+ llvm::SmallVector<std::pair<llvm::Value *, SanitizerMask>, 2> Checks;
+ // So we can 'and' all the checks together, and still get 'false',
+ // if at least one of the checks detected an issue.
+
+ Check = EmitIntegerSignChangeCheckHelper(Src, SrcType, Dst, DstType, Builder);
+ CheckKind = Check.first;
+ Checks.emplace_back(Check.second);
+
+ if (CGF.SanOpts.has(SanitizerKind::ImplicitSignedIntegerTruncation) &&
+ (SrcBits > DstBits) && !SrcSigned && DstSigned) {
+ // If the signed integer truncation sanitizer was enabled,
+ // and we are truncating from larger unsigned type to smaller signed type,
+ // let's handle the case we skipped in that check.
+ Check =
+ EmitIntegerTruncationCheckHelper(Src, SrcType, Dst, DstType, Builder);
+ CheckKind = ICCK_SignedIntegerTruncationOrSignChange;
+ Checks.emplace_back(Check.second);
+ // If the comparison result is 'i1 false', then the truncation was lossy.
+ }
llvm::Constant *StaticArgs[] = {
CGF.EmitCheckSourceLocation(Loc), CGF.EmitCheckTypeDescriptor(SrcType),
CGF.EmitCheckTypeDescriptor(DstType),
- llvm::ConstantInt::get(Builder.getInt8Ty(), Kind)};
- CGF.EmitCheck(std::make_pair(Check, Mask),
- SanitizerHandler::ImplicitConversion, StaticArgs, {Src, Dst});
+ llvm::ConstantInt::get(Builder.getInt8Ty(), CheckKind)};
+ // EmitCheck() will 'and' all the checks together.
+ CGF.EmitCheck(Checks, SanitizerHandler::ImplicitConversion, StaticArgs,
+ {Src, Dst});
}
/// Emit a conversion from the specified type to the specified destination type,
}
// Ignore conversions like int -> uint.
- if (SrcTy == DstTy)
+ if (SrcTy == DstTy) {
+ if (Opts.EmitImplicitIntegerSignChangeChecks)
+ EmitIntegerSignChangeCheck(Src, NoncanonicalSrcType, Src,
+ NoncanonicalDstType, Loc);
+
return Src;
+ }
// Handle pointer conversions next: pointers can only be converted to/from
// other pointers and integers. Check for pointer types in terms of LLVM, as
EmitIntegerTruncationCheck(Src, NoncanonicalSrcType, Res,
NoncanonicalDstType, Loc);
+ if (Opts.EmitImplicitIntegerSignChangeChecks)
+ EmitIntegerSignChangeCheck(Src, NoncanonicalSrcType, Res,
+ NoncanonicalDstType, Loc);
+
return Res;
}
case CK_IntegralCast: {
ScalarConversionOpts Opts;
- if (CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)) {
- if (auto *ICE = dyn_cast<ImplicitCastExpr>(CE))
- Opts.EmitImplicitIntegerTruncationChecks = !ICE->isPartOfExplicitCast();
+ if (auto *ICE = dyn_cast<ImplicitCastExpr>(CE)) {
+ if (CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitConversion) &&
+ !ICE->isPartOfExplicitCast()) {
+ Opts.EmitImplicitIntegerTruncationChecks =
+ CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation);
+ Opts.EmitImplicitIntegerSignChangeChecks =
+ CGF.SanOpts.has(SanitizerKind::ImplicitIntegerSignChange);
+ }
}
return EmitScalarConversion(Visit(E), E->getType(), DestTy,
CE->getExprLoc(), Opts);
--- /dev/null
+// RUN: %clang_cc1 -fsanitize=implicit-unsigned-integer-truncation,implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-recover=implicit-unsigned-integer-truncation,implicit-signed-integer-truncation,implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK
+
+// Test plan:
+// * Two types - int and char
+// * Two signs - signed and unsigned
+// * Square that - we have input and output types.
+// Thus, there are total of (2*2)^2 == 16 tests.
+// These are all the possible variations/combinations of casts.
+// However, not all of them should result in the check.
+// So here, we *only* check which should and which should not result in checks.
+
+// CHECK-DAG: @[[LINE_500_UNSIGNED_TRUNCATION:.*]] = {{.*}}, i32 500, i32 10 }, {{.*}}, {{.*}}, i8 1 }
+// CHECK-DAG: @[[LINE_900_SIGN_CHANGE:.*]] = {{.*}}, i32 900, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1000_SIGN_CHANGE:.*]] = {{.*}}, i32 1000, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1100_SIGNED_TRUNCATION:.*]] = {{.*}}, i32 1100, i32 10 }, {{.*}}, {{.*}}, i8 2 }
+// CHECK-DAG: @[[LINE_1200_SIGN_CHANGE:.*]] = {{.*}}, i32 1200, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1300_SIGN_CHANGE:.*]] = {{.*}}, i32 1300, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1400_SIGN_CHANGE:.*]] = {{.*}}, i32 1400, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1500_SIGNED_TRUNCATION_OR_SIGN_CHANGE:.*]] = {{.*}}, i32 1500, i32 10 }, {{.*}}, {{.*}}, i8 4 }
+// CHECK-DAG: @[[LINE_1600_SIGNED_TRUNCATION:.*]] = {{.*}}, i32 1600, i32 10 }, {{.*}}, {{.*}}, i8 2 }
+
+// CHECK-LABEL: @convert_unsigned_int_to_unsigned_int
+unsigned int convert_unsigned_int_to_unsigned_int(unsigned int x) {
+#line 100
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_char_to_unsigned_char
+unsigned char convert_unsigned_char_to_unsigned_char(unsigned char x) {
+#line 200
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_int_to_signed_int
+signed int convert_signed_int_to_signed_int(signed int x) {
+#line 300
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_char_to_signed_char
+signed char convert_signed_char_to_signed_char(signed char x) {
+#line 400
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_int_to_unsigned_char
+unsigned char convert_unsigned_int_to_unsigned_char(unsigned int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_500_UNSIGNED_TRUNCATION]] to i8*)
+#line 500
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_char_to_unsigned_int
+unsigned int convert_unsigned_char_to_unsigned_int(unsigned char x) {
+#line 600
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_char_to_signed_int
+signed int convert_unsigned_char_to_signed_int(unsigned char x) {
+#line 700
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_char_to_signed_int
+signed int convert_signed_char_to_signed_int(signed char x) {
+#line 800
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_int_to_signed_int
+signed int convert_unsigned_int_to_signed_int(unsigned int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_900_SIGN_CHANGE]] to i8*)
+#line 900
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_int_to_unsigned_int
+unsigned int convert_signed_int_to_unsigned_int(signed int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1000_SIGN_CHANGE]] to i8*)
+#line 1000
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_int_to_unsigned_char
+unsigned char convert_signed_int_to_unsigned_char(signed int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1100_SIGNED_TRUNCATION]] to i8*)
+#line 1100
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_char_to_unsigned_char
+unsigned char convert_signed_char_to_unsigned_char(signed char x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1200_SIGN_CHANGE]] to i8*)
+#line 1200
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_char_to_signed_char
+signed char convert_unsigned_char_to_signed_char(unsigned char x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1300_SIGN_CHANGE]] to i8*)
+#line 1300
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_char_to_unsigned_int
+unsigned int convert_signed_char_to_unsigned_int(signed char x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1400_SIGN_CHANGE]] to i8*)
+#line 1400
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_int_to_signed_char
+signed char convert_unsigned_int_to_signed_char(unsigned int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1500_SIGNED_TRUNCATION_OR_SIGN_CHANGE]] to i8*)
+#line 1500
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_int_to_signed_char
+signed char convert_signed_int_to_signed_char(signed int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1600_SIGNED_TRUNCATION]] to i8*)
+#line 1600
+ return x;
+}
--- /dev/null
+// RUN: %clang_cc1 -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK
+
+// Test plan:
+// * Two types - int and char
+// * Two signs - signed and unsigned
+// * Square that - we have input and output types.
+// Thus, there are total of (2*2)^2 == 16 tests.
+// These are all the possible variations/combinations of casts.
+// However, not all of them should result in the check.
+// So here, we *only* check which should and which should not result in checks.
+
+// CHECK-DAG: @[[LINE_900_SIGN_CHANGE:.*]] = {{.*}}, i32 900, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1000_SIGN_CHANGE:.*]] = {{.*}}, i32 1000, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1100_SIGNED_TRUNCATION:.*]] = {{.*}}, i32 1100, i32 10 }, {{.*}}, {{.*}}, i8 2 }
+// CHECK-DAG: @[[LINE_1200_SIGN_CHANGE:.*]] = {{.*}}, i32 1200, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1300_SIGN_CHANGE:.*]] = {{.*}}, i32 1300, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1400_SIGN_CHANGE:.*]] = {{.*}}, i32 1400, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1500_SIGNED_TRUNCATION_OR_SIGN_CHANGE:.*]] = {{.*}}, i32 1500, i32 10 }, {{.*}}, {{.*}}, i8 4 }
+// CHECK-DAG: @[[LINE_1600_SIGNED_TRUNCATION:.*]] = {{.*}}, i32 1600, i32 10 }, {{.*}}, {{.*}}, i8 2 }
+
+// CHECK-LABEL: @convert_unsigned_int_to_unsigned_int
+unsigned int convert_unsigned_int_to_unsigned_int(unsigned int x) {
+#line 100
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_char_to_unsigned_char
+unsigned char convert_unsigned_char_to_unsigned_char(unsigned char x) {
+#line 200
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_int_to_signed_int
+signed int convert_signed_int_to_signed_int(signed int x) {
+#line 300
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_char_to_signed_char
+signed char convert_signed_char_to_signed_char(signed char x) {
+#line 400
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_int_to_unsigned_char
+unsigned char convert_unsigned_int_to_unsigned_char(unsigned int x) {
+#line 500
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_char_to_unsigned_int
+unsigned int convert_unsigned_char_to_unsigned_int(unsigned char x) {
+#line 600
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_char_to_signed_int
+signed int convert_unsigned_char_to_signed_int(unsigned char x) {
+#line 700
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_char_to_signed_int
+signed int convert_signed_char_to_signed_int(signed char x) {
+#line 800
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_int_to_signed_int
+signed int convert_unsigned_int_to_signed_int(unsigned int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_900_SIGN_CHANGE]] to i8*)
+#line 900
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_int_to_unsigned_int
+unsigned int convert_signed_int_to_unsigned_int(signed int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1000_SIGN_CHANGE]] to i8*)
+#line 1000
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_int_to_unsigned_char
+unsigned char convert_signed_int_to_unsigned_char(signed int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1100_SIGNED_TRUNCATION]] to i8*)
+#line 1100
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_char_to_unsigned_char
+unsigned char convert_signed_char_to_unsigned_char(signed char x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1200_SIGN_CHANGE]] to i8*)
+#line 1200
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_char_to_signed_char
+signed char convert_unsigned_char_to_signed_char(unsigned char x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1300_SIGN_CHANGE]] to i8*)
+#line 1300
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_char_to_unsigned_int
+unsigned int convert_signed_char_to_unsigned_int(signed char x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1400_SIGN_CHANGE]] to i8*)
+#line 1400
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_int_to_signed_char
+signed char convert_unsigned_int_to_signed_char(unsigned int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1500_SIGNED_TRUNCATION_OR_SIGN_CHANGE]] to i8*)
+#line 1500
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_int_to_signed_char
+signed char convert_signed_int_to_signed_char(signed int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1600_SIGNED_TRUNCATION]] to i8*)
+#line 1600
+ return x;
+}
-// RUN: %clang_cc1 -fsanitize=implicit-unsigned-integer-truncation,implicit-signed-integer-truncation -fsanitize-recover=implicit-unsigned-integer-truncation,implicit-signed-integer-truncation -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK
+// RUN: %clang_cc1 -fsanitize=implicit-unsigned-integer-truncation,implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-recover=implicit-unsigned-integer-truncation,implicit-signed-integer-truncation,implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK
// Test plan:
// * Two types - int and char
// So here, we *only* check which should and which should not result in checks.
// CHECK-DAG: @[[LINE_500_UNSIGNED_TRUNCATION:.*]] = {{.*}}, i32 500, i32 10 }, {{.*}}, {{.*}}, i8 1 }
+// CHECK-DAG: @[[LINE_900_SIGN_CHANGE:.*]] = {{.*}}, i32 900, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1000_SIGN_CHANGE:.*]] = {{.*}}, i32 1000, i32 10 }, {{.*}}, {{.*}}, i8 3 }
// CHECK-DAG: @[[LINE_1100_SIGNED_TRUNCATION:.*]] = {{.*}}, i32 1100, i32 10 }, {{.*}}, {{.*}}, i8 2 }
-// CHECK-DAG: @[[LINE_1500_SIGNED_TRUNCATION:.*]] = {{.*}}, i32 1500, i32 10 }, {{.*}}, {{.*}}, i8 2 }
+// CHECK-DAG: @[[LINE_1200_SIGN_CHANGE:.*]] = {{.*}}, i32 1200, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1300_SIGN_CHANGE:.*]] = {{.*}}, i32 1300, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1400_SIGN_CHANGE:.*]] = {{.*}}, i32 1400, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1500_SIGNED_TRUNCATION_OR_SIGN_CHANGE:.*]] = {{.*}}, i32 1500, i32 10 }, {{.*}}, {{.*}}, i8 4 }
// CHECK-DAG: @[[LINE_1600_SIGNED_TRUNCATION:.*]] = {{.*}}, i32 1600, i32 10 }, {{.*}}, {{.*}}, i8 2 }
// CHECK-LABEL: @convert_unsigned_int_to_unsigned_int
// CHECK-LABEL: @convert_unsigned_int_to_signed_int
signed int convert_unsigned_int_to_signed_int(unsigned int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_900_SIGN_CHANGE]] to i8*)
#line 900
return x;
}
// CHECK-LABEL: @convert_signed_int_to_unsigned_int
unsigned int convert_signed_int_to_unsigned_int(signed int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1000_SIGN_CHANGE]] to i8*)
#line 1000
return x;
}
// CHECK-LABEL: @convert_signed_char_to_unsigned_char
unsigned char convert_signed_char_to_unsigned_char(signed char x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1200_SIGN_CHANGE]] to i8*)
#line 1200
return x;
}
// CHECK-LABEL: @convert_unsigned_char_to_signed_char
signed char convert_unsigned_char_to_signed_char(unsigned char x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1300_SIGN_CHANGE]] to i8*)
#line 1300
return x;
}
// CHECK-LABEL: @convert_signed_char_to_unsigned_int
unsigned int convert_signed_char_to_unsigned_int(signed char x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1400_SIGN_CHANGE]] to i8*)
#line 1400
return x;
}
// CHECK-LABEL: @convert_unsigned_int_to_signed_char
signed char convert_unsigned_int_to_signed_char(unsigned int x) {
- // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1500_SIGNED_TRUNCATION]] to i8*)
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1500_SIGNED_TRUNCATION_OR_SIGN_CHANGE]] to i8*)
#line 1500
return x;
}
--- /dev/null
+// RUN: %clang_cc1 -fsanitize=implicit-integer-sign-change -fsanitize-recover=implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK
+
+// Test plan:
+// * Two types - int and char
+// * Two signs - signed and unsigned
+// * Square that - we have input and output types.
+// Thus, there are total of (2*2)^2 == 16 tests.
+// These are all the possible variations/combinations of casts.
+// However, not all of them should result in the check.
+// So here, we *only* check which should and which should not result in checks.
+
+// CHECK-DAG: @[[LINE_900_SIGN_CHANGE:.*]] = {{.*}}, i32 900, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1000_SIGN_CHANGE:.*]] = {{.*}}, i32 1000, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1100_SIGN_CHANGE:.*]] = {{.*}}, i32 1100, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1200_SIGN_CHANGE:.*]] = {{.*}}, i32 1200, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1300_SIGN_CHANGE:.*]] = {{.*}}, i32 1300, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1400_SIGN_CHANGE:.*]] = {{.*}}, i32 1400, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1500_SIGN_CHANGE:.*]] = {{.*}}, i32 1500, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+// CHECK-DAG: @[[LINE_1600_SIGN_CHANGE:.*]] = {{.*}}, i32 1600, i32 10 }, {{.*}}, {{.*}}, i8 3 }
+
+//============================================================================//
+// Half of the cases do not need the check. //
+//============================================================================//
+
+//----------------------------------------------------------------------------//
+// No cast happens at all. No check needed.
+//----------------------------------------------------------------------------//
+
+// CHECK-LABEL: @convert_unsigned_int_to_unsigned_int
+unsigned int convert_unsigned_int_to_unsigned_int(unsigned int x) {
+#line 100
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_char_to_unsigned_char
+unsigned char convert_unsigned_char_to_unsigned_char(unsigned char x) {
+#line 200
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_int_to_signed_int
+signed int convert_signed_int_to_signed_int(signed int x) {
+#line 300
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_char_to_signed_char
+signed char convert_signed_char_to_signed_char(signed char x) {
+#line 400
+ return x;
+}
+
+//----------------------------------------------------------------------------//
+// Both types are unsigned. No check needed.
+//----------------------------------------------------------------------------//
+
+// CHECK-LABEL: @convert_unsigned_int_to_unsigned_char
+unsigned char convert_unsigned_int_to_unsigned_char(unsigned int x) {
+#line 500
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_char_to_unsigned_int
+unsigned int convert_unsigned_char_to_unsigned_int(unsigned char x) {
+#line 600
+ return x;
+}
+
+//----------------------------------------------------------------------------//
+// Source type was unsigned, destination type is signed, but non-negative.
+// Because zero-extension happens - the sign bit will be 0. No check needed.
+//----------------------------------------------------------------------------//
+
+// CHECK-LABEL: @convert_unsigned_char_to_signed_int
+signed int convert_unsigned_char_to_signed_int(unsigned char x) {
+#line 700
+ return x;
+}
+
+//----------------------------------------------------------------------------//
+// Both types are signed, and have the same sign, since sign-extension happens,
+// i.e. the sign bit will be propagated. No check needed.
+//----------------------------------------------------------------------------//
+
+// CHECK-LABEL: @convert_signed_char_to_signed_int
+signed int convert_signed_char_to_signed_int(signed char x) {
+#line 800
+ return x;
+}
+
+//============================================================================//
+// The remaining 8 cases *do* need the check. //
+//============================================================================//
+
+// These 3 result in simple 'icmp sge i32 %x, 0'
+
+// CHECK-LABEL: @convert_unsigned_int_to_signed_int
+signed int convert_unsigned_int_to_signed_int(unsigned int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_900_SIGN_CHANGE]] to i8*)
+#line 900
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_int_to_unsigned_int
+unsigned int convert_signed_int_to_unsigned_int(signed int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1000_SIGN_CHANGE]] to i8*)
+#line 1000
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_int_to_unsigned_char
+unsigned char convert_signed_int_to_unsigned_char(signed int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1100_SIGN_CHANGE]] to i8*)
+#line 1100
+ return x;
+}
+
+// These 3 result in simple 'icmp sge i8 %x, 0'
+
+// CHECK-LABEL: @convert_signed_char_to_unsigned_char
+unsigned char convert_signed_char_to_unsigned_char(signed char x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1200_SIGN_CHANGE]] to i8*)
+#line 1200
+ return x;
+}
+
+// CHECK-LABEL: @convert_unsigned_char_to_signed_char
+signed char convert_unsigned_char_to_signed_char(unsigned char x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1300_SIGN_CHANGE]] to i8*)
+#line 1300
+ return x;
+}
+
+// CHECK-LABEL: @convert_signed_char_to_unsigned_int
+unsigned int convert_signed_char_to_unsigned_int(signed char x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1400_SIGN_CHANGE]] to i8*)
+#line 1400
+ return x;
+}
+
+// 'icmp sge i8 (trunc i32 %x), 0'
+
+// CHECK-LABEL: @convert_unsigned_int_to_signed_char
+signed char convert_unsigned_int_to_signed_char(unsigned int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1500_SIGN_CHANGE]] to i8*)
+#line 1500
+ return x;
+}
+
+// 'xor i1 (icmp sge i8 (trunc i32 %x), 0), (icmp sge i32 %x, 0)'
+
+// CHECK-LABEL: @convert_signed_int_to_signed_char
+signed char convert_signed_int_to_signed_char(signed int x) {
+ // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1600_SIGN_CHANGE]] to i8*)
+#line 1600
+ return x;
+}
--- /dev/null
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefix=CHECK
+// RUN: %clang_cc1 -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER
+// RUN: %clang_cc1 -fsanitize=implicit-integer-sign-change -fsanitize-recover=implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER
+// RUN: %clang_cc1 -fsanitize=implicit-integer-sign-change -fsanitize-trap=implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP
+
+// ========================================================================== //
+// The expected true-negatives.
+// ========================================================================== //
+
+// Sanitization is explicitly disabled.
+// ========================================================================== //
+
+// CHECK-LABEL: @blacklist_0
+__attribute__((no_sanitize("undefined"))) unsigned int blacklist_0(signed int src) {
+ // We are not in "undefined" group, so that doesn't work.
+ // CHECK-SANITIZE: call
+ return src;
+}
+
+// CHECK-LABEL: @blacklist_1
+__attribute__((no_sanitize("integer"))) unsigned int blacklist_1(signed int src) {
+ return src;
+}
+
+// CHECK-LABEL: @blacklist_2
+__attribute__((no_sanitize("implicit-conversion"))) unsigned int blacklist_2(signed int src) {
+ return src;
+}
+
+// CHECK-LABEL: @blacklist_3
+__attribute__((no_sanitize("implicit-integer-sign-change"))) unsigned int blacklist_3(signed int src) {
+ return src;
+}
+
+// Explicit sign-changing conversions.
+// ========================================================================== //
+
+// CHECK-LABEL: explicit_signed_int_to_unsigned_int
+unsigned int explicit_signed_int_to_unsigned_int(signed int src) {
+ return (unsigned int)src;
+}
+
+// CHECK-LABEL: explicit_unsigned_int_to_signed_int
+signed int explicit_unsigned_int_to_signed_int(unsigned int src) {
+ return (signed int)src;
+}
+
+// Explicit NOP conversions.
+// ========================================================================== //
+
+// CHECK-LABEL: @explicit_ununsigned_int_to_ununsigned_int
+unsigned int explicit_ununsigned_int_to_ununsigned_int(unsigned int src) {
+ return (unsigned int)src;
+}
+
+// CHECK-LABEL: @explicit_unsigned_int_to_unsigned_int
+signed int explicit_unsigned_int_to_unsigned_int(signed int src) {
+ return (signed int)src;
+}
+
+// conversions to to boolean type are not counted as sign-change.
+// ========================================================================== //
+
+// CHECK-LABEL: @unsigned_int_to_bool
+_Bool unsigned_int_to_bool(unsigned int src) {
+ return src;
+}
+
+// CHECK-LABEL: @signed_int_to_bool
+_Bool signed_int_to_bool(signed int src) {
+ return src;
+}
+
+// CHECK-LABEL: @explicit_unsigned_int_to_bool
+_Bool explicit_unsigned_int_to_bool(unsigned int src) {
+ return (_Bool)src;
+}
+
+// CHECK-LABEL: @explicit_signed_int_to_bool
+_Bool explicit_signed_int_to_bool(signed int src) {
+ return (_Bool)src;
+}
+
+// Explicit conversions from pointer to an integer.
+// Can not have an implicit conversion from pointer to an integer.
+// Can not have an implicit conversion between two enums.
+// ========================================================================== //
+
+// CHECK-LABEL: @explicit_voidptr_to_unsigned_int
+unsigned int explicit_voidptr_to_unsigned_int(void *src) {
+ return (unsigned int)src;
+}
+
+// CHECK-LABEL: @explicit_voidptr_to_signed_int
+signed int explicit_voidptr_to_signed_int(void *src) {
+ return (signed int)src;
+}
+
+// Implicit conversions from floating-point.
+// ========================================================================== //
+
+// CHECK-LABEL: @float_to_unsigned_int
+unsigned int float_to_unsigned_int(float src) {
+ return src;
+}
+
+// CHECK-LABEL: @float_to_signed_int
+signed int float_to_signed_int(float src) {
+ return src;
+}
+
+// CHECK-LABEL: @double_to_unsigned_int
+unsigned int double_to_unsigned_int(double src) {
+ return src;
+}
+
+// CHECK-LABEL: @double_to_signed_int
+signed int double_to_signed_int(double src) {
+ return src;
+}
+
+// Sugar.
+// ========================================================================== //
+
+typedef unsigned int uint32_t;
+
+// CHECK-LABEL: @uint32_to_unsigned_int
+unsigned int uint32_to_unsigned_int(uint32_t src) {
+ return src;
+}
+
+// CHECK-LABEL: @unsigned_int_to_uint32
+uint32_t unsigned_int_to_uint32(unsigned int src) {
+ return src;
+}
+
+// CHECK-LABEL: @uint32_to_uint32
+uint32_t uint32_to_uint32(uint32_t src) {
+ return src;
+}
--- /dev/null
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE
+// RUN: %clang_cc1 -fsanitize=implicit-integer-sign-change -fsanitize-recover=implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER
+// RUN: %clang_cc1 -fsanitize=implicit-integer-sign-change -fsanitize-trap=implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE
+
+// CHECK-SANITIZE-ANYRECOVER: @[[UNSIGNED_INT:.*]] = {{.*}} c"'unsigned int'\00" }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[SIGNED_INT:.*]] = {{.*}} c"'int'\00" }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_100:.*]] = {{.*}}, i32 100, i32 10 }, {{.*}}* @[[UNSIGNED_INT]], {{.*}}* @[[SIGNED_INT]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_200:.*]] = {{.*}}, i32 200, i32 10 }, {{.*}}* @[[SIGNED_INT]], {{.*}}* @[[UNSIGNED_INT]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[UNSIGNED_CHAR:.*]] = {{.*}} c"'unsigned char'\00" }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_300:.*]] = {{.*}}, i32 300, i32 10 }, {{.*}}* @[[SIGNED_INT]], {{.*}}* @[[UNSIGNED_CHAR]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[SIGNED_CHAR:.*]] = {{.*}} c"'signed char'\00" }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_400:.*]] = {{.*}}, i32 400, i32 10 }, {{.*}}* @[[SIGNED_CHAR]], {{.*}}* @[[UNSIGNED_CHAR]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_500:.*]] = {{.*}}, i32 500, i32 10 }, {{.*}}* @[[UNSIGNED_CHAR]], {{.*}}* @[[SIGNED_CHAR]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_600:.*]] = {{.*}}, i32 600, i32 10 }, {{.*}}* @[[SIGNED_CHAR]], {{.*}}* @[[UNSIGNED_INT]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_700:.*]] = {{.*}}, i32 700, i32 10 }, {{.*}}* @[[UNSIGNED_INT]], {{.*}}* @[[SIGNED_CHAR]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_800:.*]] = {{.*}}, i32 800, i32 10 }, {{.*}}* @[[SIGNED_INT]], {{.*}}* @[[SIGNED_CHAR]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER: @[[UINT32:.*]] = {{.*}} c"'uint32_t' (aka 'unsigned int')\00" }
+// CHECK-SANITIZE-ANYRECOVER: @[[INT32:.*]] = {{.*}} c"'int32_t' (aka 'int')\00" }
+// CHECK-SANITIZE-ANYRECOVER: @[[LINE_900:.*]] = {{.*}}, i32 900, i32 10 }, {{.*}}* @[[UINT32]], {{.*}}* @[[INT32]], i8 3 }
+
+// ========================================================================== //
+// The expected true-positives.
+// These are implicit, potentially sign-altering, conversions.
+// ========================================================================== //
+
+// These 3 result (after optimizations) in simple 'icmp sge i32 %src, 0'.
+
+// CHECK-LABEL: @unsigned_int_to_signed_int
+// CHECK-SAME: (i32 %[[SRC:.*]])
+signed int unsigned_int_to_signed_int(unsigned int src) {
+ // CHECK: %[[SRC_ADDR:.*]] = alloca i32
+ // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+ // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i32 %[[DST]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 false, %[[DST_NEGATIVITYCHECK]], !nosanitize
+ // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+ // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTDST:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_100]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_100]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+ // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK-NEXT: ret i32 %[[DST]]
+ // CHECK-NEXT: }
+#line 100
+ return src;
+}
+
+// CHECK-LABEL: @signed_int_to_unsigned_int
+// CHECK-SAME: (i32 %[[SRC:.*]])
+unsigned int signed_int_to_unsigned_int(signed int src) {
+ // CHECK: %[[SRC_ADDR:.*]] = alloca i32
+ // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+ // CHECK-SANITIZE-NEXT: %[[SRC_NEGATIVITYCHECK:.*]] = icmp slt i32 %[[DST]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 %[[SRC_NEGATIVITYCHECK]], false, !nosanitize
+ // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+ // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTDST:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_200]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_200]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+ // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK-NEXT: ret i32 %[[DST]]
+ // CHECK-NEXT: }
+#line 200
+ return src;
+}
+
+// CHECK-LABEL: @signed_int_to_unsigned_char
+// CHECK-SAME: (i32 %[[SRC:.*]])
+unsigned char signed_int_to_unsigned_char(signed int src) {
+ // CHECK: %[[SRC_ADDR:.*]] = alloca i32
+ // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[CONV:.*]] = trunc i32 %[[DST]] to i8
+ // CHECK-SANITIZE-NEXT: %[[SRC_NEGATIVITYCHECK:.*]] = icmp slt i32 %[[DST]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 %[[SRC_NEGATIVITYCHECK]], false, !nosanitize
+ // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+ // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTCONV:.*]] = zext i8 %[[CONV]] to i64, !nosanitize
+ // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_300]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_300]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+ // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK-NEXT: ret i8 %[[CONV]]
+ // CHECK-NEXT: }
+#line 300
+ return src;
+}
+
+// These 3 result (after optimizations) in simple 'icmp sge i8 %src, 0'
+
+// CHECK-LABEL: @signed_char_to_unsigned_char
+// CHECK-SAME: (i8 signext %[[SRC:.*]])
+unsigned char signed_char_to_unsigned_char(signed char src) {
+ // CHECK: %[[SRC_ADDR:.*]] = alloca i8
+ // CHECK-NEXT: store i8 %[[SRC]], i8* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[DST:.*]] = load i8, i8* %[[SRC_ADDR]]
+ // CHECK-SANITIZE-NEXT: %[[SRC_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[DST]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 %[[SRC_NEGATIVITYCHECK]], false, !nosanitize
+ // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+ // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i8 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTDST:.*]] = zext i8 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_400]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_400]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+ // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK-NEXT: ret i8 %[[DST]]
+ // CHECK-NEXT: }
+#line 400
+ return src;
+}
+
+// CHECK-LABEL: @unsigned_char_to_signed_char
+// CHECK-SAME: (i8 zeroext %[[SRC:.*]])
+signed char unsigned_char_to_signed_char(unsigned char src) {
+ // CHECK: %[[SRC_ADDR:.*]] = alloca i8
+ // CHECK-NEXT: store i8 %[[SRC]], i8* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[DST:.*]] = load i8, i8* %[[SRC_ADDR]]
+ // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[DST]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 false, %[[DST_NEGATIVITYCHECK]], !nosanitize
+ // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+ // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i8 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTDST:.*]] = zext i8 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_500]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_500]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+ // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK-NEXT: ret i8 %[[DST]]
+ // CHECK-NEXT: }
+#line 500
+ return src;
+}
+
+// CHECK-LABEL: @signed_char_to_unsigned_int
+// CHECK-SAME: (i8 signext %[[SRC:.*]])
+unsigned int signed_char_to_unsigned_int(signed char src) {
+ // CHECK: %[[SRC_ADDR:.*]] = alloca i8
+ // CHECK-NEXT: store i8 %[[SRC]], i8* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[DST:.*]] = load i8, i8* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[CONV:.*]] = sext i8 %[[DST]] to i32
+ // CHECK-SANITIZE-NEXT: %[[SRC_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[DST]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 %[[SRC_NEGATIVITYCHECK]], false, !nosanitize
+ // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+ // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i8 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTCONV:.*]] = zext i32 %[[CONV]] to i64, !nosanitize
+ // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_600]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_600]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+ // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK-NEXT: ret i32 %[[CONV]]
+ // CHECK-NEXT: }
+#line 600
+ return src;
+}
+
+// This one result (after optimizations) in 'icmp sge i8 (trunc i32 %src), 0'
+
+// CHECK-LABEL: @unsigned_int_to_signed_char
+// CHECK-SAME: (i32 %[[SRC:.*]])
+signed char unsigned_int_to_signed_char(unsigned int src) {
+ // CHECK: %[[SRC_ADDR:.*]] = alloca i32
+ // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[CONV:.*]] = trunc i32 %[[DST]] to i8
+ // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[CONV]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 false, %[[DST_NEGATIVITYCHECK]], !nosanitize
+ // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+ // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTCONV:.*]] = zext i8 %[[CONV]] to i64, !nosanitize
+ // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_700]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_700]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+ // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK-NEXT: ret i8 %[[CONV]]
+ // CHECK-NEXT: }
+#line 700
+ return src;
+}
+
+// The worst one: 'xor i1 (icmp sge i8 (trunc i32 %x), 0), (icmp sge i32 %x, 0)'
+
+// CHECK-LABEL: @signed_int_to_signed_char
+// CHECK-SAME: (i32 %[[SRC:.*]])
+signed char signed_int_to_signed_char(signed int x) {
+ // CHECK: %[[SRC_ADDR:.*]] = alloca i32
+ // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[CONV:.*]] = trunc i32 %[[DST]] to i8
+ // CHECK-SANITIZE-NEXT: %[[SRC_NEGATIVITYCHECK:.*]] = icmp slt i32 %[[DST]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[CONV]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 %[[SRC_NEGATIVITYCHECK]], %[[DST_NEGATIVITYCHECK]], !nosanitize
+ // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+ // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTCONV:.*]] = zext i8 %[[CONV]] to i64, !nosanitize
+ // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_800]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_800]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+ // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK-NEXT: ret i8 %[[CONV]]
+ // CHECK-NEXT: }
+#line 800
+ return x;
+}
+
+// ========================================================================== //
+// Check canonical type stuff
+// ========================================================================== //
+
+typedef unsigned int uint32_t;
+typedef signed int int32_t;
+
+// CHECK-LABEL: @uint32_t_to_int32_t
+// CHECK-SAME: (i32 %[[SRC:.*]])
+int32_t uint32_t_to_int32_t(uint32_t src) {
+ // CHECK: %[[SRC_ADDR:.*]] = alloca i32
+ // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+ // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i32 %[[DST]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 false, %[[DST_NEGATIVITYCHECK]], !nosanitize
+ // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+ // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTDST:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_900]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_900]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+ // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK-NEXT: ret i32 %[[DST]]
+ // CHECK-NEXT: }
+#line 900
+ return src;
+}
+
+// ========================================================================== //
+// Check that explicit conversion does not interfere with implicit conversion
+// ========================================================================== //
+// These contain one implicit and one explicit sign-changing conversion.
+// We want to make sure that we still diagnose the implicit conversion.
+
+// Implicit sign-change after explicit sign-change.
+// CHECK-LABEL: @explicit_conversion_interference0
+unsigned int explicit_conversion_interference0(unsigned int c) {
+ // CHECK-SANITIZE: call
+ return (signed int)c;
+}
+
+// Implicit sign-change before explicit sign-change.
+// CHECK-LABEL: @explicit_conversion_interference1
+unsigned int explicit_conversion_interference1(unsigned int c) {
+ // CHECK-SANITIZE: call
+ signed int b;
+ return (unsigned int)(b = c);
+}
--- /dev/null
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE
+// RUN: %clang_cc1 -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER
+// RUN: %clang_cc1 -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-trap=implicit-signed-integer-truncation,implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE
+
+// CHECK-SANITIZE-ANYRECOVER: @[[UNSIGNED_INT:.*]] = {{.*}} c"'unsigned int'\00" }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[SIGNED_CHAR:.*]] = {{.*}} c"'signed char'\00" }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_100_SIGNED_TRUNCATION_OR_SIGN_CHANGE:.*]] = {{.*}}, i32 100, i32 10 }, {{.*}}* @[[UNSIGNED_INT]], {{.*}}* @[[SIGNED_CHAR]], i8 4 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_200_SIGN_CHANGE:.*]] = {{.*}}, i32 200, i32 10 }, {{.*}}* @[[UNSIGNED_INT]], {{.*}}* @[[SIGNED_CHAR]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_300_SIGN_CHANGE:.*]] = {{.*}}, i32 300, i32 10 }, {{.*}}* @[[UNSIGNED_INT]], {{.*}}* @[[SIGNED_CHAR]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_400_SIGNED_TRUNCATION:.*]] = {{.*}}, i32 400, i32 10 }, {{.*}}* @[[UNSIGNED_INT]], {{.*}}* @[[SIGNED_CHAR]], i8 2 }
+
+//============================================================================//
+// Both sanitizers are enabled, and not disabled per-function.
+//============================================================================//
+
+// CHECK-LABEL: @unsigned_int_to_signed_char
+// CHECK-SAME: (i32 %[[SRC:.*]])
+signed char unsigned_int_to_signed_char(unsigned int src) {
+ // CHECK-NEXT: [[ENTRY:.*]]:
+ // CHECK-NEXT: %[[SRC_ADDR:.*]] = alloca i32
+ // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[CONV:.*]] = trunc i32 %[[DST]] to i8
+ // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[CONV]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 false, %[[DST_NEGATIVITYCHECK]], !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[ANYEXT:.*]] = sext i8 %[[CONV]] to i32, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[TRUNCHECK:.*]] = icmp eq i32 %[[ANYEXT]], %[[DST]], !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[BOTHCHECKS:.*]] = and i1 %[[SIGNCHANGECHECK]], %[[TRUNCHECK]], !nosanitize
+ // CHECK-SANITIZE-NEXT: br i1 %[[BOTHCHECKS]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+ // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTCONV:.*]] = zext i8 %[[CONV]] to i64, !nosanitize
+ // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_100_SIGNED_TRUNCATION_OR_SIGN_CHANGE]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_100_SIGNED_TRUNCATION_OR_SIGN_CHANGE]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+ // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK-NEXT: ret i8 %[[CONV]]
+ // CHECK-NEXT: }
+#line 100
+ return src;
+}
+
+//============================================================================//
+// Truncation sanitizer is disabled per-function.
+//============================================================================//
+
+// CHECK-LABEL: @unsigned_int_to_signed_char__no_truncation_sanitizer
+// CHECK-SAME: (i32 %[[SRC:.*]])
+__attribute__((no_sanitize("implicit-integer-truncation"))) signed char
+unsigned_int_to_signed_char__no_truncation_sanitizer(unsigned int src) {
+ // CHECK-NEXT: [[ENTRY:.*]]:
+ // CHECK-NEXT: %[[SRC_ADDR:.*]] = alloca i32
+ // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[CONV:.*]] = trunc i32 %[[DST]] to i8
+ // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[CONV]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 false, %[[DST_NEGATIVITYCHECK]], !nosanitize
+ // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+ // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTCONV:.*]] = zext i8 %[[CONV]] to i64, !nosanitize
+ // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_200_SIGN_CHANGE]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_200_SIGN_CHANGE]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+ // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK-NEXT: ret i8 %[[CONV]]
+ // CHECK-NEXT: }
+#line 200
+ return src;
+}
+
+//============================================================================//
+// Signed truncation sanitizer is disabled per-function.
+//============================================================================//
+
+// CHECK-LABEL: @unsigned_int_to_signed_char__no_signed_truncation_sanitizer
+// CHECK-SAME: (i32 %[[SRC:.*]])
+__attribute__((no_sanitize("implicit-signed-integer-truncation"))) signed char
+unsigned_int_to_signed_char__no_signed_truncation_sanitizer(unsigned int src) {
+ // CHECK-NEXT: [[ENTRY:.*]]:
+ // CHECK-NEXT: %[[SRC_ADDR:.*]] = alloca i32
+ // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[CONV:.*]] = trunc i32 %[[DST]] to i8
+ // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[CONV]], 0, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 false, %[[DST_NEGATIVITYCHECK]], !nosanitize
+ // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+ // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTCONV:.*]] = zext i8 %[[CONV]] to i64, !nosanitize
+ // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_300_SIGN_CHANGE]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_300_SIGN_CHANGE]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+ // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK-NEXT: ret i8 %[[CONV]]
+ // CHECK-NEXT: }
+#line 300
+ return src;
+}
+
+//============================================================================//
+// Sign change sanitizer is disabled per-function
+//============================================================================//
+
+// CHECK-LABEL: @unsigned_int_to_signed_char__no_sign_change_sanitizer
+// CHECK-SAME: (i32 %[[SRC:.*]])
+__attribute__((no_sanitize("implicit-integer-sign-change"))) signed char
+unsigned_int_to_signed_char__no_sign_change_sanitizer(unsigned int src) {
+ // CHECK-NEXT: [[ENTRY:.*]]:
+ // CHECK-NEXT: %[[SRC_ADDR:.*]] = alloca i32
+ // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[CONV:.*]] = trunc i32 %[[DST]] to i8
+ // CHECK-SANITIZE-NEXT: %[[ANYEXT:.*]] = sext i8 %[[CONV]] to i32, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[TRUNCHECK:.*]] = icmp eq i32 %[[ANYEXT]], %[[DST]], !nosanitize
+ // CHECK-SANITIZE-NEXT: br i1 %[[TRUNCHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+ // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+ // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTCONV:.*]] = zext i8 %[[CONV]] to i64, !nosanitize
+ // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_400_SIGNED_TRUNCATION]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_400_SIGNED_TRUNCATION]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+ // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK-NEXT: ret i8 %[[CONV]]
+ // CHECK-NEXT: }
+#line 400
+ return src;
+}
+
+//============================================================================//
+// Both sanitizers are disabled per-function.
+//============================================================================//
+
+// CHECK-LABEL: @unsigned_int_to_signed_char__no_sanitizers
+// CHECK-SAME: (i32 %[[SRC:.*]])
+__attribute__((no_sanitize("implicit-integer-truncation"),
+ no_sanitize("implicit-integer-sign-change"))) signed char
+unsigned_int_to_signed_char__no_sanitizers(unsigned int src) {
+ // CHECK-NEXT: [[ENTRY:.*]]:
+ // CHECK-NEXT: %[[SRC_ADDR:.*]] = alloca i32
+ // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+ // CHECK-NEXT: %[[CONV:.*]] = trunc i32 %[[DST]] to i8
+ // CHECK-NEXT: ret i8 %[[CONV]]
+ // CHECK-NEXT: }
+ return src;
+}
--- /dev/null
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER
+// RUN: %clang_cc1 -fsanitize=implicit-integer-sign-change -fsanitize-recover=implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER
+// RUN: %clang_cc1 -fsanitize=implicit-integer-sign-change -fsanitize-trap=implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP
+
+extern "C" { // Disable name mangling.
+
+// ========================================================================== //
+// The expected true-negatives.
+// ========================================================================== //
+
+// Sanitization is explicitly disabled.
+// ========================================================================== //
+
+// CHECK-LABEL: @blacklist_0
+__attribute__((no_sanitize("undefined"))) unsigned int blacklist_0(signed int src) {
+ // We are not in "undefined" group, so that doesn't work.
+ // CHECK-SANITIZE: call
+ // CHECK: }
+ return src;
+}
+
+// CHECK-LABEL: @blacklist_1
+__attribute__((no_sanitize("integer"))) unsigned int blacklist_1(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return src;
+}
+
+// CHECK-LABEL: @blacklist_2
+__attribute__((no_sanitize("implicit-conversion"))) unsigned int blacklist_2(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return src;
+}
+
+// CHECK-LABEL: @blacklist_3
+__attribute__((no_sanitize("implicit-integer-sign-change"))) unsigned int blacklist_3(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return src;
+}
+
+// Explicit sign-changing conversions.
+// ========================================================================== //
+
+// CHECK-LABEL: @explicit_signed_int_to_unsigned_int
+unsigned int explicit_signed_int_to_unsigned_int(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (unsigned int)src;
+}
+
+// CHECK-LABEL: @explicit_unsigned_int_to_signed_int
+signed int explicit_unsigned_int_to_signed_int(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (signed int)src;
+}
+
+// Explicit NOP conversions.
+// ========================================================================== //
+
+// CHECK-LABEL: @explicit_unsigned_int_to_unsigned_int
+unsigned int explicit_unsigned_int_to_unsigned_int(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (unsigned int)src;
+}
+
+// CHECK-LABEL: @explicit_signed_int_to_signed_int
+signed int explicit_signed_int_to_signed_int(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (signed int)src;
+}
+
+// Explicit functional sign-changing casts.
+// ========================================================================== //
+
+using UnsignedInt = unsigned int;
+using SignedInt = signed int;
+
+// CHECK-LABEL: explicit_functional_unsigned_int_to_signed_int
+signed int explicit_functional_unsigned_int_to_signed_int(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return SignedInt(src);
+}
+
+// CHECK-LABEL: @explicit_functional_signed_int_to_unsigned_int
+unsigned int explicit_functional_signed_int_to_unsigned_int(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return UnsignedInt(src);
+}
+
+// Explicit functional NOP casts.
+// ========================================================================== //
+
+// CHECK-LABEL: @explicit_functional_unsigned_int_to_unsigned_int
+unsigned int explicit_functional_unsigned_int_to_unsigned_int(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return UnsignedInt(src);
+}
+
+// CHECK-LABEL: @explicit_functional_signed_int_to_signed_int
+signed int explicit_functional_signed_int_to_signed_int(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return SignedInt(src);
+}
+
+// Explicit C++-style sign-changing casts.
+// ========================================================================== //
+
+// CHECK-LABEL: @explicit_cppstyle_unsigned_int_to_signed_int
+signed int explicit_cppstyle_unsigned_int_to_signed_int(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return static_cast<signed int>(src);
+}
+
+// CHECK-LABEL: @explicit_cppstyle_signed_int_to_unsigned_int
+unsigned int explicit_cppstyle_signed_int_to_unsigned_int(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return static_cast<unsigned int>(src);
+}
+
+// Explicit C++-style casts NOP casts.
+// ========================================================================== //
+
+// CHECK-LABEL: @explicit_cppstyle_unsigned_int_to_unsigned_int
+unsigned int explicit_cppstyle_unsigned_int_to_unsigned_int(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return static_cast<unsigned int>(src);
+}
+
+// CHECK-LABEL: @explicit_cppstyle_signed_int_to_signed_int
+signed int explicit_cppstyle_signed_int_to_signed_int(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return static_cast<signed int>(src);
+}
+
+} // extern "C"
// CHECK-COVERAGE-WIN64: "--dependent-lib={{[^"]*}}ubsan_standalone-x86_64.lib"
// RUN: %clang -target x86_64-linux-gnu -fsanitize=integer %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INTEGER -implicit-check-not="-fsanitize-address-use-after-scope"
-// CHECK-INTEGER: "-fsanitize={{((signed-integer-overflow|unsigned-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){7}"}}
+// CHECK-INTEGER: "-fsanitize={{((signed-integer-overflow|unsigned-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){8}"}}
// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-conversion %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-conversion,CHECK-implicit-conversion-RECOVER
// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-conversion -fsanitize-recover=implicit-conversion %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-conversion,CHECK-implicit-conversion-RECOVER
// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-conversion -fno-sanitize-recover=implicit-conversion %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-conversion,CHECK-implicit-conversion-NORECOVER
// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-conversion -fsanitize-trap=implicit-conversion %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-conversion,CHECK-implicit-conversion-TRAP
-// CHECK-implicit-conversion: "-fsanitize={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
-// CHECK-implicit-conversion-RECOVER: "-fsanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
-// CHECK-implicit-conversion-RECOVER-NOT: "-fno-sanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
-// CHECK-implicit-conversion-RECOVER-NOT: "-fsanitize-trap={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
-// CHECK-implicit-conversion-NORECOVER-NOT: "-fno-sanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}} // ???
-// CHECK-implicit-conversion-NORECOVER-NOT: "-fsanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
-// CHECK-implicit-conversion-NORECOVER-NOT: "-fsanitize-trap={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
-// CHECK-implicit-conversion-TRAP: "-fsanitize-trap={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
-// CHECK-implicit-conversion-TRAP-NOT: "-fsanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
-// CHECK-implicit-conversion-TRAP-NOT: "-fno-sanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
+// CHECK-implicit-conversion: "-fsanitize={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+// CHECK-implicit-conversion-RECOVER: "-fsanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+// CHECK-implicit-conversion-RECOVER-NOT: "-fno-sanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+// CHECK-implicit-conversion-RECOVER-NOT: "-fsanitize-trap={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+// CHECK-implicit-conversion-NORECOVER-NOT: "-fno-sanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}} // ???
+// CHECK-implicit-conversion-NORECOVER-NOT: "-fsanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+// CHECK-implicit-conversion-NORECOVER-NOT: "-fsanitize-trap={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+// CHECK-implicit-conversion-TRAP: "-fsanitize-trap={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+// CHECK-implicit-conversion-TRAP-NOT: "-fsanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+// CHECK-implicit-conversion-TRAP-NOT: "-fno-sanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-integer-arithmetic-value-change %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-integer-arithmetic-value-change,CHECK-implicit-integer-arithmetic-value-change-RECOVER
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-integer-arithmetic-value-change -fsanitize-recover=implicit-integer-arithmetic-value-change %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-integer-arithmetic-value-change,CHECK-implicit-integer-arithmetic-value-change-RECOVER
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-integer-arithmetic-value-change -fno-sanitize-recover=implicit-integer-arithmetic-value-change %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-integer-arithmetic-value-change,CHECK-implicit-integer-arithmetic-value-change-NORECOVER
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-integer-arithmetic-value-change -fsanitize-trap=implicit-integer-arithmetic-value-change %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-integer-arithmetic-value-change,CHECK-implicit-integer-arithmetic-value-change-TRAP
+// CHECK-implicit-integer-arithmetic-value-change: "-fsanitize={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
+// CHECK-implicit-integer-arithmetic-value-change-RECOVER: "-fsanitize-recover={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
+// CHECK-implicit-integer-arithmetic-value-change-RECOVER-NOT: "-fno-sanitize-recover={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
+// CHECK-implicit-integer-arithmetic-value-change-RECOVER-NOT: "-fsanitize-trap={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
+// CHECK-implicit-integer-arithmetic-value-change-NORECOVER-NOT: "-fno-sanitize-recover={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}} // ???
+// CHECK-implicit-integer-arithmetic-value-change-NORECOVER-NOT: "-fsanitize-recover={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
+// CHECK-implicit-integer-arithmetic-value-change-NORECOVER-NOT: "-fsanitize-trap={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
+// CHECK-implicit-integer-arithmetic-value-change-TRAP: "-fsanitize-trap={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
+// CHECK-implicit-integer-arithmetic-value-change-TRAP-NOT: "-fsanitize-recover={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
+// CHECK-implicit-integer-arithmetic-value-change-TRAP-NOT: "-fno-sanitize-recover={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-integer-truncation %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-integer-truncation,CHECK-implicit-integer-truncation-RECOVER
// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-integer-truncation -fsanitize-recover=implicit-integer-truncation %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-integer-truncation,CHECK-implicit-integer-truncation-RECOVER