lebedev.ri updated this revision to Diff 156237.
lebedev.ri added a comment.

Breakthrough: no more false-positives due to the `MaterializeTemporaryExpr` 
skipping over NoOp casts. (https://reviews.llvm.org/D49508)
Slight docs update.

Ping, please review!
We are so close :)


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/CGExpr.cpp
  lib/CodeGen/CGExprConstant.cpp
  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,254 @@
+// 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 cast
+// ========================================================================== //
+// These contain one implicit truncating cast, and one explicit truncating cast.
+// We want to make sure that we still diagnose the implicit cast.
+
+// 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 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"
+
+// ---------------------------------------------------------------------------//
+// A problematic false-positive involving simple C++ code.
+// 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"
Index: test/CodeGen/catch-implicit-integer-truncations.c
===================================================================
--- /dev/null
+++ test/CodeGen/catch-implicit-integer-truncations.c
@@ -0,0 +1,400 @@
+// 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;
+}
+
+// ========================================================================== //
+// Check that explicit cast does not interfere with implicit cast
+// ========================================================================== //
+// These contain one implicit truncating cast, and one explicit truncating cast.
+// We want to make sure that we still diagnose the implicit cast.
+
+// 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 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
@@ -35,6 +35,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/IR/ValueHandle.h"
 #include "llvm/Support/Debug.h"
@@ -116,6 +117,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)                            \
@@ -376,6 +378,51 @@
     ~SanitizerScope();
   };
 
+  // The stack of currently-visiting Cast expressions.
+  // This stack is used/maintained exclusively by the implicit cast sanitizer.
+  llvm::SmallVector<const CastExpr *, 8> CastExprStack;
+
+  class CastExprStackGuard {
+    CodeGenFunction &CGF;
+
+    llvm::SmallVector<const CastExpr *, 2> GuardedCasts;
+
+    // We only maintain this stack if the implicit cast sanitizer is enabled,
+    // since that is the only user of this stack.
+    bool Enabled() { return CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitCast); }
+
+    void Setup() {
+      CGF.CastExprStack.append(GuardedCasts.begin(), GuardedCasts.end());
+    }
+
+  public:
+    CastExprStackGuard(CodeGenFunction &CGF, const CastExpr *GC) : CGF(CGF) {
+      if (!Enabled())
+        return;
+      GuardedCasts.emplace_back(GC);
+      Setup();
+    }
+    CastExprStackGuard(CodeGenFunction &CGF,
+                       SmallVector<const CastExpr *, 2> &&GC)
+        : CGF(CGF), GuardedCasts(std::move(GC)) {
+      if (!Enabled())
+        return;
+      Setup();
+    }
+
+    ~CastExprStackGuard() {
+      if (!Enabled())
+        return;
+
+      for (const CastExpr *Cast : llvm::reverse(GuardedCasts)) {
+        assert(!CGF.CastExprStack.empty());
+        assert(CGF.CastExprStack.back() == Cast);
+        (void)Cast; // Avoid warnings in assert-less builds.
+        CGF.CastExprStack.pop_back();
+      }
+    }
+  };
+
   /// In C++, whether we are code generating a thunk.  This controls whether we
   /// should emit cleanups.
   bool CurFuncIsThunk = false;
Index: lib/CodeGen/CGExprScalar.cpp
===================================================================
--- lib/CodeGen/CGExprScalar.cpp
+++ lib/CodeGen/CGExprScalar.cpp
@@ -299,13 +299,33 @@
                                 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,
+  };
+
+  bool IsTopCastPartOfExplictCast();
+
+  /// 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 +943,78 @@
                 SanitizerHandler::FloatCastOverflow, StaticArgs, OrigSrc);
 }
 
