]> granicus.if.org Git - clang/commitdiff
[clang][ubsan] Implicit Conversion Sanitizer - integer sign change - clang part
authorRoman Lebedev <lebedev.ri@gmail.com>
Tue, 30 Oct 2018 21:58:56 +0000 (21:58 +0000)
committerRoman Lebedev <lebedev.ri@gmail.com>
Tue, 30 Oct 2018 21:58:56 +0000 (21:58 +0000)
This is the second half of Implicit Integer Conversion Sanitizer.
It completes the first half, and finally makes the sanitizer
fully functional! Only the bitfield handling is missing.

Summary:
C and C++ are interesting languages. They are statically typed, but weakly.
The implicit conversions are allowed. This is nice, allows to write code
while balancing between getting drowned in everything being convertible,
and nothing being convertible. As usual, this comes with a price:

```
void consume(unsigned int val);

void test(int val) {
  consume(val);
  // The 'val' is `signed int`, but `consume()` takes `unsigned int`.
  // If val is negative, then consume() will be operating on a large
  // unsigned value, and you may or may not have a bug.

  // But yes, sometimes this is intentional.
  // Making the conversion explicit silences the sanitizer.
  consume((unsigned int)val);
}
```

Yes, there is a `-Wsign-conversion`` diagnostic group, but first, it is kinda
noisy, since it warns on everything (unlike sanitizers, warning on an
actual issues), and second, likely there are cases where it does **not** warn.

The actual detection is pretty easy. We just need to check each of the values
whether it is negative, and equality-compare the results of those comparisons.
The unsigned value is obviously non-negative. Zero is non-negative too.
https://godbolt.org/g/w93oj2

We do not have to emit the check *always*, there are obvious situations
where we can avoid emitting it, since it would **always** get optimized-out.
But i do think the tautological IR (`icmp ult %x, 0`, which is always false)
should be emitted, and the middle-end should cleanup it.

This sanitizer is in the `-fsanitize=implicit-conversion` group,
and is a logical continuation of D48958 `-fsanitize=implicit-integer-truncation`.
As for the ordering, i'we opted to emit the check **after**
`-fsanitize=implicit-integer-truncation`. At least on these simple 16 test cases,
this results in 1 of the 12 emitted checks being optimized away,
as compared to 0 checks being optimized away if the order is reversed.

This is a clang part.
The compiler-rt part is D50251.

Finishes fixing [[ https://bugs.llvm.org/show_bug.cgi?id=21530 | PR21530 ]], [[ https://bugs.llvm.org/show_bug.cgi?id=37552 | PR37552 ]], [[ https://bugs.llvm.org/show_bug.cgi?id=35409 | PR35409 ]].
Finishes partially fixing [[ https://bugs.llvm.org/show_bug.cgi?id=9821 | PR9821 ]].
Finishes fixing https://github.com/google/sanitizers/issues/940.

Only the bitfield handling is missing.

Reviewers: vsk, rsmith, rjmccall, #sanitizers, erichkeane

Reviewed By: rsmith

Subscribers: chandlerc, filcab, cfe-commits, regehr

Tags: #sanitizers, #clang

Differential Revision: https://reviews.llvm.org/D50250

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@345660 91177308-0d34-0410-b5e6-96231b3b80d8

13 files changed:
docs/ReleaseNotes.rst
docs/UndefinedBehaviorSanitizer.rst
include/clang/Basic/Sanitizers.def
lib/CodeGen/CGExprScalar.cpp
test/CodeGen/catch-implicit-conversions-basics.c [new file with mode: 0644]
test/CodeGen/catch-implicit-integer-arithmetic-value-change-basics.c [new file with mode: 0644]
test/CodeGen/catch-implicit-integer-conversions-basics.c
test/CodeGen/catch-implicit-integer-sign-changes-basics.c [new file with mode: 0644]
test/CodeGen/catch-implicit-integer-sign-changes-true-negatives.c [new file with mode: 0644]
test/CodeGen/catch-implicit-integer-sign-changes.c [new file with mode: 0644]
test/CodeGen/catch-implicit-signed-integer-truncation-or-sign-change.c [new file with mode: 0644]
test/CodeGenCXX/catch-implicit-integer-sign-changes-true-negatives.cpp [new file with mode: 0644]
test/Driver/fsanitize.c

index 3638c536581cf91bfa198c2dfb6f7afee798aecc..46a75cb97fda142199d6c34381330b81ffba682e 100644 (file)
@@ -198,6 +198,29 @@ Static Analyzer
 Undefined Behavior Sanitizer (UBSan)
 ------------------------------------
 
+* The Implicit Conversion Sanitizer (``-fsanitize=implicit-conversion``) group
+  was extended. One more type of issues is caught - implicit integer sign change.
+  (``-fsanitize=implicit-integer-sign-change``).
+  This makes the Implicit Conversion Sanitizer feature-complete,
+  with only missing piece being bitfield handling.
+  While there is a ``-Wsign-conversion`` diagnostic group that catches this kind
+  of issues, it is both noisy, and does not catch **all** the cases.
+
+  .. code-block:: c++
+
+      bool consume(unsigned int val);
+
+      void test(int val) {
+        (void)consume(val); // If the value was negative, it is now large positive.
+        (void)consume((unsigned int)val); // OK, the conversion is explicit.
+      }
+
+  Like some other ``-fsanitize=integer`` checks, these issues are **not**
+  undefined behaviour. But they are not *always* intentional, and are somewhat
+  hard to track down. This group is **not** enabled by ``-fsanitize=undefined``,
+  but the ``-fsanitize=implicit-integer-sign-change`` check
+  is enabled by ``-fsanitize=integer``.
+  (as is ``-fsanitize=implicit-integer-truncation`` check)
 
 Core Analysis Improvements
 ==========================
index c971f634d7af1329a9a6e2e87146cc2a4fb87a93..48658e65346916dc037644ca93e37c670258df76 100644 (file)
@@ -100,6 +100,12 @@ Available checks are:
      conversions - when either one, or both of the types are signed.
      Issues caught by these sanitizers are not undefined behavior,
      but are often unintentional.
+  -  ``-fsanitize=implicit-integer-sign-change``: Implicit conversion between
+     integer types, if that changes the sign of the value. That is, if the the
+     original value was negative and the new value is positive (or zero),
+     or the original value was positive, and the new value is negative.
+     Issues caught by this sanitizer are not undefined behavior,
+     but are often unintentional.
   -  ``-fsanitize=integer-divide-by-zero``: Integer division by zero.
   -  ``-fsanitize=nonnull-attribute``: Passing null pointer as a function
      parameter which is declared to never be null.
@@ -161,17 +167,24 @@ You can also use the following check groups:
      ``nullability-*`` group of checks.
   -  ``-fsanitize=undefined-trap``: Deprecated alias of
      ``-fsanitize=undefined``.
+  -  ``-fsanitize=implicit-integer-truncation``: Catches lossy integral
+     conversions. Enables ``implicit-signed-integer-truncation`` and
+     ``implicit-unsigned-integer-truncation``.
+  -  ``-fsanitize=implicit-integer-arithmetic-value-change``: Catches implicit
+     conversions that change the arithmetic value of the integer. Enables
+     ``implicit-signed-integer-truncation`` and ``implicit-integer-sign-change``.
+  -  ``-fsanitize=implicit-conversion``: Checks for suspicious
+     behaviour of implicit conversions. Enables
+     ``implicit-unsigned-integer-truncation``,
+     ``implicit-signed-integer-truncation`` and
+     ``implicit-integer-sign-change``.
   -  ``-fsanitize=integer``: Checks for undefined or suspicious integer
      behavior (e.g. unsigned integer overflow).
      Enables ``signed-integer-overflow``, ``unsigned-integer-overflow``,
-     ``shift``, ``integer-divide-by-zero``, and ``implicit-integer-truncation``.
-  -  ``fsanitize=implicit-integer-truncation``: Checks for implicit integral
-     conversions that result in data loss.
-     Enables ``implicit-unsigned-integer-truncation`` and
-     ``implicit-signed-integer-truncation``.
-  -  ``-fsanitize=implicit-conversion``: Checks for suspicious behaviours of
-     implicit conversions.
-     Currently, only ``-fsanitize=implicit-integer-truncation`` is implemented.
+     ``shift``, ``integer-divide-by-zero``,
+     ``implicit-unsigned-integer-truncation``,
+     ``implicit-signed-integer-truncation`` and
+     ``implicit-integer-sign-change``.
   -  ``-fsanitize=nullability``: Enables ``nullability-arg``,
      ``nullability-assign``, and ``nullability-return``. While violating
      nullability does not have undefined behavior, it is often unintentional,
index 41bfc064d85c11c31bb4a29d9c8db66bae1f147b..0287468d71f58273522e2081ddb69001b8e677c5 100644 (file)
@@ -142,11 +142,25 @@ SANITIZER_GROUP("implicit-integer-truncation", ImplicitIntegerTruncation,
                 ImplicitUnsignedIntegerTruncation |
                     ImplicitSignedIntegerTruncation)
 
+SANITIZER("implicit-integer-sign-change", ImplicitIntegerSignChange)
+
+SANITIZER_GROUP("implicit-integer-arithmetic-value-change",
+                ImplicitIntegerArithmeticValueChange,
+                ImplicitIntegerSignChange | ImplicitSignedIntegerTruncation)
+
+// FIXME:
+//SANITIZER_GROUP("implicit-integer-conversion", ImplicitIntegerConversion,
+//                ImplicitIntegerArithmeticValueChange |
+//                    ImplicitUnsignedIntegerTruncation)
+//SANITIZER_GROUP("implicit-conversion", ImplicitConversion,
+//                ImplicitIntegerConversion)
+
 SANITIZER_GROUP("implicit-conversion", ImplicitConversion,
-                ImplicitIntegerTruncation)
+                ImplicitIntegerArithmeticValueChange |
+                    ImplicitUnsignedIntegerTruncation)
 
 SANITIZER_GROUP("integer", Integer,
-                ImplicitIntegerTruncation | IntegerDivideByZero | Shift |
+                ImplicitConversion | IntegerDivideByZero | Shift |
                     SignedIntegerOverflow | UnsignedIntegerOverflow)
 
 SANITIZER("local-bounds", LocalBounds)
index a2b3f99573eb77c4f1de185a890cd572665c31c4..e95cd7d2d63a3cb4b4654bc9e5af2091372a3103 100644 (file)
@@ -306,6 +306,8 @@ public:
     ICCK_IntegerTruncation = 0, // Legacy, was only used by clang 7.
     ICCK_UnsignedIntegerTruncation = 1,
     ICCK_SignedIntegerTruncation = 2,
+    ICCK_IntegerSignChange = 3,
+    ICCK_SignedIntegerTruncationOrSignChange = 4,
   };
 
   /// Emit a check that an [implicit] truncation of an integer  does not
@@ -313,15 +315,23 @@ public:
   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,
@@ -947,66 +957,231 @@ void ScalarExprEmitter::EmitFloatConversionCheck(
                 SanitizerHandler::FloatCastOverflow, StaticArgs, OrigSrc);
 }
 
+// Should be called within CodeGenFunction::SanitizerScope RAII scope.
+// Returns 'i1 false' when the truncation Src -> Dst was lossy.
+static std::pair<ScalarExprEmitter::ImplicitConversionCheckKind,
+                 std::pair<llvm::Value *, SanitizerMask>>
+EmitIntegerTruncationCheckHelper(Value *Src, QualType SrcType, Value *Dst,
+                                 QualType DstType, CGBuilderTy &Builder) {
+  llvm::Type *SrcTy = Src->getType();
+  llvm::Type *DstTy = Dst->getType();
+
+  // This should be truncation of integral types.
+  assert(Src != Dst);
+  assert(SrcTy->getScalarSizeInBits() > Dst->getType()->getScalarSizeInBits());
+  assert(isa<llvm::IntegerType>(SrcTy) && isa<llvm::IntegerType>(DstTy) &&
+         "non-integer llvm type");
+
+  bool SrcSigned = SrcType->isSignedIntegerOrEnumerationType();
+  bool DstSigned = DstType->isSignedIntegerOrEnumerationType();
+
+  // If both (src and dst) types are unsigned, then it's an unsigned truncation.
+  // Else, it is a signed truncation.
+  ScalarExprEmitter::ImplicitConversionCheckKind Kind;
+  SanitizerMask Mask;
+  if (!SrcSigned && !DstSigned) {
+    Kind = ScalarExprEmitter::ICCK_UnsignedIntegerTruncation;
+    Mask = SanitizerKind::ImplicitUnsignedIntegerTruncation;
+  } else {
+    Kind = ScalarExprEmitter::ICCK_SignedIntegerTruncation;
+    Mask = SanitizerKind::ImplicitSignedIntegerTruncation;
+  }
+
+  llvm::Value *Check = nullptr;
+  // 1. Extend the truncated value back to the same width as the Src.
+  Check = Builder.CreateIntCast(Dst, SrcTy, DstSigned, "anyext");
+  // 2. Equality-compare with the original source value
+  Check = Builder.CreateICmpEQ(Check, Src, "truncheck");
+  // If the comparison result is 'i1 false', then the truncation was lossy.
+  return std::make_pair(Kind, std::make_pair(Check, Mask));
+}
+
 void ScalarExprEmitter::EmitIntegerTruncationCheck(Value *Src, QualType SrcType,
                                                    Value *Dst, QualType DstType,
                                                    SourceLocation Loc) {
   if (!CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation))
     return;
 
-  llvm::Type *SrcTy = Src->getType();
-  llvm::Type *DstTy = Dst->getType();
-
   // We only care about int->int conversions here.
   // We ignore conversions to/from pointer and/or bool.
   if (!(SrcType->isIntegerType() && DstType->isIntegerType()))
     return;
 
-  assert(isa<llvm::IntegerType>(SrcTy) && isa<llvm::IntegerType>(DstTy) &&
-         "clang integer type lowered to non-integer llvm type");
-
-  unsigned SrcBits = SrcTy->getScalarSizeInBits();
-  unsigned DstBits = DstTy->getScalarSizeInBits();
+  unsigned SrcBits = Src->getType()->getScalarSizeInBits();
+  unsigned DstBits = Dst->getType()->getScalarSizeInBits();
   // This must be truncation. Else we do not care.
   if (SrcBits <= DstBits)
     return;
 
   assert(!DstType->isBooleanType() && "we should not get here with booleans.");
 
+  // If the integer sign change sanitizer is enabled,
+  // and we are truncating from larger unsigned type to smaller signed type,
+  // let that next sanitizer deal with it.
   bool SrcSigned = SrcType->isSignedIntegerOrEnumerationType();
   bool DstSigned = DstType->isSignedIntegerOrEnumerationType();
+  if (CGF.SanOpts.has(SanitizerKind::ImplicitIntegerSignChange) &&
+      (!SrcSigned && DstSigned))
+    return;
 
-  // If both (src and dst) types are unsigned, then it's an unsigned truncation.
-  // Else, it is a signed truncation.
-  ImplicitConversionCheckKind Kind;
-  SanitizerMask Mask;
-  if (!SrcSigned && !DstSigned) {
-    Kind = ICCK_UnsignedIntegerTruncation;
-    Mask = SanitizerKind::ImplicitUnsignedIntegerTruncation;
-  } else {
-    Kind = ICCK_SignedIntegerTruncation;
-    Mask = SanitizerKind::ImplicitSignedIntegerTruncation;
-  }
+  CodeGenFunction::SanitizerScope SanScope(&CGF);
+
+  std::pair<ScalarExprEmitter::ImplicitConversionCheckKind,
+            std::pair<llvm::Value *, SanitizerMask>>
+      Check =
+          EmitIntegerTruncationCheckHelper(Src, SrcType, Dst, DstType, Builder);
+  // If the comparison result is 'i1 false', then the truncation was lossy.
 
   // Do we care about this type of truncation?
-  if (!CGF.SanOpts.has(Mask))
+  if (!CGF.SanOpts.has(Check.second.second))
     return;
 
-  CodeGenFunction::SanitizerScope SanScope(&CGF);
+  llvm::Constant *StaticArgs[] = {
+      CGF.EmitCheckSourceLocation(Loc), CGF.EmitCheckTypeDescriptor(SrcType),
+      CGF.EmitCheckTypeDescriptor(DstType),
+      llvm::ConstantInt::get(Builder.getInt8Ty(), Check.first)};
+  CGF.EmitCheck(Check.second, SanitizerHandler::ImplicitConversion, StaticArgs,
+                {Src, Dst});
+}
+
+// Should be called within CodeGenFunction::SanitizerScope RAII scope.
+// Returns 'i1 false' when the conversion Src -> Dst changed the sign.
+static std::pair<ScalarExprEmitter::ImplicitConversionCheckKind,
+                 std::pair<llvm::Value *, SanitizerMask>>
+EmitIntegerSignChangeCheckHelper(Value *Src, QualType SrcType, Value *Dst,
+                                 QualType DstType, CGBuilderTy &Builder) {
+  llvm::Type *SrcTy = Src->getType();
+  llvm::Type *DstTy = Dst->getType();
+
+  assert(isa<llvm::IntegerType>(SrcTy) && isa<llvm::IntegerType>(DstTy) &&
+         "non-integer llvm type");
 
+  bool SrcSigned = SrcType->isSignedIntegerOrEnumerationType();
+  bool DstSigned = DstType->isSignedIntegerOrEnumerationType();
+  unsigned SrcBits = SrcTy->getScalarSizeInBits();
+  unsigned DstBits = DstTy->getScalarSizeInBits();
+  (void)SrcBits; // Only used in assert()
+  (void)DstBits; // Only used in assert()
+
+  assert(((SrcBits != DstBits) || (SrcSigned != DstSigned)) &&
+         "either the widths should be different, or the signednesses.");
+
+  // NOTE: zero value is considered to be non-negative.
+  auto EmitIsNegativeTest = [&Builder](Value *V, QualType VType,
+                                       const char *Name) -> Value * {
+    // Is this value a signed type?
+    bool VSigned = VType->isSignedIntegerOrEnumerationType();
+    llvm::Type *VTy = V->getType();
+    if (!VSigned) {
+      // If the value is unsigned, then it is never negative.
+      // FIXME: can we encounter non-scalar VTy here?
+      return llvm::ConstantInt::getFalse(VTy->getContext());
+    }
+    // Get the zero of the same type with which we will be comparing.
+    llvm::Constant *Zero = llvm::ConstantInt::get(VTy, 0);
+    // %V.isnegative = icmp slt %V, 0
+    // I.e is %V *strictly* less than zero, does it have negative value?
+    return Builder.CreateICmp(llvm::ICmpInst::ICMP_SLT, V, Zero,
+                              llvm::Twine(Name) + "." + V->getName() +
+                                  ".negativitycheck");
+  };
+
+  // 1. Was the old Value negative?
+  llvm::Value *SrcIsNegative = EmitIsNegativeTest(Src, SrcType, "src");
+  // 2. Is the new Value negative?
+  llvm::Value *DstIsNegative = EmitIsNegativeTest(Dst, DstType, "dst");
+  // 3. Now, was the 'negativity status' preserved during the conversion?
+  //    NOTE: conversion from negative to zero is considered to change the sign.
+  //    (We want to get 'false' when the conversion changed the sign)
+  //    So we should just equality-compare the negativity statuses.
   llvm::Value *Check = nullptr;
+  Check = Builder.CreateICmpEQ(SrcIsNegative, DstIsNegative, "signchangecheck");
+  // If the comparison result is 'false', then the conversion changed the sign.
+  return std::make_pair(
+      ScalarExprEmitter::ICCK_IntegerSignChange,
+      std::make_pair(Check, SanitizerKind::ImplicitIntegerSignChange));
+}
 
-  // 1. Extend the truncated value back to the same width as the Src.
-  Check = Builder.CreateIntCast(Dst, SrcTy, DstSigned, "anyext");
-  // 2. Equality-compare with the original source value
-  Check = Builder.CreateICmpEQ(Check, Src, "truncheck");
-  // If the comparison result is 'i1 false', then the truncation was lossy.
+void ScalarExprEmitter::EmitIntegerSignChangeCheck(Value *Src, QualType SrcType,
+                                                   Value *Dst, QualType DstType,
+                                                   SourceLocation Loc) {
+  if (!CGF.SanOpts.has(SanitizerKind::ImplicitIntegerSignChange))
+    return;
+
+  llvm::Type *SrcTy = Src->getType();
+  llvm::Type *DstTy = Dst->getType();
+
+  // We only care about int->int conversions here.
+  // We ignore conversions to/from pointer and/or bool.
+  if (!(SrcType->isIntegerType() && DstType->isIntegerType()))
+    return;
+
+  bool SrcSigned = SrcType->isSignedIntegerOrEnumerationType();
+  bool DstSigned = DstType->isSignedIntegerOrEnumerationType();
+  unsigned SrcBits = SrcTy->getScalarSizeInBits();
+  unsigned DstBits = DstTy->getScalarSizeInBits();
+
+  // Now, we do not need to emit the check in *all* of the cases.
+  // We can avoid emitting it in some obvious cases where it would have been
+  // dropped by the opt passes (instcombine) always anyways.
+  // If it's a cast between the same type, just differently-sugared. no check.
+  QualType CanonSrcType = CGF.getContext().getCanonicalType(SrcType);
+  QualType CanonDstType = CGF.getContext().getCanonicalType(DstType);
+  if (CanonSrcType == CanonDstType)
+    return;
+  // At least one of the values needs to have signed type.
+  // If both are unsigned, then obviously, neither of them can be negative.
+  if (!SrcSigned && !DstSigned)
+    return;
+  // If the conversion is to *larger* *signed* type, then no check is needed.
+  // Because either sign-extension happens (so the sign will remain),
+  // or zero-extension will happen (the sign bit will be zero.)
+  if ((DstBits > SrcBits) && DstSigned)
+    return;
+  if (CGF.SanOpts.has(SanitizerKind::ImplicitSignedIntegerTruncation) &&
+      (SrcBits > DstBits) && SrcSigned) {
+    // If the signed integer truncation sanitizer is enabled,
+    // and this is a truncation from signed type, then no check is needed.
+    // Because here sign change check is interchangeable with truncation check.
+    return;
+  }
+  // That's it. We can't rule out any more cases with the data we have.
+
+  CodeGenFunction::SanitizerScope SanScope(&CGF);
+
+  std::pair<ScalarExprEmitter::ImplicitConversionCheckKind,
+            std::pair<llvm::Value *, SanitizerMask>>
+      Check;
+
+  // Each of these checks needs to return 'false' when an issue was detected.
+  ImplicitConversionCheckKind CheckKind;
+  llvm::SmallVector<std::pair<llvm::Value *, SanitizerMask>, 2> Checks;
+  // So we can 'and' all the checks together, and still get 'false',
+  // if at least one of the checks detected an issue.
+
+  Check = EmitIntegerSignChangeCheckHelper(Src, SrcType, Dst, DstType, Builder);
+  CheckKind = Check.first;
+  Checks.emplace_back(Check.second);
+
+  if (CGF.SanOpts.has(SanitizerKind::ImplicitSignedIntegerTruncation) &&
+      (SrcBits > DstBits) && !SrcSigned && DstSigned) {
+    // If the signed integer truncation sanitizer was enabled,
+    // and we are truncating from larger unsigned type to smaller signed type,
+    // let's handle the case we skipped in that check.
+    Check =
+        EmitIntegerTruncationCheckHelper(Src, SrcType, Dst, DstType, Builder);
+    CheckKind = ICCK_SignedIntegerTruncationOrSignChange;
+    Checks.emplace_back(Check.second);
+    // If the comparison result is 'i1 false', then the truncation was lossy.
+  }
 
   llvm::Constant *StaticArgs[] = {
       CGF.EmitCheckSourceLocation(Loc), CGF.EmitCheckTypeDescriptor(SrcType),
       CGF.EmitCheckTypeDescriptor(DstType),
-      llvm::ConstantInt::get(Builder.getInt8Ty(), Kind)};
-  CGF.EmitCheck(std::make_pair(Check, Mask),
-                SanitizerHandler::ImplicitConversion, StaticArgs, {Src, Dst});
+      llvm::ConstantInt::get(Builder.getInt8Ty(), CheckKind)};
+  // EmitCheck() will 'and' all the checks together.
+  CGF.EmitCheck(Checks, SanitizerHandler::ImplicitConversion, StaticArgs,
+                {Src, Dst});
 }
 
 /// Emit a conversion from the specified type to the specified destination type,
@@ -1081,8 +1256,13 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
   }
 
   // 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
@@ -1226,6 +1406,10 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
     EmitIntegerTruncationCheck(Src, NoncanonicalSrcType, Res,
                                NoncanonicalDstType, Loc);
 
+  if (Opts.EmitImplicitIntegerSignChangeChecks)
+    EmitIntegerSignChangeCheck(Src, NoncanonicalSrcType, Res,
+                               NoncanonicalDstType, Loc);
+
   return Res;
 }
 
@@ -2010,9 +2194,14 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
 
   case CK_IntegralCast: {
     ScalarConversionOpts Opts;
-    if (CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)) {
-      if (auto *ICE = dyn_cast<ImplicitCastExpr>(CE))
-        Opts.EmitImplicitIntegerTruncationChecks = !ICE->isPartOfExplicitCast();
+    if (auto *ICE = dyn_cast<ImplicitCastExpr>(CE)) {
+      if (CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitConversion) &&
+          !ICE->isPartOfExplicitCast()) {
+        Opts.EmitImplicitIntegerTruncationChecks =
+            CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation);
+        Opts.EmitImplicitIntegerSignChangeChecks =
+            CGF.SanOpts.has(SanitizerKind::ImplicitIntegerSignChange);
+      }
     }
     return EmitScalarConversion(Visit(E), E->getType(), DestTy,
                                 CE->getExprLoc(), Opts);
diff --git a/test/CodeGen/catch-implicit-conversions-basics.c b/test/CodeGen/catch-implicit-conversions-basics.c
new file mode 100644 (file)
index 0000000..2af16e8
--- /dev/null
@@ -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;
+}
diff --git a/test/CodeGen/catch-implicit-integer-arithmetic-value-change-basics.c b/test/CodeGen/catch-implicit-integer-arithmetic-value-change-basics.c
new file mode 100644 (file)
index 0000000..0ba16eb
--- /dev/null
@@ -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 1a34246d78bdf8067ed1bcb8eddf0d2005cf91bb..2af16e80c8ffc910671e303766e44207d184080b 100644 (file)
@@ -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
 // 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 @@ signed int convert_signed_char_to_signed_int(signed char 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;
 }
@@ -84,25 +91,28 @@ unsigned char convert_signed_int_to_unsigned_char(signed int x) {
 
 // CHECK-LABEL: @convert_signed_char_to_unsigned_char
 unsigned char convert_signed_char_to_unsigned_char(signed char x) {
+  // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1200_SIGN_CHANGE]] to i8*)
 #line 1200
   return x;
 }
 
 // CHECK-LABEL: @convert_unsigned_char_to_signed_char
 signed char convert_unsigned_char_to_signed_char(unsigned char x) {
+  // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1300_SIGN_CHANGE]] to i8*)
 #line 1300
   return x;
 }
 
 // CHECK-LABEL: @convert_signed_char_to_unsigned_int
 unsigned int convert_signed_char_to_unsigned_int(signed char x) {
+  // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1400_SIGN_CHANGE]] to i8*)
 #line 1400
   return x;
 }
 
 // CHECK-LABEL: @convert_unsigned_int_to_signed_char
 signed char convert_unsigned_int_to_signed_char(unsigned int x) {
-  // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1500_SIGNED_TRUNCATION]] to i8*)
+  // CHECK: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_1500_SIGNED_TRUNCATION_OR_SIGN_CHANGE]] to i8*)
 #line 1500
   return x;
 }
diff --git a/test/CodeGen/catch-implicit-integer-sign-changes-basics.c b/test/CodeGen/catch-implicit-integer-sign-changes-basics.c
new file mode 100644 (file)
index 0000000..71533a9
--- /dev/null
@@ -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;
+}
diff --git a/test/CodeGen/catch-implicit-integer-sign-changes-true-negatives.c b/test/CodeGen/catch-implicit-integer-sign-changes-true-negatives.c
new file mode 100644 (file)
index 0000000..3798ccc
--- /dev/null
@@ -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;
+}
diff --git a/test/CodeGen/catch-implicit-integer-sign-changes.c b/test/CodeGen/catch-implicit-integer-sign-changes.c
new file mode 100644 (file)
index 0000000..e171904
--- /dev/null
@@ -0,0 +1,273 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE
+// RUN: %clang_cc1 -fsanitize=implicit-integer-sign-change -fsanitize-recover=implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER
+// RUN: %clang_cc1 -fsanitize=implicit-integer-sign-change -fsanitize-trap=implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE
+
+// CHECK-SANITIZE-ANYRECOVER: @[[UNSIGNED_INT:.*]] = {{.*}} c"'unsigned int'\00" }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[SIGNED_INT:.*]] = {{.*}} c"'int'\00" }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_100:.*]] = {{.*}}, i32 100, i32 10 }, {{.*}}* @[[UNSIGNED_INT]], {{.*}}* @[[SIGNED_INT]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_200:.*]] = {{.*}}, i32 200, i32 10 }, {{.*}}* @[[SIGNED_INT]], {{.*}}* @[[UNSIGNED_INT]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[UNSIGNED_CHAR:.*]] = {{.*}} c"'unsigned char'\00" }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_300:.*]] = {{.*}}, i32 300, i32 10 }, {{.*}}* @[[SIGNED_INT]], {{.*}}* @[[UNSIGNED_CHAR]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[SIGNED_CHAR:.*]] = {{.*}} c"'signed char'\00" }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_400:.*]] = {{.*}}, i32 400, i32 10 }, {{.*}}* @[[SIGNED_CHAR]], {{.*}}* @[[UNSIGNED_CHAR]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_500:.*]] = {{.*}}, i32 500, i32 10 }, {{.*}}* @[[UNSIGNED_CHAR]], {{.*}}* @[[SIGNED_CHAR]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_600:.*]] = {{.*}}, i32 600, i32 10 }, {{.*}}* @[[SIGNED_CHAR]], {{.*}}* @[[UNSIGNED_INT]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_700:.*]] = {{.*}}, i32 700, i32 10 }, {{.*}}* @[[UNSIGNED_INT]], {{.*}}* @[[SIGNED_CHAR]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_800:.*]] = {{.*}}, i32 800, i32 10 }, {{.*}}* @[[SIGNED_INT]], {{.*}}* @[[SIGNED_CHAR]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER: @[[UINT32:.*]] = {{.*}} c"'uint32_t' (aka 'unsigned int')\00" }
+// CHECK-SANITIZE-ANYRECOVER: @[[INT32:.*]] = {{.*}} c"'int32_t' (aka 'int')\00" }
+// CHECK-SANITIZE-ANYRECOVER: @[[LINE_900:.*]] = {{.*}}, i32 900, i32 10 }, {{.*}}* @[[UINT32]], {{.*}}* @[[INT32]], i8 3 }
+
+// ========================================================================== //
+// The expected true-positives.
+// These are implicit, potentially sign-altering, conversions.
+// ========================================================================== //
+
+// These 3 result (after optimizations) in simple 'icmp sge i32 %src, 0'.
+
+// CHECK-LABEL: @unsigned_int_to_signed_int
+// CHECK-SAME: (i32 %[[SRC:.*]])
+signed int unsigned_int_to_signed_int(unsigned int src) {
+  // CHECK: %[[SRC_ADDR:.*]] = alloca i32
+  // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+  // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i32 %[[DST]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 false, %[[DST_NEGATIVITYCHECK]], !nosanitize
+  // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTDST:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_100]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_100]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+  // CHECK-SANITIZE: [[CONT]]:
+  // CHECK-NEXT: ret i32 %[[DST]]
+  // CHECK-NEXT: }
+#line 100
+  return src;
+}
+
+// CHECK-LABEL: @signed_int_to_unsigned_int
+// CHECK-SAME: (i32 %[[SRC:.*]])
+unsigned int signed_int_to_unsigned_int(signed int src) {
+  // CHECK: %[[SRC_ADDR:.*]] = alloca i32
+  // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+  // CHECK-SANITIZE-NEXT: %[[SRC_NEGATIVITYCHECK:.*]] = icmp slt i32 %[[DST]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 %[[SRC_NEGATIVITYCHECK]], false, !nosanitize
+  // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTDST:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_200]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_200]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+  // CHECK-SANITIZE: [[CONT]]:
+  // CHECK-NEXT: ret i32 %[[DST]]
+  // CHECK-NEXT: }
+#line 200
+  return src;
+}
+
+// CHECK-LABEL: @signed_int_to_unsigned_char
+// CHECK-SAME: (i32 %[[SRC:.*]])
+unsigned char signed_int_to_unsigned_char(signed int src) {
+  // CHECK: %[[SRC_ADDR:.*]] = alloca i32
+  // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[CONV:.*]] = trunc i32 %[[DST]] to i8
+  // CHECK-SANITIZE-NEXT: %[[SRC_NEGATIVITYCHECK:.*]] = icmp slt i32 %[[DST]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 %[[SRC_NEGATIVITYCHECK]], false, !nosanitize
+  // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTCONV:.*]] = zext i8 %[[CONV]] to i64, !nosanitize
+  // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_300]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_300]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+  // CHECK-SANITIZE: [[CONT]]:
+  // CHECK-NEXT: ret i8 %[[CONV]]
+  // CHECK-NEXT: }
+#line 300
+  return src;
+}
+
+// These 3 result (after optimizations) in simple 'icmp sge i8 %src, 0'
+
+// CHECK-LABEL: @signed_char_to_unsigned_char
+// CHECK-SAME: (i8 signext %[[SRC:.*]])
+unsigned char signed_char_to_unsigned_char(signed char src) {
+  // CHECK: %[[SRC_ADDR:.*]] = alloca i8
+  // CHECK-NEXT: store i8 %[[SRC]], i8* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[DST:.*]] = load i8, i8* %[[SRC_ADDR]]
+  // CHECK-SANITIZE-NEXT: %[[SRC_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[DST]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 %[[SRC_NEGATIVITYCHECK]], false, !nosanitize
+  // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i8 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTDST:.*]] = zext i8 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_400]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_400]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+  // CHECK-SANITIZE: [[CONT]]:
+  // CHECK-NEXT: ret i8 %[[DST]]
+  // CHECK-NEXT: }
+#line 400
+  return src;
+}
+
+// CHECK-LABEL: @unsigned_char_to_signed_char
+// CHECK-SAME: (i8 zeroext %[[SRC:.*]])
+signed char unsigned_char_to_signed_char(unsigned char src) {
+  // CHECK: %[[SRC_ADDR:.*]] = alloca i8
+  // CHECK-NEXT: store i8 %[[SRC]], i8* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[DST:.*]] = load i8, i8* %[[SRC_ADDR]]
+  // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[DST]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 false, %[[DST_NEGATIVITYCHECK]], !nosanitize
+  // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i8 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTDST:.*]] = zext i8 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_500]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_500]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+  // CHECK-SANITIZE: [[CONT]]:
+  // CHECK-NEXT: ret i8 %[[DST]]
+  // CHECK-NEXT: }
+#line 500
+  return src;
+}
+
+// CHECK-LABEL: @signed_char_to_unsigned_int
+// CHECK-SAME: (i8 signext %[[SRC:.*]])
+unsigned int signed_char_to_unsigned_int(signed char src) {
+  // CHECK: %[[SRC_ADDR:.*]] = alloca i8
+  // CHECK-NEXT: store i8 %[[SRC]], i8* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[DST:.*]] = load i8, i8* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[CONV:.*]] = sext i8 %[[DST]] to i32
+  // CHECK-SANITIZE-NEXT: %[[SRC_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[DST]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 %[[SRC_NEGATIVITYCHECK]], false, !nosanitize
+  // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i8 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTCONV:.*]] = zext i32 %[[CONV]] to i64, !nosanitize
+  // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_600]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_600]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+  // CHECK-SANITIZE: [[CONT]]:
+  // CHECK-NEXT: ret i32 %[[CONV]]
+  // CHECK-NEXT: }
+#line 600
+  return src;
+}
+
+// This one result (after optimizations) in 'icmp sge i8 (trunc i32 %src), 0'
+
+// CHECK-LABEL: @unsigned_int_to_signed_char
+// CHECK-SAME: (i32 %[[SRC:.*]])
+signed char unsigned_int_to_signed_char(unsigned int src) {
+  // CHECK: %[[SRC_ADDR:.*]] = alloca i32
+  // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[CONV:.*]] = trunc i32 %[[DST]] to i8
+  // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[CONV]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 false, %[[DST_NEGATIVITYCHECK]], !nosanitize
+  // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTCONV:.*]] = zext i8 %[[CONV]] to i64, !nosanitize
+  // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_700]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_700]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+  // CHECK-SANITIZE: [[CONT]]:
+  // CHECK-NEXT: ret i8 %[[CONV]]
+  // CHECK-NEXT: }
+#line 700
+  return src;
+}
+
+// The worst one: 'xor i1 (icmp sge i8 (trunc i32 %x), 0), (icmp sge i32 %x, 0)'
+
+// CHECK-LABEL: @signed_int_to_signed_char
+// CHECK-SAME: (i32 %[[SRC:.*]])
+signed char signed_int_to_signed_char(signed int x) {
+  // CHECK: %[[SRC_ADDR:.*]] = alloca i32
+  // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[CONV:.*]] = trunc i32 %[[DST]] to i8
+  // CHECK-SANITIZE-NEXT: %[[SRC_NEGATIVITYCHECK:.*]] = icmp slt i32 %[[DST]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[CONV]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 %[[SRC_NEGATIVITYCHECK]], %[[DST_NEGATIVITYCHECK]], !nosanitize
+  // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTCONV:.*]] = zext i8 %[[CONV]] to i64, !nosanitize
+  // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_800]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_800]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+  // CHECK-SANITIZE: [[CONT]]:
+  // CHECK-NEXT: ret i8 %[[CONV]]
+  // CHECK-NEXT: }
+#line 800
+  return x;
+}
+
+// ========================================================================== //
+// Check canonical type stuff
+// ========================================================================== //
+
+typedef unsigned int uint32_t;
+typedef signed int int32_t;
+
+// CHECK-LABEL: @uint32_t_to_int32_t
+// CHECK-SAME: (i32 %[[SRC:.*]])
+int32_t uint32_t_to_int32_t(uint32_t src) {
+  // CHECK: %[[SRC_ADDR:.*]] = alloca i32
+  // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+  // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i32 %[[DST]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 false, %[[DST_NEGATIVITYCHECK]], !nosanitize
+  // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTDST:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_900]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_900]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+  // CHECK-SANITIZE: [[CONT]]:
+  // CHECK-NEXT: ret i32 %[[DST]]
+  // CHECK-NEXT: }
+#line 900
+  return src;
+}
+
+// ========================================================================== //
+// Check that explicit conversion does not interfere with implicit conversion
+// ========================================================================== //
+// These contain one implicit and one explicit sign-changing conversion.
+// We want to make sure that we still diagnose the implicit conversion.
+
+// Implicit sign-change after explicit sign-change.
+// CHECK-LABEL: @explicit_conversion_interference0
+unsigned int explicit_conversion_interference0(unsigned int c) {
+  // CHECK-SANITIZE: call
+  return (signed int)c;
+}
+
+// Implicit sign-change before explicit sign-change.
+// CHECK-LABEL: @explicit_conversion_interference1
+unsigned int explicit_conversion_interference1(unsigned int c) {
+  // CHECK-SANITIZE: call
+  signed int b;
+  return (unsigned int)(b = c);
+}
diff --git a/test/CodeGen/catch-implicit-signed-integer-truncation-or-sign-change.c b/test/CodeGen/catch-implicit-signed-integer-truncation-or-sign-change.c
new file mode 100644 (file)
index 0000000..45171ba
--- /dev/null
@@ -0,0 +1,152 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-NORECOVER,CHECK-SANITIZE-UNREACHABLE
+// RUN: %clang_cc1 -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-ANYRECOVER,CHECK-SANITIZE-RECOVER
+// RUN: %clang_cc1 -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-trap=implicit-signed-integer-truncation,implicit-integer-sign-change -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -implicit-check-not="call void @__ubsan_handle_implicit_conversion" --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP,CHECK-SANITIZE-UNREACHABLE
+
+// CHECK-SANITIZE-ANYRECOVER: @[[UNSIGNED_INT:.*]] = {{.*}} c"'unsigned int'\00" }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[SIGNED_CHAR:.*]] = {{.*}} c"'signed char'\00" }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_100_SIGNED_TRUNCATION_OR_SIGN_CHANGE:.*]] = {{.*}}, i32 100, i32 10 }, {{.*}}* @[[UNSIGNED_INT]], {{.*}}* @[[SIGNED_CHAR]], i8 4 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_200_SIGN_CHANGE:.*]] = {{.*}}, i32 200, i32 10 }, {{.*}}* @[[UNSIGNED_INT]], {{.*}}* @[[SIGNED_CHAR]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_300_SIGN_CHANGE:.*]] = {{.*}}, i32 300, i32 10 }, {{.*}}* @[[UNSIGNED_INT]], {{.*}}* @[[SIGNED_CHAR]], i8 3 }
+// CHECK-SANITIZE-ANYRECOVER-NEXT: @[[LINE_400_SIGNED_TRUNCATION:.*]] = {{.*}}, i32 400, i32 10 }, {{.*}}* @[[UNSIGNED_INT]], {{.*}}* @[[SIGNED_CHAR]], i8 2 }
+
+//============================================================================//
+// Both sanitizers are enabled, and not disabled per-function.
+//============================================================================//
+
+// CHECK-LABEL: @unsigned_int_to_signed_char
+// CHECK-SAME: (i32 %[[SRC:.*]])
+signed char unsigned_int_to_signed_char(unsigned int src) {
+  // CHECK-NEXT: [[ENTRY:.*]]:
+  // CHECK-NEXT: %[[SRC_ADDR:.*]] = alloca i32
+  // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[CONV:.*]] = trunc i32 %[[DST]] to i8
+  // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[CONV]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 false, %[[DST_NEGATIVITYCHECK]], !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[ANYEXT:.*]] = sext i8 %[[CONV]] to i32, !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[TRUNCHECK:.*]] = icmp eq i32 %[[ANYEXT]], %[[DST]], !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[BOTHCHECKS:.*]] = and i1 %[[SIGNCHANGECHECK]], %[[TRUNCHECK]], !nosanitize
+  // CHECK-SANITIZE-NEXT: br i1 %[[BOTHCHECKS]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTCONV:.*]] = zext i8 %[[CONV]] to i64, !nosanitize
+  // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_100_SIGNED_TRUNCATION_OR_SIGN_CHANGE]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_100_SIGNED_TRUNCATION_OR_SIGN_CHANGE]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+  // CHECK-SANITIZE: [[CONT]]:
+  // CHECK-NEXT: ret i8 %[[CONV]]
+  // CHECK-NEXT: }
+#line 100
+  return src;
+}
+
+//============================================================================//
+// Truncation sanitizer is disabled per-function.
+//============================================================================//
+
+// CHECK-LABEL: @unsigned_int_to_signed_char__no_truncation_sanitizer
+// CHECK-SAME: (i32 %[[SRC:.*]])
+__attribute__((no_sanitize("implicit-integer-truncation"))) signed char
+unsigned_int_to_signed_char__no_truncation_sanitizer(unsigned int src) {
+  // CHECK-NEXT: [[ENTRY:.*]]:
+  // CHECK-NEXT: %[[SRC_ADDR:.*]] = alloca i32
+  // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[CONV:.*]] = trunc i32 %[[DST]] to i8
+  // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[CONV]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 false, %[[DST_NEGATIVITYCHECK]], !nosanitize
+  // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTCONV:.*]] = zext i8 %[[CONV]] to i64, !nosanitize
+  // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_200_SIGN_CHANGE]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_200_SIGN_CHANGE]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+  // CHECK-SANITIZE: [[CONT]]:
+  // CHECK-NEXT: ret i8 %[[CONV]]
+  // CHECK-NEXT: }
+#line 200
+  return src;
+}
+
+//============================================================================//
+// Signed truncation sanitizer is disabled per-function.
+//============================================================================//
+
+// CHECK-LABEL: @unsigned_int_to_signed_char__no_signed_truncation_sanitizer
+// CHECK-SAME: (i32 %[[SRC:.*]])
+__attribute__((no_sanitize("implicit-signed-integer-truncation"))) signed char
+unsigned_int_to_signed_char__no_signed_truncation_sanitizer(unsigned int src) {
+  // CHECK-NEXT: [[ENTRY:.*]]:
+  // CHECK-NEXT: %[[SRC_ADDR:.*]] = alloca i32
+  // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[CONV:.*]] = trunc i32 %[[DST]] to i8
+  // CHECK-SANITIZE-NEXT: %[[DST_NEGATIVITYCHECK:.*]] = icmp slt i8 %[[CONV]], 0, !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[SIGNCHANGECHECK:.*]] = icmp eq i1 false, %[[DST_NEGATIVITYCHECK]], !nosanitize
+  // CHECK-SANITIZE-NEXT: br i1 %[[SIGNCHANGECHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTCONV:.*]] = zext i8 %[[CONV]] to i64, !nosanitize
+  // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_300_SIGN_CHANGE]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_300_SIGN_CHANGE]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+  // CHECK-SANITIZE: [[CONT]]:
+  // CHECK-NEXT: ret i8 %[[CONV]]
+  // CHECK-NEXT: }
+#line 300
+  return src;
+}
+
+//============================================================================//
+// Sign change sanitizer is disabled per-function
+//============================================================================//
+
+// CHECK-LABEL: @unsigned_int_to_signed_char__no_sign_change_sanitizer
+// CHECK-SAME: (i32 %[[SRC:.*]])
+__attribute__((no_sanitize("implicit-integer-sign-change"))) signed char
+unsigned_int_to_signed_char__no_sign_change_sanitizer(unsigned int src) {
+  // CHECK-NEXT: [[ENTRY:.*]]:
+  // CHECK-NEXT: %[[SRC_ADDR:.*]] = alloca i32
+  // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[CONV:.*]] = trunc i32 %[[DST]] to i8
+  // CHECK-SANITIZE-NEXT: %[[ANYEXT:.*]] = sext i8 %[[CONV]] to i32, !nosanitize
+  // CHECK-SANITIZE-NEXT: %[[TRUNCHECK:.*]] = icmp eq i32 %[[ANYEXT]], %[[DST]], !nosanitize
+  // CHECK-SANITIZE-NEXT: br i1 %[[TRUNCHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CONVERSION:[^,]+]],{{.*}} !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CONVERSION]]:
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTSRC:.*]] = zext i32 %[[DST]] to i64, !nosanitize
+  // CHECK-SANITIZE-ANYRECOVER-NEXT: %[[EXTCONV:.*]] = zext i8 %[[CONV]] to i64, !nosanitize
+  // CHECK-SANITIZE-NORECOVER-NEXT: call void @__ubsan_handle_implicit_conversion_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_400_SIGNED_TRUNCATION]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_400_SIGNED_TRUNCATION]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTCONV]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+  // CHECK-SANITIZE-UNREACHABLE-NEXT: unreachable, !nosanitize
+  // CHECK-SANITIZE: [[CONT]]:
+  // CHECK-NEXT: ret i8 %[[CONV]]
+  // CHECK-NEXT: }
+#line 400
+  return src;
+}
+
+//============================================================================//
+// Both sanitizers are disabled per-function.
+//============================================================================//
+
+// CHECK-LABEL: @unsigned_int_to_signed_char__no_sanitizers
+// CHECK-SAME: (i32 %[[SRC:.*]])
+__attribute__((no_sanitize("implicit-integer-truncation"),
+               no_sanitize("implicit-integer-sign-change"))) signed char
+unsigned_int_to_signed_char__no_sanitizers(unsigned int src) {
+  // CHECK-NEXT: [[ENTRY:.*]]:
+  // CHECK-NEXT: %[[SRC_ADDR:.*]] = alloca i32
+  // CHECK-NEXT: store i32 %[[SRC]], i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[DST:.*]] = load i32, i32* %[[SRC_ADDR]]
+  // CHECK-NEXT: %[[CONV:.*]] = trunc i32 %[[DST]] to i8
+  // CHECK-NEXT: ret i8 %[[CONV]]
+  // CHECK-NEXT: }
+  return src;
+}
diff --git a/test/CodeGenCXX/catch-implicit-integer-sign-changes-true-negatives.cpp b/test/CodeGenCXX/catch-implicit-integer-sign-changes-true-negatives.cpp
new file mode 100644 (file)
index 0000000..9534938
--- /dev/null
@@ -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<signed int>(src);
+}
+
+// CHECK-LABEL: @explicit_cppstyle_signed_int_to_unsigned_int
+unsigned int explicit_cppstyle_signed_int_to_unsigned_int(signed int src) {
+  // CHECK-SANITIZE-NOT: call
+  // CHECK: }
+  return static_cast<unsigned int>(src);
+}
+
+// Explicit C++-style casts NOP casts.
+// ========================================================================== //
+
+// CHECK-LABEL: @explicit_cppstyle_unsigned_int_to_unsigned_int
+unsigned int explicit_cppstyle_unsigned_int_to_unsigned_int(unsigned int src) {
+  // CHECK-SANITIZE-NOT: call
+  // CHECK: }
+  return static_cast<unsigned int>(src);
+}
+
+// CHECK-LABEL: @explicit_cppstyle_signed_int_to_signed_int
+signed int explicit_cppstyle_signed_int_to_signed_int(signed int src) {
+  // CHECK-SANITIZE-NOT: call
+  // CHECK: }
+  return static_cast<signed int>(src);
+}
+
+} // extern "C"
index 51d910c304174c4604259f7055509e65b6edfea6..ac4a7d27b38c91e843e5bbd6644e438e5e41539c 100644 (file)
 // CHECK-COVERAGE-WIN64: "--dependent-lib={{[^"]*}}ubsan_standalone-x86_64.lib"
 
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=integer %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INTEGER -implicit-check-not="-fsanitize-address-use-after-scope"
-// CHECK-INTEGER: "-fsanitize={{((signed-integer-overflow|unsigned-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){7}"}}
+// CHECK-INTEGER: "-fsanitize={{((signed-integer-overflow|unsigned-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){8}"}}
 
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-conversion %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-conversion,CHECK-implicit-conversion-RECOVER
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-conversion -fsanitize-recover=implicit-conversion %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-conversion,CHECK-implicit-conversion-RECOVER
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-conversion -fno-sanitize-recover=implicit-conversion %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-conversion,CHECK-implicit-conversion-NORECOVER
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-conversion -fsanitize-trap=implicit-conversion %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-conversion,CHECK-implicit-conversion-TRAP
-// CHECK-implicit-conversion: "-fsanitize={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
-// CHECK-implicit-conversion-RECOVER: "-fsanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
-// CHECK-implicit-conversion-RECOVER-NOT: "-fno-sanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
-// CHECK-implicit-conversion-RECOVER-NOT: "-fsanitize-trap={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
-// CHECK-implicit-conversion-NORECOVER-NOT: "-fno-sanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}} // ???
-// CHECK-implicit-conversion-NORECOVER-NOT: "-fsanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
-// CHECK-implicit-conversion-NORECOVER-NOT: "-fsanitize-trap={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
-// CHECK-implicit-conversion-TRAP: "-fsanitize-trap={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
-// CHECK-implicit-conversion-TRAP-NOT: "-fsanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
-// CHECK-implicit-conversion-TRAP-NOT: "-fno-sanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation),?){2}"}}
+// CHECK-implicit-conversion: "-fsanitize={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+// CHECK-implicit-conversion-RECOVER: "-fsanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+// CHECK-implicit-conversion-RECOVER-NOT: "-fno-sanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+// CHECK-implicit-conversion-RECOVER-NOT: "-fsanitize-trap={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+// CHECK-implicit-conversion-NORECOVER-NOT: "-fno-sanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}} // ???
+// CHECK-implicit-conversion-NORECOVER-NOT: "-fsanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+// CHECK-implicit-conversion-NORECOVER-NOT: "-fsanitize-trap={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+// CHECK-implicit-conversion-TRAP: "-fsanitize-trap={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+// CHECK-implicit-conversion-TRAP-NOT: "-fsanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+// CHECK-implicit-conversion-TRAP-NOT: "-fno-sanitize-recover={{((implicit-unsigned-integer-truncation|implicit-signed-integer-truncation|implicit-integer-sign-change),?){3}"}}
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-integer-arithmetic-value-change %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-integer-arithmetic-value-change,CHECK-implicit-integer-arithmetic-value-change-RECOVER
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-integer-arithmetic-value-change -fsanitize-recover=implicit-integer-arithmetic-value-change %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-integer-arithmetic-value-change,CHECK-implicit-integer-arithmetic-value-change-RECOVER
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-integer-arithmetic-value-change -fno-sanitize-recover=implicit-integer-arithmetic-value-change %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-integer-arithmetic-value-change,CHECK-implicit-integer-arithmetic-value-change-NORECOVER
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-integer-arithmetic-value-change -fsanitize-trap=implicit-integer-arithmetic-value-change %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-integer-arithmetic-value-change,CHECK-implicit-integer-arithmetic-value-change-TRAP
+// CHECK-implicit-integer-arithmetic-value-change: "-fsanitize={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
+// CHECK-implicit-integer-arithmetic-value-change-RECOVER: "-fsanitize-recover={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
+// CHECK-implicit-integer-arithmetic-value-change-RECOVER-NOT: "-fno-sanitize-recover={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
+// CHECK-implicit-integer-arithmetic-value-change-RECOVER-NOT: "-fsanitize-trap={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
+// CHECK-implicit-integer-arithmetic-value-change-NORECOVER-NOT: "-fno-sanitize-recover={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}} // ???
+// CHECK-implicit-integer-arithmetic-value-change-NORECOVER-NOT: "-fsanitize-recover={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
+// CHECK-implicit-integer-arithmetic-value-change-NORECOVER-NOT: "-fsanitize-trap={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
+// CHECK-implicit-integer-arithmetic-value-change-TRAP: "-fsanitize-trap={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
+// CHECK-implicit-integer-arithmetic-value-change-TRAP-NOT: "-fsanitize-recover={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
+// CHECK-implicit-integer-arithmetic-value-change-TRAP-NOT: "-fno-sanitize-recover={{((implicit-signed-integer-truncation|implicit-integer-sign-change),?){2}"}}
 
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-integer-truncation %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-integer-truncation,CHECK-implicit-integer-truncation-RECOVER
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-integer-truncation -fsanitize-recover=implicit-integer-truncation %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-implicit-integer-truncation,CHECK-implicit-integer-truncation-RECOVER