Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -168,6 +168,27 @@ 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``). + 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. + } + + Just like 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 + (as is ``-fsanitize=implicit-integer-truncation`` check) + is enabled by ``-fsanitize=integer``. Core Analysis Improvements ========================== Index: docs/UndefinedBehaviorSanitizer.rst =================================================================== --- docs/UndefinedBehaviorSanitizer.rst +++ docs/UndefinedBehaviorSanitizer.rst @@ -100,6 +100,12 @@ conversions - when either one, or both of the types are signed. Issues caught by this sanitizer 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. @@ -161,17 +167,26 @@ ``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-integer-conversion``: Checks for suspicious + behaviours of implicit integral conversions. Enables + ``implicit-unsigned-integer-truncation``, + ``implicit-signed-integer-truncation`` and + ``implicit-integer-sign-change``. + - ``-fsanitize=implicit-conversion``: Currently a direct alias for + ``-fsanitize=implicit-integer-conversion``. - ``-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, Index: include/clang/Basic/Sanitizers.def =================================================================== --- include/clang/Basic/Sanitizers.def +++ include/clang/Basic/Sanitizers.def @@ -139,11 +139,21 @@ ImplicitUnsignedIntegerTruncation | ImplicitSignedIntegerTruncation) +SANITIZER("implicit-integer-sign-change", ImplicitIntegerSignChange) + +SANITIZER_GROUP("implicit-integer-arithmetic-value-change", + ImplicitIntegerArithmeticValueChange, + ImplicitIntegerSignChange | ImplicitSignedIntegerTruncation) + +SANITIZER_GROUP("implicit-integer-conversion", ImplicitIntegerConversion, + ImplicitIntegerArithmeticValueChange | + ImplicitUnsignedIntegerTruncation) + SANITIZER_GROUP("implicit-conversion", ImplicitConversion, - ImplicitIntegerTruncation) + ImplicitIntegerConversion) SANITIZER_GROUP("integer", Integer, - ImplicitIntegerTruncation | IntegerDivideByZero | Shift | + ImplicitIntegerConversion | IntegerDivideByZero | Shift | SignedIntegerOverflow | UnsignedIntegerOverflow) SANITIZER("local-bounds", LocalBounds) Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -305,6 +305,8 @@ ICCK_IntegerTruncation = 0, // Legacy, no longer used. ICCK_UnsignedIntegerTruncation = 1, ICCK_SignedIntegerTruncation = 2, + ICCK_IntegerSignChange = 3, + ICCK_SignedIntegerTruncationOrSignChange = 4, }; /// Emit a check that an [implicit] truncation of an integer does not @@ -312,15 +314,23 @@ 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, @@ -943,12 +953,168 @@ 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> +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(SrcTy) && isa(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; + // We only care about int->int conversions here. + // We ignore conversions to/from pointer and/or bool. + if (!(SrcType->isIntegerType() && DstType->isIntegerType())) + return; + + 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; + + CodeGenFunction::SanitizerScope SanScope(&CGF); + + std::pair> + 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(Check.second.second)) + return; + + 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> +EmitIntegerSignChangeCheckHelper(Value *Src, QualType SrcType, Value *Dst, + QualType DstType, CGBuilderTy &Builder) { + llvm::Type *SrcTy = Src->getType(); + llvm::Type *DstTy = Dst->getType(); + + assert(isa(SrcTy) && isa(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(); + // So which 'lt' predicate for the comparison do we have to use? + // NOTE: if it is unsigned, then the comparison is naturally always 'false'. + llvm::ICmpInst::Predicate Pred = + VSigned ? llvm::ICmpInst::ICMP_SLT : llvm::ICmpInst::ICMP_ULT; + // Get the zero of the same type with which we will be comparing. + llvm::Type *VTy = V->getType(); + llvm::Constant *Zero = llvm::ConstantInt::get(VTy, 0); + // %V.isnegative = icmp [s/u]lt %V, 0 + // I.e is %V *strictly* less than zero, does it have negative value? + return Builder.CreateICmp(Pred, 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) + // Truth table: + // /-----------------|-----------------|-----------------\ + // | src is negative | dst is negative | no sign change? | + // |-----------------|-----------------|-----------------| + // | false | false | true | + // |-----------------|-----------------|-----------------| + // | false | true | false (!) | + // |-----------------|-----------------|-----------------| + // | true | false | false (!) | + // |-----------------|-----------------|-----------------| + // | true | true | true | + // |-----------------|-----------------|-----------------| + // 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)); +} + +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(); @@ -957,52 +1123,72 @@ if (!(SrcType->isIntegerType() && DstType->isIntegerType())) return; - assert(isa(SrcTy) && isa(DstTy) && - "clang integer type lowered to non-integer llvm type"); - - unsigned SrcBits = SrcTy->getScalarSizeInBits(); - unsigned DstBits = DstTy->getScalarSizeInBits(); - // This must be truncation. Else we do not care. - if (SrcBits <= DstBits) - return; - - assert(!DstType->isBooleanType() && "we should not get here with booleans."); - bool SrcSigned = SrcType->isSignedIntegerOrEnumerationType(); bool DstSigned = DstType->isSignedIntegerOrEnumerationType(); + unsigned SrcBits = SrcTy->getScalarSizeInBits(); + unsigned DstBits = DstTy->getScalarSizeInBits(); - // 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; - } - - // Do we care about this type of truncation? - if (!CGF.SanOpts.has(Mask)) + // 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); - llvm::Value *Check = nullptr; + std::pair> + Check; - // 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. + // Each of these checks needs to return 'false' when an issue was detected. + ImplicitConversionCheckKind CheckKind; + llvm::SmallVector, 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, @@ -1056,8 +1242,13 @@ } // 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 @@ -1201,6 +1392,10 @@ EmitIntegerTruncationCheck(Src, NoncanonicalSrcType, Res, NoncanonicalDstType, Loc); + if (Opts.EmitImplicitIntegerSignChangeChecks) + EmitIntegerSignChangeCheck(Src, NoncanonicalSrcType, Res, + NoncanonicalDstType, Loc); + return Res; } @@ -1896,9 +2091,14 @@ case CK_IntegralCast: { ScalarConversionOpts Opts; - if (CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)) { - if (auto *ICE = dyn_cast(CE)) - Opts.EmitImplicitIntegerTruncationChecks = !ICE->isPartOfExplicitCast(); + if (auto *ICE = dyn_cast(CE)) { + if (CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitIntegerConversion) && + !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); Index: test/CodeGen/catch-implicit-conversions-basics.c =================================================================== --- /dev/null +++ test/CodeGen/catch-implicit-conversions-basics.c @@ -0,0 +1,125 @@ +// 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; +} Index: test/CodeGen/catch-implicit-integer-arithmetic-value-change-basics.c =================================================================== --- /dev/null +++ test/CodeGen/catch-implicit-integer-arithmetic-value-change-basics.c @@ -0,0 +1,123 @@ +// 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; +} Index: test/CodeGen/catch-implicit-integer-conversions-basics.c =================================================================== --- test/CodeGen/catch-implicit-integer-conversions-basics.c +++ test/CodeGen/catch-implicit-integer-conversions-basics.c @@ -1,4 +1,4 @@ -// 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 @@ -10,8 +10,13 @@ // 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 @@ -65,12 +70,14 @@ // 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; } @@ -84,25 +91,28 @@ // 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; } Index: test/CodeGen/catch-implicit-integer-sign-changes-basics.c =================================================================== --- /dev/null +++ test/CodeGen/catch-implicit-integer-sign-changes-basics.c @@ -0,0 +1,157 @@ +// 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; +} Index: test/CodeGen/catch-implicit-integer-sign-changes-true-negatives.c =================================================================== --- /dev/null +++ test/CodeGen/catch-implicit-integer-sign-changes-true-negatives.c @@ -0,0 +1,140 @@ +// 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; +} Index: test/CodeGen/catch-implicit-integer-sign-changes.c =================================================================== --- /dev/null +++ test/CodeGen/catch-implicit-integer-sign-changes.c @@ -0,0 +1,281 @@ +// 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: %[[SRC_NEGATIVITYCHECK:.*]] = icmp ult i32 %[[DST]], 0, !nosanitize + // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i32 %[[DST]], 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: %[[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: %[[DST_NEGATIVITYCHECK:.*]] = icmp ult i32 %[[DST]], 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: %[[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: %[[DST_NEGATIVITYCHECK:.*]] = icmp ult 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_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: %[[DST_NEGATIVITYCHECK:.*]] = icmp ult i8 %[[DST]], 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 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: %[[SRC_NEGATIVITYCHECK:.*]] = icmp ult i8 %[[DST]], 0, !nosanitize + // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[DST]], 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 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: %[[DST_NEGATIVITYCHECK:.*]] = icmp ult i32 %[[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 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: %[[SRC_NEGATIVITYCHECK:.*]] = icmp ult 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_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: %[[SRC_NEGATIVITYCHECK:.*]] = icmp ult i32 %[[DST]], 0, !nosanitize + // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i32 %[[DST]], 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: %[[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); +} Index: test/CodeGen/catch-implicit-signed-integer-truncation-or-sign-change.c =================================================================== --- /dev/null +++ test/CodeGen/catch-implicit-signed-integer-truncation-or-sign-change.c @@ -0,0 +1,155 @@ +// 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: %[[SRC_NEGATIVITYCHECK:.*]] = icmp ult 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: %[[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: %[[SRC_NEGATIVITYCHECK:.*]] = icmp ult 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_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: %[[SRC_NEGATIVITYCHECK:.*]] = icmp ult 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_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; +} Index: test/CodeGenCXX/catch-implicit-integer-sign-changes-true-negatives.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/catch-implicit-integer-sign-changes-true-negatives.cpp @@ -0,0 +1,149 @@ +// 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(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(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(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(src); +} + +} // extern "C" Index: test/Driver/fsanitize.c =================================================================== --- test/Driver/fsanitize.c +++ test/Driver/fsanitize.c @@ -29,22 +29,52 @@ // 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-conversion %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-integer-conversion,CHECK-implicit-integer-conversion-RECOVER +// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-integer-conversion -fsanitize-recover=implicit-integer-conversion %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-integer-conversion,CHECK-implicit-integer-conversion-RECOVER +// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-integer-conversion -fno-sanitize-recover=implicit-integer-conversion %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-integer-conversion,CHECK-implicit-integer-conversion-NORECOVER +// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-integer-conversion -fsanitize-trap=implicit-integer-conversion %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-integer-conversion,CHECK-implicit-integer-conversion-TRAP +// CHECK-implicit-integer-conversion: "-fsanitize={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}} +// CHECK-implicit-integer-conversion-RECOVER: "-fsanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}} +// CHECK-implicit-integer-conversion-RECOVER-NOT: "-fno-sanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}} +// CHECK-implicit-integer-conversion-RECOVER-NOT: "-fsanitize-trap={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}} +// CHECK-implicit-integer-conversion-NORECOVER-NOT: "-fno-sanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}} // ??? +// CHECK-implicit-integer-conversion-NORECOVER-NOT: "-fsanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}} +// CHECK-implicit-integer-conversion-NORECOVER-NOT: "-fsanitize-trap={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}} +// CHECK-implicit-integer-conversion-TRAP: "-fsanitize-trap={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}} +// CHECK-implicit-integer-conversion-TRAP-NOT: "-fsanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}} +// CHECK-implicit-integer-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