+bool ScalarExprEmitter::IsTopCastPartOfExplictCast() {
+  assert(!CGF.CastExprStack.empty());
+  // Walk the current stack of CastExprs in reverse order.
+  // That is, the current CastExpr, which is the top()/back() one,
+  // will be processed first.
+  const Stmt *PreviousCast = nullptr;
+  for (const CastExpr *Cast : llvm::reverse(CGF.CastExprStack)) {
+    // Was there a previous cast?
+    // If it is *not* a direct child of the current one, we are ok.
+    assert(!PreviousCast || !isa<ExplicitCastExpr>(PreviousCast));
+    if (PreviousCast && Cast->getSubExpr() != PreviousCast)
+      return false;
+    // If this is an explicit cast, then no need to look further.
+    if (isa<ExplicitCastExpr>(Cast))
+      return true;
+    PreviousCast = Cast;
+  }
+
+  // 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);
-}
-
 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 +1163,7 @@
 
   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 +1198,10 @@
     }
   }
 
+  if (Opts.EmitImplicitIntegerTruncationChecks)
+    EmitIntegerTruncationCheck(Src, NoncanonicalSrcType, Res,
+                               NoncanonicalDstType, Loc);
+
   return Res;
 }
 
@@ -1576,6 +1660,8 @@
 // have to handle a more broad range of conversions than explicit casts, as they
 // handle things like function to ptr-to-function decay etc.
 Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
+  CodeGenFunction::CastExprStackGuard CEStackGuard(CGF, CE);
+
   Expr *E = CE->getSubExpr();
   QualType DestTy = CE->getType();
   CastKind Kind = CE->getCastKind();
@@ -1812,16 +1898,26 @@
     return Builder.CreateVectorSplat(NumElements, Elt, "splat");
   }
 
-  case CK_IntegralCast:
+  case CK_IntegralCast: {
+    ScalarConversionOpts Opts;
+    if (isa<ImplicitCastExpr>(CE) &&
+        CGF.SanOpts.has(SanitizerKind::ImplicitIntegerTruncation) &&
+        !IsTopCastPartOfExplictCast())
+      Opts.EmitImplicitIntegerTruncationChecks = true;
+    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:
Index: lib/CodeGen/CGExprConstant.cpp
===================================================================
--- lib/CodeGen/CGExprConstant.cpp
+++ lib/CodeGen/CGExprConstant.cpp
@@ -1837,6 +1837,10 @@
   SmallVector<const CastExpr *, 2> SkippedCasts;
   const Expr *Inner = E->GetTemporaryExpr()->skipRValueSubobjectAdjustments(
       CommaLHSs, Adjustments, SkippedCasts);
+  if (!Emitter.CGF)
+    return CGM.GetAddrOfGlobalTemporary(E, Inner);
+  CodeGenFunction::CastExprStackGuard CEStackGuard(*Emitter.CGF,
+                                                   std::move(SkippedCasts));
   return CGM.GetAddrOfGlobalTemporary(E, Inner);
 }
 
Index: lib/CodeGen/CGExpr.cpp
===================================================================
--- lib/CodeGen/CGExpr.cpp
+++ lib/CodeGen/CGExpr.cpp
@@ -470,6 +470,9 @@
   SmallVector<const CastExpr *, 2> SkippedCasts;
   E = E->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments, SkippedCasts);
 
+  CodeGenFunction::CastExprStackGuard CEStackGuard(*this,
+                                                   std::move(SkippedCasts));
+
   for (const auto &Ignored : CommaLHSs)
     EmitIgnoredExpr(Ignored);
 
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,11 @@
   -  ``-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. This kind of issues
+     may often be caused by an implicit integer promotions.
   -  ``-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.
@@ -123,13 +129,17 @@
      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``).
+     overflow in signed division (``INT_MIN / -1``). Please be aware of
+     integer promotions, as those may result in an unexpected computation
+     results, even though no overflow happens (signed or unsigned).
   -  ``-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.
+     it is often unintentional, so UBSan offers to catch it. Please be aware of
+     integer promotions, as those may result in an unexpected computation
+     results, even though no overflow happens (signed or unsigned).
   -  ``-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
@@ -145,6 +155,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
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -266,10 +268,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. This group is **not** enabled by ``-fsanitize=undefined``.
 
 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