lebedev.ri updated this revision to Diff 154591.
lebedev.ri retitled this revision from "[private][clang] Implicit Cast 
Sanitizer - integer truncation  - clang part" to "[clang][ubsan] Implicit Cast 
Sanitizer - integer truncation  - clang part".
lebedev.ri edited the summary of this revision.
lebedev.ri added reviewers: rjmccall, rsmith, samsonov, pcc, vsk, eugenis, 
efriedma, kcc.
lebedev.ri added a project: Sanitizers.
lebedev.ri added subscribers: cfe-commits, mclow.lists, milianw, dvyukov, 
ygribov, danielaustin, filcab, dtzWill, RKSimon, aaron.ballman, Sanitizers.
lebedev.ri added a comment.

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:

  unsigned char store = 0;
  
  bool consume(unsigned int val);
  
  void test(unsigned long val) {
    if (consume(val)) {
      // the 'val' is `unsigned long`, but `consume()` takes `unsigned int`.
      // If their bit widths are different on this platform, the implicit
      // truncation happens. And if that `unsigned long` had a value bigger
      // than UINT_MAX, then you may or may not have a bug.
  
      // Similarly, integer addition happens on `int`s, so `store` will
      // be promoted to an `int`, the sum calculated (0+768=768),
      // and the result demoted to `unsigned char`, and stored to `store`.
      // In this case, the `store` will still be 0. Again, not always intended.
      store = store + 768; // before addition, 'store' was promoted to int.
    }
  
    // But yes, sometimes this is intentional.
    // You can either make the cast explicit
    (void)consume((unsigned int)val);
    // or mask the value so no bits will be *implicitly* lost.
    (void)consume((~((unsigned int)0)) & val);
  }

