summaryrefslogtreecommitdiffstats
path: root/clang
diff options
context:
space:
mode:
Diffstat (limited to 'clang')
-rw-r--r--clang/docs/ReleaseNotes.rst32
-rw-r--r--clang/docs/UndefinedBehaviorSanitizer.rst34
-rw-r--r--clang/include/clang/Basic/Sanitizers.def9
-rw-r--r--clang/include/clang/Basic/Sanitizers.h3
-rw-r--r--clang/lib/CodeGen/CGExprScalar.cpp105
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.h1
-rw-r--r--clang/lib/Driver/SanitizerArgs.cpp10
-rw-r--r--clang/lib/Driver/ToolChain.cpp4
-rw-r--r--clang/test/CodeGen/catch-implicit-integer-truncations.c395
-rw-r--r--clang/test/CodeGenCXX/catch-implicit-integer-truncations.cpp256
-rw-r--r--clang/test/Driver/fsanitize.c17
11 files changed, 829 insertions, 37 deletions
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 342fed3393b..1e45a13ef91 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -46,7 +46,9 @@ sections with improvements to Clang's support for those languages.
Major New Features
------------------
-- ...
+- A new Implicit Conversion Sanitizer (``-fsanitize=implicit-conversion``) group
+ was added. Please refer to the :ref:`release-notes-ubsan` section of the
+ release notes for the details.
Improvements to Clang's diagnostics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -280,10 +282,36 @@ Static Analyzer
...
+.. _release-notes-ubsan:
+
Undefined Behavior Sanitizer (UBSan)
------------------------------------
-* ...
+* A new Implicit Conversion Sanitizer (``-fsanitize=implicit-conversion``) group
+ was added.
+
+ Currently, only one type of issues is caught - implicit integer truncation
+ (``-fsanitize=implicit-integer-truncation``), also known as integer demotion.
+ While there is a ``-Wconversion`` diagnostic group that catches this kind of
+ issues, it is both noisy, and does not catch **all** the cases.
+
+ .. code-block:: c++
+
+ unsigned char store = 0;
+
+ bool consume(unsigned int val);
+
+ void test(unsigned long val) {
+ if (consume(val)) // the value may have been silently truncated.
+ store = store + 768; // before addition, 'store' was promoted to int.
+ (void)consume((unsigned int)val); // OK, the truncation is explicit.
+ }
+
+ Just like other ``-fsanitize=integer`` checks, these issues are **not**
+ undefined behaviour. But they are not *always* intentional, and are somewhat
+ hard to track down. This group is **not** enabled by ``-fsanitize=undefined``,
+ but the ``-fsanitize=implicit-integer-truncation`` check
+ is enabled by ``-fsanitize=integer``.
Core Analysis Improvements
==========================
diff --git a/clang/docs/UndefinedBehaviorSanitizer.rst b/clang/docs/UndefinedBehaviorSanitizer.rst
index 71a8ebd4bc6..6cf53f56833 100644
--- a/clang/docs/UndefinedBehaviorSanitizer.rst
+++ b/clang/docs/UndefinedBehaviorSanitizer.rst
@@ -89,6 +89,12 @@ Available checks are:
- ``-fsanitize=function``: Indirect call of a function through a
function pointer of the wrong type (Darwin/Linux, C++ and x86/x86_64
only).
+ - ``-fsanitize=implicit-integer-truncation``: Implicit conversion from
+ integer of larger bit width to smaller bit width, if that results in data
+ loss. That is, if the demoted value, after casting back to the original
+ width, is not equal to the original value before the downcast.
+ 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.
@@ -121,15 +127,21 @@ Available checks are:
unsigned overflow in C++. You can use ``-fsanitize=shift-base`` or
``-fsanitize=shift-exponent`` to check only left-hand side or
right-hand side of shift operation, respectively.
- - ``-fsanitize=signed-integer-overflow``: Signed integer overflow,
- including all the checks added by ``-ftrapv``, and checking for
- overflow in signed division (``INT_MIN / -1``).
+ - ``-fsanitize=signed-integer-overflow``: Signed integer overflow, where the
+ result of a signed integer computation cannot be represented in its type.
+ This includes all the checks covered by ``-ftrapv``, as well as checks for
+ signed division overflow (``INT_MIN/-1``), but not checks for
+ lossy implicit conversions performed after the computation
+ (see ``-fsanitize=implicit-conversion``). Both of these two issues are
+ handled by ``-fsanitize=implicit-conversion`` group of checks.
- ``-fsanitize=unreachable``: If control flow reaches an unreachable
program point.
- - ``-fsanitize=unsigned-integer-overflow``: Unsigned integer
- overflows. Note that unlike signed integer overflow, unsigned integer
- is not undefined behavior. However, while it has well-defined semantics,
- it is often unintentional, so UBSan offers to catch it.
+ - ``-fsanitize=unsigned-integer-overflow``: Unsigned integer overflow, where
+ the result of an unsigned integer computation cannot be represented in its
+ type. Unlike signed integer overflow, this is not undefined behavior, but
+ it is often unintentional. This sanitizer does not check for lossy implicit
+ conversions performed after such a computation
+ (see ``-fsanitize=implicit-conversion``).
- ``-fsanitize=vla-bound``: A variable-length array whose bound
does not evaluate to a positive value.
- ``-fsanitize=vptr``: Use of an object whose vptr indicates that it is of
@@ -140,11 +152,17 @@ Available checks are:
You can also use the following check groups:
- ``-fsanitize=undefined``: All of the checks listed above other than
- ``unsigned-integer-overflow`` and the ``nullability-*`` checks.
+ ``unsigned-integer-overflow``, ``implicit-conversion`` and the
+ ``nullability-*`` group of checks.
- ``-fsanitize=undefined-trap``: Deprecated alias of
``-fsanitize=undefined``.
- ``-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-conversion``: Checks for suspicious behaviours of
+ implicit conversions.
+ Currently, only ``-fsanitize=implicit-integer-truncation`` is implemented.
- ``-fsanitize=nullability``: Enables ``nullability-arg``,
``nullability-assign``, and ``nullability-return``. While violating
nullability does not have undefined behavior, it is often unintentional,
diff --git a/clang/include/clang/Basic/Sanitizers.def b/clang/include/clang/Basic/Sanitizers.def
index 5a36822a630..ae5c75fc5bb 100644
--- a/clang/include/clang/Basic/Sanitizers.def
+++ b/clang/include/clang/Basic/Sanitizers.def
@@ -131,9 +131,14 @@ SANITIZER_GROUP("undefined", Undefined,
// -fsanitize=undefined-trap is an alias for -fsanitize=undefined.
SANITIZER_GROUP("undefined-trap", UndefinedTrap, Undefined)
+// ImplicitConversionSanitizer
+SANITIZER("implicit-integer-truncation", ImplicitIntegerTruncation)
+SANITIZER_GROUP("implicit-conversion", ImplicitConversion,
+ ImplicitIntegerTruncation)
+
SANITIZER_GROUP("integer", Integer,
- SignedIntegerOverflow | UnsignedIntegerOverflow | Shift |
- IntegerDivideByZero)
+ ImplicitIntegerTruncation | IntegerDivideByZero | Shift |
+ SignedIntegerOverflow | UnsignedIntegerOverflow)
SANITIZER("local-bounds", LocalBounds)
SANITIZER_GROUP("bounds", Bounds, ArrayBounds | LocalBounds)
diff --git a/clang/include/clang/Basic/Sanitizers.h b/clang/include/clang/Basic/Sanitizers.h
index 469d9e2e959..2ecbde82159 100644
--- a/clang/include/clang/Basic/Sanitizers.h
+++ b/clang/include/clang/Basic/Sanitizers.h
@@ -84,7 +84,8 @@ SanitizerMask expandSanitizerGroups(SanitizerMask Kinds);
/// Return the sanitizers which do not affect preprocessing.
inline SanitizerMask getPPTransparentSanitizers() {
return SanitizerKind::CFI | SanitizerKind::Integer |
- SanitizerKind::Nullability | SanitizerKind::Undefined;
+ SanitizerKind::ImplicitConversion | SanitizerKind::Nullability |
+ SanitizerKind::Undefined;
}
} // namespace clang
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 783f74c5026..b9fcb507a6e 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -299,13 +299,31 @@ public:
Value *Src, QualType SrcType, QualType DstType,
llvm::Type *DstTy, SourceLocation Loc);
+ /// Known implicit conversion check kinds.
+ /// Keep in sync with the enum of the same name in ubsan_handlers.h
+ enum ImplicitConversionCheckKind : unsigned char {
+ ICCK_IntegerTruncation = 0,
+ };
+
+ /// Emit a check that an [implicit] truncation of an integer does not
+ /// discard any bits. It is not UB, so we use the value after truncation.
+ void EmitIntegerTruncationCheck(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.
- Value *EmitScalarConversion(Value *Src, QualType SrcTy, QualType DstTy,
- SourceLocation Loc);
+ struct ScalarConversionOpts {
+ bool TreatBooleanAsSigned;
+ bool EmitImplicitIntegerTruncationChecks;
- Value *EmitScalarConversion(Value *Src, QualType SrcTy, QualType DstTy,
- SourceLocation Loc, bool TreatBooleanAsSigned);
+ ScalarConversionOpts()
+ : TreatBooleanAsSigned(false),
+ EmitImplicitIntegerTruncationChecks(false) {}
+ };
+ Value *
+ EmitScalarConversion(Value *Src, QualType SrcTy, QualType DstTy,
+ SourceLocation Loc,
+ ScalarConversionOpts Opts = ScalarConversionOpts());
/// Emit a conversion from the specified complex type to the specified
/// destination type, where the destination type is an LLVM scalar type.
@@ -923,18 +941,59 @@ void ScalarExprEmitter::EmitFloatConversionCheck(
SanitizerHandler::FloatCastOverflow, StaticArgs, OrigSrc);
}
-/// Emit a conversion from the specified type to the specified destination type,
-/// both of which are LLVM scalar types.
-Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
- QualType DstType,
- SourceLocation Loc) {
- return EmitScalarConversion(Src, SrcType, DstType, Loc, false);
+void ScalarExprEmitter::EmitIntegerTruncationCheck(Value *Src, QualType SrcType,
+ Value *Dst, QualType DstType,
+ SourceLocation Loc) {
+ if (!CGF.SanOpts.has(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();
+ // This must be truncation. Else we do not care.
+ if (SrcBits <= DstBits)
+ return;
+
+ assert(!DstType->isBooleanType() && "we should not get here with booleans.");
+
+ CodeGenFunction::SanitizerScope SanScope(&CGF);
+
+ llvm::Value *Check = nullptr;
+
+ // 1. Extend the truncated value back to the same width as the Src.
+ bool InputSigned = DstType->isSignedIntegerOrEnumerationType();
+ Check = Builder.CreateIntCast(Dst, SrcTy, InputSigned, "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.
+
+ llvm::Constant *StaticArgs[] = {
+ CGF.EmitCheckSourceLocation(Loc), CGF.EmitCheckTypeDescriptor(SrcType),
+ CGF.EmitCheckTypeDescriptor(DstType),
+ llvm::ConstantInt::get(Builder.getInt8Ty(), ICCK_IntegerTruncation)};
+ CGF.EmitCheck(std::make_pair(Check, SanitizerKind::ImplicitIntegerTruncation),
+ SanitizerHandler::ImplicitConversion, StaticArgs, {Src, Dst});
}
+/// Emit a conversion from the specified type to the specified destination type,
+/// both of which are LLVM scalar types.
Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
QualType DstType,
SourceLocation Loc,
- bool TreatBooleanAsSigned) {
+ ScalarConversionOpts Opts) {
+ QualType NoncanonicalSrcType = SrcType;
+ QualType NoncanonicalDstType = DstType;
+
SrcType = CGF.getContext().getCanonicalType(SrcType);
DstType = CGF.getContext().getCanonicalType(DstType);
if (SrcType == DstType) return Src;
@@ -1083,7 +1142,7 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
if (isa<llvm::IntegerType>(SrcTy)) {
bool InputSigned = SrcType->isSignedIntegerOrEnumerationType();
- if (SrcType->isBooleanType() && TreatBooleanAsSigned) {
+ if (SrcType->isBooleanType() && Opts.TreatBooleanAsSigned) {
InputSigned = true;
}
if (isa<llvm::IntegerType>(DstTy))
@@ -1118,6 +1177,10 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
}
}
+ if (Opts.EmitImplicitIntegerTruncationChecks)
+ EmitIntegerTruncationCheck(Src, NoncanonicalSrcType, Res,
+ NoncanonicalDstType, Loc);
+
return Res;
}
@@ -1812,16 +1875,26 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
return Builder.CreateVectorSplat(NumElements, Elt, "splat");
}
- case CK_IntegralCast:
+ case CK_IntegralCast: {
+ ScalarConversionOpts Opts;
+ if (CGF.SanOpts.has(SanitizerKind::ImplicitIntegerTruncation)) {
+ if (auto *ICE = dyn_cast<ImplicitCastExpr>(CE))
+ Opts.EmitImplicitIntegerTruncationChecks = !ICE->isPartOfExplicitCast();
+ }
+ return EmitScalarConversion(Visit(E), E->getType(), DestTy,
+ CE->getExprLoc(), Opts);
+ }
case CK_IntegralToFloating:
case CK_FloatingToIntegral:
case CK_FloatingCast:
return EmitScalarConversion(Visit(E), E->getType(), DestTy,
CE->getExprLoc());
- case CK_BooleanToSignedIntegral:
+ case CK_BooleanToSignedIntegral: {
+ ScalarConversionOpts Opts;
+ Opts.TreatBooleanAsSigned = true;
return EmitScalarConversion(Visit(E), E->getType(), DestTy,
- CE->getExprLoc(),
- /*TreatBooleanAsSigned=*/true);
+ CE->getExprLoc(), Opts);
+ }
case CK_IntegralToBoolean:
return EmitIntToBoolConversion(Visit(E));
case CK_PointerToBoolean:
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 1f8f3367dbe..f9e28423297 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -116,6 +116,7 @@ enum TypeEvaluationKind {
SANITIZER_CHECK(DynamicTypeCacheMiss, dynamic_type_cache_miss, 0) \
SANITIZER_CHECK(FloatCastOverflow, float_cast_overflow, 0) \
SANITIZER_CHECK(FunctionTypeMismatch, function_type_mismatch, 0) \
+ SANITIZER_CHECK(ImplicitConversion, implicit_conversion, 0) \
SANITIZER_CHECK(InvalidBuiltin, invalid_builtin, 0) \
SANITIZER_CHECK(LoadInvalidValue, load_invalid_value, 0) \
SANITIZER_CHECK(MissingReturn, missing_return, 0) \
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index bdc17d11c92..55036daa444 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -27,22 +27,22 @@ using namespace clang::driver;
using namespace llvm::opt;
enum : SanitizerMask {
- NeedsUbsanRt = Undefined | Integer | Nullability | CFI,
+ NeedsUbsanRt = Undefined | Integer | ImplicitConversion | Nullability | CFI,
NeedsUbsanCxxRt = Vptr | CFI,
NotAllowedWithTrap = Vptr,
NotAllowedWithMinimalRuntime = Vptr,
RequiresPIE = DataFlow | HWAddress | Scudo,
NeedsUnwindTables = Address | HWAddress | Thread | Memory | DataFlow,
SupportsCoverage = Address | HWAddress | KernelAddress | KernelHWAddress |
- Memory | Leak | Undefined | Integer | Nullability |
- DataFlow | Fuzzer | FuzzerNoLink,
- RecoverableByDefault = Undefined | Integer | Nullability,
+ Memory | Leak | Undefined | Integer | ImplicitConversion |
+ Nullability | DataFlow | Fuzzer | FuzzerNoLink,
+ RecoverableByDefault = Undefined | Integer | ImplicitConversion | Nullability,
Unrecoverable = Unreachable | Return,
AlwaysRecoverable = KernelAddress | KernelHWAddress,
LegacyFsanitizeRecoverMask = Undefined | Integer,
NeedsLTO = CFI,
TrappingSupported = (Undefined & ~Vptr) | UnsignedIntegerOverflow |
- Nullability | LocalBounds | CFI,
+ ImplicitConversion | Nullability | LocalBounds | CFI,
TrappingDefault = CFI,
CFIClasses =
CFIVCall | CFINVCall | CFIMFCall | CFIDerivedCast | CFIUnrelatedCast,
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index d62ba125334..39a6d9b4cc7 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -803,8 +803,8 @@ SanitizerMask ToolChain::getSupportedSanitizers() const {
using namespace SanitizerKind;
SanitizerMask Res = (Undefined & ~Vptr & ~Function) | (CFI & ~CFIICall) |
- CFICastStrict | UnsignedIntegerOverflow | Nullability |
- LocalBounds;
+ CFICastStrict | UnsignedIntegerOverflow |
+ ImplicitConversion | Nullability | LocalBounds;
if (getTriple().getArch() == llvm::Triple::x86 ||
getTriple().getArch() == llvm::Triple::x86_64 ||
getTriple().getArch() == llvm::Triple::arm ||
diff --git a/clang/test/CodeGen/catch-implicit-integer-truncations.c b/clang/test/CodeGen/catch-implicit-integer-truncations.c
new file mode 100644
index 00000000000..b09f7ddfaed
--- /dev/null
+++ b/clang/test/CodeGen/catch-implicit-integer-truncations.c
@@ -0,0 +1,395 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -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-truncation -fsanitize-recover=implicit-integer-truncation -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-truncation -fsanitize-trap=implicit-integer-truncation -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP
+
+// CHECK-SANITIZE-ANYRECOVER: @[[UNSIGNED_INT:.*]] = {{.*}} c"'unsigned int'\00" }
+// CHECK-SANITIZE-ANYRECOVER: @[[UNSIGNED_CHAR:.*]] = {{.*}} c"'unsigned char'\00" }
+
+// CHECK-SANITIZE-ANYRECOVER: @[[LINE_100:.*]] = {{.*}}, i32 100, i32 10 }, {{.*}}* @[[UNSIGNED_INT]], {{.*}}* @[[UNSIGNED_CHAR]], i8 0 }
+// CHECK-SANITIZE-ANYRECOVER: @[[SIGNED_INT:.*]] = {{.*}} c"'int'\00" }
+// CHECK-SANITIZE-ANYRECOVER: @[[LINE_200:.*]] = {{.*}}, i32 200, i32 10 }, {{.*}}* @[[SIGNED_INT]], {{.*}}* @[[UNSIGNED_CHAR]], i8 0 }
+// CHECK-SANITIZE-ANYRECOVER: @[[SIGNED_CHAR:.*]] = {{.*}} c"'signed char'\00" }
+// CHECK-SANITIZE-ANYRECOVER: @[[LINE_300:.*]] = {{.*}}, i32 300, i32 10 }, {{.*}}* @[[UNSIGNED_INT]], {{.*}}* @[[SIGNED_CHAR]], i8 0 }
+// CHECK-SANITIZE-ANYRECOVER: @[[LINE_400:.*]] = {{.*}}, i32 400, i32 10 }, {{.*}}* @[[SIGNED_INT]], {{.*}}* @[[SIGNED_CHAR]], i8 0 }
+
+// CHECK-SANITIZE-ANYRECOVER: @[[UINT32:.*]] = {{.*}} c"'uint32_t' (aka 'unsigned int')\00" }
+// CHECK-SANITIZE-ANYRECOVER: @[[UINT8:.*]] = {{.*}} c"'uint8_t' (aka 'unsigned char')\00" }
+// CHECK-SANITIZE-ANYRECOVER: @[[LINE_500:.*]] = {{.*}}, i32 500, i32 10 }, {{.*}}* @[[UINT32]], {{.*}}* @[[UINT8]], i8 0 }
+
+// ========================================================================== //
+// The expected true-positives. These are implicit conversions, and they truncate.
+// ========================================================================== //
+
+// CHECK-LABEL: @unsigned_int_to_unsigned_char
+unsigned char unsigned_int_to_unsigned_char(unsigned int src) {
+ // CHECK: %[[DST:.*]] = trunc i32 %[[SRC:.*]] to i8
+ // CHECK-SANITIZE-NEXT: %[[ANYEXT:.*]] = zext i8 %[[DST]] to i32, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[TRUNCHECK:.*]] = icmp eq i32 %[[ANYEXT]], %[[SRC]], !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 %[[SRC]] 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_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-TRAP-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK: ret i8 %[[DST]]
+#line 100
+ return src;
+}
+
+// CHECK-LABEL: @signed_int_to_unsigned_char
+unsigned char signed_int_to_unsigned_char(signed int src) {
+ // CHECK: %[[DST:.*]] = trunc i32 %[[SRC:.*]] to i8
+ // CHECK-SANITIZE-NEXT: %[[ANYEXT:.*]] = zext i8 %[[DST]] to i32, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[TRUNCHECK:.*]] = icmp eq i32 %[[ANYEXT]], %[[SRC]], !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 %[[SRC]] 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_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-TRAP-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK: ret i8 %[[DST]]
+#line 200
+ return src;
+}
+
+// CHECK-LABEL: @unsigned_int_to_signed_char
+signed char unsigned_int_to_signed_char(unsigned int src) {
+ // CHECK: %[[DST:.*]] = trunc i32 %[[SRC:.*]] to i8
+ // CHECK-SANITIZE-NEXT: %[[ANYEXT:.*]] = sext i8 %[[DST]] to i32, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[TRUNCHECK:.*]] = icmp eq i32 %[[ANYEXT]], %[[SRC]], !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 %[[SRC]] 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_300]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_conversion(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_300]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+ // CHECK-SANITIZE-TRAP-NEXT: call void @llvm.trap(){{.*}}, !nosanitize
+ // CHECK-SANITIZE-TRAP-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK: ret i8 %[[DST]]
+#line 300
+ return src;
+}
+
+// CHECK-LABEL: @signed_int_to_signed_char
+signed char signed_int_to_signed_char(signed int src) {
+ // CHECK: %[[DST:.*]] = trunc i32 %[[SRC:.*]] to i8
+ // CHECK-SANITIZE-NEXT: %[[ANYEXT:.*]] = sext i8 %[[DST]] to i32, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[TRUNCHECK:.*]] = icmp eq i32 %[[ANYEXT]], %[[SRC]], !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 %[[SRC]] 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-TRAP-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK: ret i8 %[[DST]]
+#line 400
+ return src;
+}
+
+// ========================================================================== //
+// Check canonical type stuff
+// ========================================================================== //
+
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+
+// CHECK-LABEL: @uint32_to_uint8
+uint8_t uint32_to_uint8(uint32_t src) {
+ // CHECK: %[[DST:.*]] = trunc i32 %[[SRC:.*]] to i8
+ // CHECK-SANITIZE-NEXT: %[[ANYEXT:.*]] = zext i8 %[[DST]] to i32, !nosanitize
+ // CHECK-SANITIZE-NEXT: %[[TRUNCHECK:.*]] = icmp eq i32 %[[ANYEXT]], %[[SRC]], !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 %[[SRC]] 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-TRAP-NEXT: unreachable, !nosanitize
+ // CHECK-SANITIZE: [[CONT]]:
+ // CHECK: ret i8 %[[DST]]
+#line 500
+ return src;
+}
+
+// ========================================================================== //
+// Check that explicit conversion does not interfere with implicit conversion
+// ========================================================================== //
+// These contain one implicit truncating conversion, and one explicit truncating conversion.
+// We want to make sure that we still diagnose the implicit conversion.
+
+// Implicit truncation after explicit truncation.
+// CHECK-LABEL: @explicit_conversion_interference0
+unsigned char explicit_conversion_interference0(unsigned int c) {
+ // CHECK-SANITIZE: %[[ANYEXT:.*]] = zext i8 %[[DST:.*]] to i16, !nosanitize
+ // CHECK-SANITIZE: call
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (unsigned short)c;
+}
+
+// Implicit truncation before explicit truncation.
+// CHECK-LABEL: @explicit_conversion_interference1
+unsigned char explicit_conversion_interference1(unsigned int c) {
+ // CHECK-SANITIZE: %[[ANYEXT:.*]] = zext i16 %[[DST:.*]] to i32, !nosanitize
+ // CHECK-SANITIZE: call
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ unsigned short b;
+ return (unsigned char)(b = c);
+}
+
+// ========================================================================== //
+// The expected true-negatives.
+// ========================================================================== //
+
+// Sanitization is explicitly disabled.
+// ========================================================================== //
+
+// CHECK-LABEL: @blacklist_0
+__attribute__((no_sanitize("undefined"))) unsigned char blacklist_0(unsigned 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("implicit-conversion"))) unsigned char blacklist_1(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return src;
+}
+
+// CHECK-LABEL: @blacklist_2
+__attribute__((no_sanitize("implicit-integer-truncation"))) unsigned char blacklist_2(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return src;
+}
+
+// Explicit truncating conversions.
+// ========================================================================== //
+
+// CHECK-LABEL: @explicit_unsigned_int_to_unsigned_char
+unsigned char explicit_unsigned_int_to_unsigned_char(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (unsigned char)src;
+}
+
+// CHECK-LABEL: @explicit_signed_int_to_unsigned_char
+unsigned char explicit_signed_int_to_unsigned_char(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (unsigned char)src;
+}
+
+// CHECK-LABEL: @explicit_unsigned_int_to_signed_char
+signed char explicit_unsigned_int_to_signed_char(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (signed char)src;
+}
+
+// CHECK-LABEL: @explicit_signed_int_to_signed_char
+signed char explicit_signed_int_to_signed_char(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (signed char)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;
+}
+
+// CHECK-LABEL: @explicit_unsigned_char_to_signed_char
+unsigned char explicit_unsigned_char_to_signed_char(unsigned char src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (unsigned char)src;
+}
+
+// CHECK-LABEL: @explicit_signed_char_to_signed_char
+signed char explicit_signed_char_to_signed_char(signed char src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (signed char)src;
+}
+
+// upcasts.
+// ========================================================================== //
+
+// CHECK-LABEL: @unsigned_char_to_unsigned_int
+unsigned int unsigned_char_to_unsigned_int(unsigned char src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return src;
+}
+
+// CHECK-LABEL: @signed_char_to_unsigned_int
+unsigned int signed_char_to_unsigned_int(signed char src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return src;
+}
+
+// CHECK-LABEL: @unsigned_char_to_signed_int
+signed int unsigned_char_to_signed_int(unsigned char src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return src;
+}
+
+// CHECK-LABEL: @signed_char_to_signed_int
+signed int signed_char_to_signed_int(signed char src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return src;
+}
+
+// Explicit upcasts.
+// ========================================================================== //
+
+// CHECK-LABEL: @explicit_unsigned_char_to_unsigned_int
+unsigned int explicit_unsigned_char_to_unsigned_int(unsigned char src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (unsigned int)src;
+}
+
+// CHECK-LABEL: @explicit_signed_char_to_unsigned_int
+unsigned int explicit_signed_char_to_unsigned_int(signed char src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (unsigned int)src;
+}
+
+// CHECK-LABEL: @explicit_unsigned_char_to_signed_int
+signed int explicit_unsigned_char_to_signed_int(unsigned char src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (signed int)src;
+}
+
+// CHECK-LABEL: @explicit_signed_char_to_signed_int
+signed int explicit_signed_char_to_signed_int(signed char src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (signed int)src;
+}
+
+// conversions to to boolean type are not counted as truncation.
+// ========================================================================== //
+
+// CHECK-LABEL: @unsigned_int_to_bool
+_Bool unsigned_int_to_bool(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return src;
+}
+
+// CHECK-LABEL: @signed_int_to_bool
+_Bool signed_int_to_bool(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return src;
+}
+
+// CHECK-LABEL: @explicit_unsigned_int_to_bool
+_Bool explicit_unsigned_int_to_bool(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (_Bool)src;
+}
+
+// CHECK-LABEL: @explicit_signed_int_to_bool
+_Bool explicit_signed_int_to_bool(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (_Bool)src;
+}
+
+// Explicit truncating conversions from pointer to a much-smaller 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_char
+unsigned char explicit_voidptr_to_unsigned_char(void *src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (unsigned char)src;
+}
+
+// CHECK-LABEL: @explicit_voidptr_to_signed_char
+signed char explicit_voidptr_to_signed_char(void *src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (signed char)src;
+}
+
+// Implicit truncating conversions from floating-point may result in precision loss.
+// ========================================================================== //
+
+// CHECK-LABEL: @float_to_unsigned_int
+unsigned int float_to_unsigned_int(float src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return src;
+}
+
+// CHECK-LABEL: @float_to_signed_int
+signed int float_to_signed_int(float src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return src;
+}
+
+// CHECK-LABEL: @double_to_unsigned_int
+unsigned int double_to_unsigned_int(double src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return src;
+}
+
+// CHECK-LABEL: @double_to_signed_int
+signed int double_to_signed_int(double src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return src;
+}
+
+// Implicit truncating conversions between fp may result in precision loss.
+// ========================================================================== //
+
+// CHECK-LABEL: @double_to_float
+float double_to_float(double src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return src;
+}
diff --git a/clang/test/CodeGenCXX/catch-implicit-integer-truncations.cpp b/clang/test/CodeGenCXX/catch-implicit-integer-truncations.cpp
new file mode 100644
index 00000000000..ba7676a3504
--- /dev/null
+++ b/clang/test/CodeGenCXX/catch-implicit-integer-truncations.cpp
@@ -0,0 +1,256 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -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-truncation -fsanitize-recover=implicit-integer-truncation -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-truncation -fsanitize-trap=implicit-integer-truncation -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-SANITIZE,CHECK-SANITIZE-TRAP
+
+extern "C" { // Disable name mangling.
+
+// ========================================================================== //
+// Check that explicit cast does not interfere with implicit conversion
+// ========================================================================== //
+// These contain one implicit truncating conversion, and one explicit truncating cast.
+// We want to make sure that we still diagnose the implicit conversion.
+
+// Implicit truncation after explicit truncation.
+// CHECK-LABEL: @explicit_cast_interference0
+unsigned char explicit_cast_interference0(unsigned int c) {
+ // CHECK-SANITIZE: %[[ANYEXT:.*]] = zext i8 %[[DST:.*]] to i16, !nosanitize
+ // CHECK-SANITIZE: call
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (unsigned short)c;
+}
+
+// Implicit truncation before explicit truncation.
+// CHECK-LABEL: @explicit_cast_interference1
+unsigned char explicit_cast_interference1(unsigned int c) {
+ // CHECK-SANITIZE: %[[ANYEXT:.*]] = zext i16 %[[DST:.*]] to i32, !nosanitize
+ // CHECK-SANITIZE: call
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ unsigned short b;
+ return (unsigned char)(b = c);
+}
+
+// ========================================================================== //
+// The expected true-negatives.
+// ========================================================================== //
+
+// Explicit truncating casts.
+// ========================================================================== //
+
+// CHECK-LABEL: @explicit_unsigned_int_to_unsigned_char
+unsigned char explicit_unsigned_int_to_unsigned_char(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (unsigned char)src;
+}
+
+// CHECK-LABEL: @explicit_signed_int_to_unsigned_char
+unsigned char explicit_signed_int_to_unsigned_char(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (unsigned char)src;
+}
+
+// CHECK-LABEL: @explicit_unsigned_int_to_signed_char
+signed char explicit_unsigned_int_to_signed_char(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (signed char)src;
+}
+
+// CHECK-LABEL: @explicit_signed_int_to_signed_char
+signed char explicit_signed_int_to_signed_char(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (signed char)src;
+}
+
+// Explicit NOP casts.
+// ========================================================================== //
+
+// 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;
+}
+
+// CHECK-LABEL: @explicit_unsigned_char_to_signed_char
+unsigned char explicit_unsigned_char_to_signed_char(unsigned char src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (unsigned char)src;
+}
+
+// CHECK-LABEL: @explicit_signed_char_to_signed_char
+signed char explicit_signed_char_to_signed_char(signed char src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return (signed char)src;
+}
+
+// Explicit functional truncating casts.
+// ========================================================================== //
+
+using UnsignedChar = unsigned char;
+using SignedChar = signed char;
+using UnsignedInt = unsigned int;
+using SignedInt = signed int;
+
+// CHECK-LABEL: @explicit_functional_unsigned_int_to_unsigned_char
+unsigned char explicit_functional_unsigned_int_to_unsigned_char(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return UnsignedChar(src);
+}
+
+// CHECK-LABEL: @explicit_functional_signed_int_to_unsigned_char
+unsigned char explicit_functional_signed_int_to_unsigned_char(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return UnsignedChar(src);
+}
+
+// CHECK-LABEL: @explicit_functional_unsigned_int_to_signed_char
+signed char explicit_functional_unsigned_int_to_signed_char(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return SignedChar(src);
+}
+
+// CHECK-LABEL: @explicit_functional_signed_int_to_signed_char
+signed char explicit_functional_signed_int_to_signed_char(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return SignedChar(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);
+}
+
+// CHECK-LABEL: @explicit_functional_unsigned_char_to_signed_char
+unsigned char explicit_functional_unsigned_char_to_signed_char(unsigned char src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return UnsignedChar(src);
+}
+
+// CHECK-LABEL: @explicit_functional_signed_char_to_signed_char
+signed char explicit_functional_signed_char_to_signed_char(signed char src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return SignedChar(src);
+}
+
+// Explicit C++-style casts truncating casts.
+// ========================================================================== //
+
+// CHECK-LABEL: @explicit_cppstyleunsigned_int_to_unsigned_char
+unsigned char explicit_cppstyleunsigned_int_to_unsigned_char(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return static_cast<unsigned char>(src);
+}
+
+// CHECK-LABEL: @explicit_cppstylesigned_int_to_unsigned_char
+unsigned char explicit_cppstylesigned_int_to_unsigned_char(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return static_cast<unsigned char>(src);
+}
+
+// CHECK-LABEL: @explicit_cppstyleunsigned_int_to_signed_char
+signed char explicit_cppstyleunsigned_int_to_signed_char(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return static_cast<signed char>(src);
+}
+
+// CHECK-LABEL: @explicit_cppstylesigned_int_to_signed_char
+signed char explicit_cppstylesigned_int_to_signed_char(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return static_cast<signed char>(src);
+}
+
+// Explicit C++-style casts NOP casts.
+// ========================================================================== //
+
+// CHECK-LABEL: @explicit_cppstyleunsigned_int_to_unsigned_int
+unsigned int explicit_cppstyleunsigned_int_to_unsigned_int(unsigned int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return static_cast<unsigned int>(src);
+}
+
+// CHECK-LABEL: @explicit_cppstylesigned_int_to_signed_int
+signed int explicit_cppstylesigned_int_to_signed_int(signed int src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return static_cast<signed int>(src);
+}
+
+// CHECK-LABEL: @explicit_cppstyleunsigned_char_to_signed_char
+unsigned char explicit_cppstyleunsigned_char_to_signed_char(unsigned char src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return static_cast<unsigned char>(src);
+}
+
+// CHECK-LABEL: @explicit_cppstylesigned_char_to_signed_char
+signed char explicit_cppstylesigned_char_to_signed_char(signed char src) {
+ // CHECK-SANITIZE-NOT: call
+ // CHECK: }
+ return static_cast<signed char>(src);
+}
+
+} // extern "C"
+
+// ---------------------------------------------------------------------------//
+// A problematic true-negative involving simple C++ code.
+// The problem is tha the NoOp ExplicitCast is directly within MaterializeTemporaryExpr(),
+// so a special care is neeeded.
+// See https://reviews.llvm.org/D48958#1161345
+template <typename a>
+a b(a c, const a &d) {
+ if (d)
+ ;
+ return c;
+}
+
+extern "C" { // Disable name mangling.
+
+// CHECK-LABEL: @false_positive_with_MaterializeTemporaryExpr
+int false_positive_with_MaterializeTemporaryExpr() {
+ // CHECK-SANITIZE-NOT: call{{.*}}ubsan
+ // CHECK: }
+ int e = b<unsigned>(4, static_cast<unsigned>(4294967296));
+ return e;
+}
+
+// ---------------------------------------------------------------------------//
+
+} // extern "C"
diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c
index e6d94649f06..458bbf96e21 100644
--- a/clang/test/Driver/fsanitize.c
+++ b/clang/test/Driver/fsanitize.c
@@ -29,7 +29,22 @@
// 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),?){5}"}}
+// CHECK-INTEGER: "-fsanitize={{((signed-integer-overflow|unsigned-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|implicit-integer-truncation),?){6}"}}
+
+// 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-integer-truncation),?){1}"}}
+// CHECK-implicit-conversion-RECOVER: "-fsanitize-recover={{((implicit-integer-truncation),?){1}"}}
+// CHECK-implicit-conversion-RECOVER-NOT: "-fno-sanitize-recover={{((implicit-integer-truncation),?){1}"}}
+// CHECK-implicit-conversion-RECOVER-NOT: "-fsanitize-trap={{((implicit-integer-truncation),?){1}"}}
+// CHECK-implicit-conversion-NORECOVER-NOT: "-fno-sanitize-recover={{((implicit-integer-truncation),?){1}"}} // ???
+// CHECK-implicit-conversion-NORECOVER-NOT: "-fsanitize-recover={{((implicit-integer-truncation),?){1}"}}
+// CHECK-implicit-conversion-NORECOVER-NOT: "-fsanitize-trap={{((implicit-integer-truncation),?){1}"}}
+// CHECK-implicit-conversion-TRAP: "-fsanitize-trap={{((implicit-integer-truncation),?){1}"}}
+// CHECK-implicit-conversion-TRAP-NOT: "-fsanitize-recover={{((implicit-integer-truncation),?){1}"}}
+// CHECK-implicit-conversion-TRAP-NOT: "-fno-sanitize-recover={{((implicit-integer-truncation),?){1}"}}
// RUN: %clang -fsanitize=bounds -### -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK-BOUNDS
// CHECK-BOUNDS: "-fsanitize={{((array-bounds|local-bounds),?){2}"}}
OpenPOWER on IntegriCloud