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