Yes, there is a `-Wconversion`` diagnostic group, but first, it is kinda
noisy, since it warns on everything (unlike sanitizers, warning on an
actual issues), and second, there are cases where it does **not** warn.
So a Sanitizer is needed. I don't have any motivational numbers, but i know
i had this kind of problem 10-20 times, and it was never easy to track down.

The logic to detect whether an truncation has happened is pretty simple
if you think about it - https://godbolt.org/g/NEzXbb - basically, just
extend (using the new, not original!, signedness) the 'truncated' value
back to it's original width, and equality-compare it with the original value.

The most non-trivial thing here is the logic to detect whether this
`ImplicitCastExpr` AST node is **actually** an implicit cast, //or// part of
an explicit cast. Because the explicit casts are modeled as an outer
`ExplicitCastExpr` with some `ImplicitCastExpr`'s as **direct** children.
https://godbolt.org/g/eE1GkJ

It would //seem//, we can just check that the current `ImplicitCastExpr`
we are processing either has no `CastExpr` parents, or all of them are
`ImplicitCastExpr`.

As you may have noted, this isn't just named 
`-fsanitize=implicit-integer-truncation`.
There are potentially some more implicit casts to be warned about.
Namely, implicit casts that result in sign change; implicit cast
between different floating point types, or between fp and an integer,
when again, that conversion is lossy.

I suspect, there may be some false-negatives, cases yet to be handled.

This is a clang part.
The compiler-rt part is https://reviews.llvm.org/D48959.

Fixes PR21530 <https://bugs.llvm.org/show_bug.cgi?id=21530>, PR37552 
<https://bugs.llvm.org/show_bug.cgi?id=37552>, PR35409 
<https://bugs.llvm.org/show_bug.cgi?id=35409>.
Partially fixes PR9821 <https://bugs.llvm.org/show_bug.cgi?id=9821>.
Fixes https://github.com/google/sanitizers/issues/940.


Repository:
  rC Clang

https://reviews.llvm.org/D48958

Files:
  docs/ReleaseNotes.rst
  docs/UndefinedBehaviorSanitizer.rst
  include/clang/Basic/Sanitizers.def
  include/clang/Basic/Sanitizers.h
  lib/CodeGen/CGExprScalar.cpp
  lib/CodeGen/CodeGenFunction.h
  lib/Driver/SanitizerArgs.cpp
  lib/Driver/ToolChain.cpp
  test/CodeGen/catch-implicit-integer-truncations.c
  test/CodeGenCXX/catch-implicit-integer-truncations.cpp
  test/Driver/fsanitize.c

Index: test/Driver/fsanitize.c
===================================================================
--- test/Driver/fsanitize.c
+++ test/Driver/fsanitize.c
@@ -31,6 +31,21 @@
 // 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}"}}
 
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-cast %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-IMPLICIT-CAST,CHECK-IMPLICIT-CAST-RECOVER
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-cast -fsanitize-recover=implicit-cast %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-IMPLICIT-CAST,CHECK-IMPLICIT-CAST-RECOVER
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-cast -fno-sanitize-recover=implicit-cast %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-IMPLICIT-CAST,CHECK-IMPLICIT-CAST-NORECOVER
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=implicit-cast -fsanitize-trap=implicit-cast %s -### 2>&1 | FileCheck %s --check-prefixes=CHECK-IMPLICIT-CAST,CHECK-IMPLICIT-CAST-TRAP
+// CHECK-IMPLICIT-CAST: "-fsanitize={{((implicit-integer-truncation),?){1}"}}
+// CHECK-IMPLICIT-CAST-RECOVER: "-fsanitize-recover={{((implicit-integer-truncation),?){1}"}}
+// CHECK-IMPLICIT-CAST-RECOVER-NOT: "-fno-sanitize-recover={{((implicit-integer-truncation),?){1}"}}
+// CHECK-IMPLICIT-CAST-RECOVER-NOT: "-fsanitize-trap={{((implicit-integer-truncation),?){1}"}}
+// CHECK-IMPLICIT-CAST-NORECOVER-NOT: "-fno-sanitize-recover={{((implicit-integer-truncation),?){1}"}} // ???
+// CHECK-IMPLICIT-CAST-NORECOVER-NOT: "-fsanitize-recover={{((implicit-integer-truncation),?){1}"}}
+// CHECK-IMPLICIT-CAST-NORECOVER-NOT: "-fsanitize-trap={{((implicit-integer-truncation),?){1}"}}
+// CHECK-IMPLICIT-CAST-TRAP: "-fsanitize-trap={{((implicit-integer-truncation),?){1}"}}
+// CHECK-IMPLICIT-CAST-TRAP-NOT: "-fsanitize-recover={{((implicit-integer-truncation),?){1}"}}
+// CHECK-IMPLICIT-CAST-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}"}}
 
Index: test/CodeGenCXX/catch-implicit-integer-truncations.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/catch-implicit-integer-truncations.cpp
@@ -0,0 +1,205 @@
+// 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.
+
+// For now, all the true-positives are tested in the C test in ../CodeGen/
+
+// ========================================================================== //
+// The expected false-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"
Index: test/CodeGen/catch-implicit-integer-truncations.c
===================================================================
--- /dev/null
+++ test/CodeGen/catch-implicit-integer-truncations.c
@@ -0,0 +1,373 @@
+// 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 casts, 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-ANYRECOVER-NEXT: br i1 %[[TRUNCHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CAST:.*]], !prof ![[WEIGHT_MD:.*]], !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: br i1 %[[TRUNCHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CAST:.*]], !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CAST]]:
+  // 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_cast_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_100]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_cast(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-ANYRECOVER-NEXT: br i1 %[[TRUNCHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CAST:.*]], !prof ![[WEIGHT_MD:.*]], !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: br i1 %[[TRUNCHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CAST:.*]], !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CAST]]:
+  // 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_cast_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_200]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_cast(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-ANYRECOVER-NEXT: br i1 %[[TRUNCHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CAST:.*]], !prof ![[WEIGHT_MD:.*]], !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: br i1 %[[TRUNCHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CAST:.*]], !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CAST]]:
+  // 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_cast_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_300]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_cast(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-ANYRECOVER-NEXT: br i1 %[[TRUNCHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CAST:.*]], !prof ![[WEIGHT_MD:.*]], !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: br i1 %[[TRUNCHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CAST:.*]], !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CAST]]:
+  // 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_cast_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_400]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_cast(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-ANYRECOVER-NEXT: br i1 %[[TRUNCHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CAST:.*]], !prof ![[WEIGHT_MD:.*]], !nosanitize
+  // CHECK-SANITIZE-TRAP-NEXT: br i1 %[[TRUNCHECK]], label %[[CONT:.*]], label %[[HANDLER_IMPLICIT_CAST:.*]], !nosanitize
+  // CHECK-SANITIZE: [[HANDLER_IMPLICIT_CAST]]:
+  // 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_cast_abort(i8* bitcast ({ {{{.*}}}, {{{.*}}}*, {{{.*}}}*, i8 }* @[[LINE_500]] to i8*), i64 %[[EXTSRC]], i64 %[[EXTDST]]){{.*}}, !nosanitize
+  // CHECK-SANITIZE-RECOVER-NEXT: call void @__ubsan_handle_implicit_cast(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;
+}
+
+// ========================================================================== //
+// The expected false-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-cast"))) 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 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;
+}
+
+// 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;
+}
+
+// Casts 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 casts from pointer to a much-smaller integer.
+// Can not have an implicit cast from pointer to an integer.
+// Can not have an implicit cast 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 casts 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 casts 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;
+}
Index: lib/Driver/ToolChain.cpp
===================================================================
--- lib/Driver/ToolChain.cpp
+++ lib/Driver/ToolChain.cpp
@@ -803,8 +803,8 @@
   using namespace SanitizerKind;
 
   SanitizerMask Res = (Undefined & ~Vptr & ~Function) | (CFI & ~CFIICall) |
-                      CFICastStrict | UnsignedIntegerOverflow | Nullability |
-                      LocalBounds;
+                      CFICastStrict | UnsignedIntegerOverflow | ImplicitCast |
+                      Nullability | LocalBounds;
   if (getTriple().getArch() == llvm::Triple::x86 ||
       getTriple().getArch() == llvm::Triple::x86_64 ||
       getTriple().getArch() == llvm::Triple::arm ||
Index: lib/Driver/SanitizerArgs.cpp
===================================================================
--- lib/Driver/SanitizerArgs.cpp
+++ lib/Driver/SanitizerArgs.cpp
@@ -27,22 +27,22 @@
 using namespace llvm::opt;
 
 enum : SanitizerMask {
-  NeedsUbsanRt = Undefined | Integer | Nullability | CFI,
+  NeedsUbsanRt = Undefined | Integer | ImplicitCast | 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 | ImplicitCast |
+                     Nullability | DataFlow | Fuzzer | FuzzerNoLink,
+  RecoverableByDefault = Undefined | Integer | ImplicitCast | Nullability,
   Unrecoverable = Unreachable | Return,
   AlwaysRecoverable = KernelAddress | KernelHWAddress,
   LegacyFsanitizeRecoverMask = Undefined | Integer,
   NeedsLTO = CFI,
   TrappingSupported = (Undefined & ~Vptr) | UnsignedIntegerOverflow |
-                      Nullability | LocalBounds | CFI,
+                      ImplicitCast | Nullability | LocalBounds | CFI,
   TrappingDefault = CFI,
   CFIClasses =
       CFIVCall | CFINVCall | CFIMFCall | CFIDerivedCast | CFIUnrelatedCast,
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -116,6 +116,7 @@
   SANITIZER_CHECK(DynamicTypeCacheMiss, dynamic_type_cache_miss, 0)            \
   SANITIZER_CHECK(FloatCastOverflow, float_cast_overflow, 0)                   \
   SANITIZER_CHECK(FunctionTypeMismatch, function_type_mismatch, 0)             \
+  SANITIZER_CHECK(ImplicitCast, implicit_cast, 0)                              \
   SANITIZER_CHECK(InvalidBuiltin, invalid_builtin, 0)                          \
   SANITIZER_CHECK(LoadInvalidValue, load_invalid_value, 0)                     \
   SANITIZER_CHECK(MissingReturn, missing_return, 0)                            \
Index: lib/CodeGen/CGExprScalar.cpp
===================================================================
--- lib/CodeGen/CGExprScalar.cpp
+++ lib/CodeGen/CGExprScalar.cpp
@@ -299,14 +299,32 @@
                                 Value *Src, QualType SrcType, QualType DstType,
                                 llvm::Type *DstTy, SourceLocation Loc);
 
+  /// Known implicit cast check kinds.
+  /// Keep in sync with the enum of the same name in ubsan_handlers.h
+  enum ImplicitCastCheckKind : 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);
 
-  Value *EmitScalarConversion(Value *Src, QualType SrcTy, QualType DstTy,
+  Value *EmitScalarConversion(Value *Src, QualType SrcType, QualType DstType,
                               SourceLocation Loc, bool TreatBooleanAsSigned);
 
+  Value *EmitScalarConversion(CastExpr *CE, Value *Src, QualType SrcTy,
+                              QualType DstTy, SourceLocation Loc);
+
+  Value *EmitScalarConversion(CastExpr *CE, Value *Src, QualType SrcTy,
+                              QualType DstTy, SourceLocation Loc,
+                              bool TreatBooleanAsSigned);
+
   /// Emit a conversion from the specified complex type to the specified
   /// destination type, where the destination type is an LLVM scalar type.
   Value *EmitComplexToScalarConversion(CodeGenFunction::ComplexPairTy Src,
@@ -923,18 +941,110 @@
                 SanitizerHandler::FloatCastOverflow, StaticArgs, OrigSrc);
 }
 
+// Even if we got an ImplicitCastExpr node, this does not nessesairly mean
+// that it is actually an implicit cast in the source code.
+// In C++, it stil can be part of an explicit cast.
+// So we need to analyze the parents of this cast.
+// There are basically two situations: (from child to parent)
+//   implicit cast -> something that is not a cast  <- that is an implicit cast.
+//   implicit cast -> explicit cast                 <- that is an explicit cast.
+static bool CastIsPartOfExplictCast(ASTContext &Ctx, const Expr *E) {
+  // If it's a nulllptr, or not a cast, then it's not a part of Explict Cast.
+  if (!E || !isa<CastExpr>(E))
+    return false;
+  // If this is an explicit cast, then no need to look further.
+  if (isa<ExplicitCastExpr>(E))
+    return true;
+
+  // Else, now we need to look at the parents of this expr.
+  auto Parents = Ctx.getParents(*E);
+  // If there is no parents, then this is clearly not a part of Explicit Cast.
+  if (Parents.empty())
+    return false;
+
+  // Look at the parents, and recurse upwards.
+  for (auto &Parent : Parents) {
+    // If *any* parent is [a part of] explicit cast, then we propagate that.
+    if (CastIsPartOfExplictCast(Ctx, Parent.get<Expr>()))
+      return true;
+  }
+
+  // Did not find any ExplictCast as an immediate parent of this cast.
+  return 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 casts here, and ignore casts to/from pointer.
+  if (!(isa<llvm::IntegerType>(SrcTy) && isa<llvm::IntegerType>(DstTy)))
+    return;
+  // We do not care about booleans.
+  if (SrcType->isBooleanType() || DstType->isBooleanType())
+    return;
+
+  unsigned SrcBits = SrcTy->getScalarSizeInBits();
+  unsigned DstBits = DstTy->getScalarSizeInBits();
+  // This must be truncation. Else we do not care.
+  if (SrcBits <= DstBits)
+    return;
+
+  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::ImplicitCast, 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) {
-  return EmitScalarConversion(Src, SrcType, DstType, Loc, false);
+  return EmitScalarConversion(nullptr, Src, SrcType, DstType, Loc);
 }
 
 Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
                                                QualType DstType,
                                                SourceLocation Loc,
                                                bool TreatBooleanAsSigned) {
+  return EmitScalarConversion(nullptr, Src, SrcType, DstType, Loc,
+                              TreatBooleanAsSigned);
+}
+
+Value *ScalarExprEmitter::EmitScalarConversion(CastExpr *CE, Value *Src,
+                                               QualType SrcType,
+                                               QualType DstType,
+                                               SourceLocation Loc) {
+  return EmitScalarConversion(CE, Src, SrcType, DstType, Loc, false);
+}
+
+Value *ScalarExprEmitter::EmitScalarConversion(CastExpr *CE, Value *Src,
+                                               QualType SrcType,
+                                               QualType DstType,
+                                               SourceLocation Loc,
+                                               bool TreatBooleanAsSigned) {
+  QualType NoncanonicalSrcType = SrcType;
+  QualType NoncanonicalDstType = DstType;
+
   SrcType = CGF.getContext().getCanonicalType(SrcType);
   DstType = CGF.getContext().getCanonicalType(DstType);
   if (SrcType == DstType) return Src;
@@ -1118,6 +1228,11 @@
     }
   }
 
+  if (CE && isa<ImplicitCastExpr>(CE) &&
+      !CastIsPartOfExplictCast(CGF.getContext(), cast<Expr>(CE)))
+    EmitIntegerTruncationCheck(Src, NoncanonicalSrcType, Res,
+                               NoncanonicalDstType, Loc);
+
   return Res;
 }
 
@@ -1816,7 +1931,7 @@
   case CK_IntegralToFloating:
   case CK_FloatingToIntegral:
   case CK_FloatingCast:
-    return EmitScalarConversion(Visit(E), E->getType(), DestTy,
+    return EmitScalarConversion(CE, Visit(E), E->getType(), DestTy,
                                 CE->getExprLoc());
   case CK_BooleanToSignedIntegral:
     return EmitScalarConversion(Visit(E), E->getType(), DestTy,
Index: include/clang/Basic/Sanitizers.h
===================================================================
--- include/clang/Basic/Sanitizers.h
+++ include/clang/Basic/Sanitizers.h
@@ -84,7 +84,8 @@
 /// Return the sanitizers which do not affect preprocessing.
 inline SanitizerMask getPPTransparentSanitizers() {
   return SanitizerKind::CFI | SanitizerKind::Integer |
-         SanitizerKind::Nullability | SanitizerKind::Undefined;
+         SanitizerKind::ImplicitCast | SanitizerKind::Nullability |
+         SanitizerKind::Undefined;
 }
 
 } // namespace clang
Index: include/clang/Basic/Sanitizers.def
===================================================================
--- include/clang/Basic/Sanitizers.def
+++ include/clang/Basic/Sanitizers.def
@@ -135,6 +135,11 @@
                 SignedIntegerOverflow | UnsignedIntegerOverflow | Shift |
                 IntegerDivideByZero)
 
+// ImplicitCastSanitizer
+SANITIZER("implicit-integer-truncation", ImplicitIntegerTruncation)
+SANITIZER_GROUP("implicit-cast", ImplicitCast,
+                ImplicitIntegerTruncation)
+
 SANITIZER("local-bounds", LocalBounds)
 SANITIZER_GROUP("bounds", Bounds, ArrayBounds | LocalBounds)
 
Index: docs/UndefinedBehaviorSanitizer.rst
===================================================================
--- docs/UndefinedBehaviorSanitizer.rst
+++ docs/UndefinedBehaviorSanitizer.rst
@@ -14,6 +14,7 @@
 
 * Using misaligned or null pointer
 * Signed integer overflow
+* Problematic Implicit Casts (not UB, but not always intentional)
 * Conversion to, from, or between floating-point types which would
   overflow the destination
 
@@ -89,6 +90,10 @@
   -  ``-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 cast from integer
+     of bigger 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.
   -  ``-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.
@@ -145,6 +150,9 @@
      ``-fsanitize=undefined``.
   -  ``-fsanitize=integer``: Checks for undefined or suspicious integer
      behavior (e.g. unsigned integer overflow).
+  -  ``-fsanitize=implicit-cast``: Checks for suspicious behaviours of implicit
+     casts. 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,
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -46,7 +46,9 @@
 Major New Features
 ------------------
 
--  ...
+- A new Implicit Cast Sanitizer (``-fsanitize=implicit-cast``) group was added.
+  Please refer to the :ref:`release-notes-ubsan` section of the release notes
+  for the details.
 
 Improvements to Clang's diagnostics
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -246,10 +248,33 @@
 
 ...
 
+.. _release-notes-ubsan:
+
 Undefined Behavior Sanitizer (UBSan)
 ------------------------------------
 
-* ...
+* A new Implicit Cast Sanitizer (``-fsanitize=implicit-cast``) 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 got silently truncated.
+          store = store + 768; // before addition, 'store' was promoted to int.
+        (void)consume((unsigned int)val); // OK, the truncation is implicit.
+      }
+
+  Just like ``-fsanitize=integer``, these issues are **not** undefined
+  behaviour. But they are not *always* intentional, and are somewhat hard to
+  track down.
 
 Core Analysis Improvements
 ==========================
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to