diff options
author | Roman Lebedev <lebedev.ri@gmail.com> | 2018-07-30 18:58:30 +0000 |
---|---|---|
committer | Roman Lebedev <lebedev.ri@gmail.com> | 2018-07-30 18:58:30 +0000 |
commit | 5f775cc5e961dbe81b655e79247234cfb8295a3d (patch) | |
tree | 469a3cc78861cfdfd0272f5f25169fcb1ce30c62 | |
parent | 1c06b114805c7b7a5468a501ab874bf3fe5202f1 (diff) | |
download | compiler-rt-5f775cc5e961dbe81b655e79247234cfb8295a3d.tar.gz |
[compiler-rt][ubsan] Implicit Conversion Sanitizer - integer truncation - compiler-rt part
Summary:
This is a compiler-rt part.
The clang part is D48958.
See [[ https://bugs.llvm.org/show_bug.cgi?id=21530 | PR21530 ]], https://github.com/google/sanitizers/issues/940.
Reviewers: #sanitizers, samsonov, vsk, rsmith, pcc, eugenis, kcc, filcab
Reviewed By: #sanitizers, vsk, filcab
Subscribers: llvm-commits, eugenis, filcab, kubamracek, dberris, #sanitizers, regehr
Tags: #sanitizers
Differential Revision: https://reviews.llvm.org/D48959
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@338287 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/ubsan/ubsan_checks.inc | 2 | ||||
-rw-r--r-- | lib/ubsan/ubsan_diag.h | 2 | ||||
-rw-r--r-- | lib/ubsan/ubsan_handlers.cc | 43 | ||||
-rw-r--r-- | lib/ubsan/ubsan_handlers.h | 17 | ||||
-rw-r--r-- | lib/ubsan/ubsan_interface.inc | 2 | ||||
-rw-r--r-- | lib/ubsan_minimal/ubsan_minimal_handlers.cc | 1 | ||||
-rw-r--r-- | test/fuzzer/ImplicitIntegerTruncationTest.cpp | 27 | ||||
-rw-r--r-- | test/fuzzer/fuzzer-implicit-integer-truncation.test | 5 | ||||
-rw-r--r-- | test/ubsan/TestCases/ImplicitCast/integer-truncation-blacklist.c | 15 | ||||
-rw-r--r-- | test/ubsan/TestCases/ImplicitCast/integer-truncation-summary.cpp | 13 | ||||
-rw-r--r-- | test/ubsan/TestCases/ImplicitCast/integer-truncation.c | 63 | ||||
-rw-r--r-- | test/ubsan_minimal/TestCases/implicit-integer-truncation.c | 24 |
12 files changed, 213 insertions, 1 deletions
diff --git a/lib/ubsan/ubsan_checks.inc b/lib/ubsan/ubsan_checks.inc index 73c693632..5a7bdec2d 100644 --- a/lib/ubsan/ubsan_checks.inc +++ b/lib/ubsan/ubsan_checks.inc @@ -30,6 +30,8 @@ UBSAN_CHECK(IntegerDivideByZero, "integer-divide-by-zero", "integer-divide-by-zero") UBSAN_CHECK(FloatDivideByZero, "float-divide-by-zero", "float-divide-by-zero") UBSAN_CHECK(InvalidBuiltin, "invalid-builtin-use", "invalid-builtin-use") +UBSAN_CHECK(ImplicitIntegerTruncation, "implicit-integer-truncation", + "implicit-integer-truncation") UBSAN_CHECK(InvalidShiftBase, "invalid-shift-base", "shift-base") UBSAN_CHECK(InvalidShiftExponent, "invalid-shift-exponent", "shift-exponent") UBSAN_CHECK(OutOfBoundsIndex, "out-of-bounds-index", "bounds") diff --git a/lib/ubsan/ubsan_diag.h b/lib/ubsan/ubsan_diag.h index d9ea410c3..bde749684 100644 --- a/lib/ubsan/ubsan_diag.h +++ b/lib/ubsan/ubsan_diag.h @@ -178,7 +178,7 @@ public: }; private: - static const unsigned MaxArgs = 5; + static const unsigned MaxArgs = 8; static const unsigned MaxRanges = 1; /// The arguments which have been added to this diagnostic so far. diff --git a/lib/ubsan/ubsan_handlers.cc b/lib/ubsan/ubsan_handlers.cc index 49c6bcacb..e72a32cf3 100644 --- a/lib/ubsan/ubsan_handlers.cc +++ b/lib/ubsan/ubsan_handlers.cc @@ -451,6 +451,49 @@ void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data, Die(); } +static void handleImplicitConversion(ImplicitConversionData *Data, + ReportOptions Opts, ValueHandle Src, + ValueHandle Dst) { + SourceLocation Loc = Data->Loc.acquire(); + ErrorType ET = ErrorType::GenericUB; + + switch (Data->Kind) { + case ICCK_IntegerTruncation: + ET = ErrorType::ImplicitIntegerTruncation; + break; + } + + if (ignoreReport(Loc, Opts, ET)) + return; + + const TypeDescriptor &SrcTy = Data->FromType; + const TypeDescriptor &DstTy = Data->ToType; + + ScopedReport R(Opts, Loc, ET); + + // FIXME: is it possible to dump the values as hex with fixed width? + + Diag(Loc, DL_Error, ET, + "implicit conversion from type %0 of value %1 (%2-bit, %3signed) to " + "type %4 changed the value to %5 (%6-bit, %7signed)") + << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth() + << (SrcTy.isSignedIntegerTy() ? "" : "un") << DstTy << Value(DstTy, Dst) + << DstTy.getIntegerBitWidth() << (DstTy.isSignedIntegerTy() ? "" : "un"); +} + +void __ubsan::__ubsan_handle_implicit_conversion(ImplicitConversionData *Data, + ValueHandle Src, + ValueHandle Dst) { + GET_REPORT_OPTIONS(false); + handleImplicitConversion(Data, Opts, Src, Dst); +} +void __ubsan::__ubsan_handle_implicit_conversion_abort( + ImplicitConversionData *Data, ValueHandle Src, ValueHandle Dst) { + GET_REPORT_OPTIONS(true); + handleImplicitConversion(Data, Opts, Src, Dst); + Die(); +} + static void handleInvalidBuiltin(InvalidBuiltinData *Data, ReportOptions Opts) { SourceLocation Loc = Data->Loc.acquire(); ErrorType ET = ErrorType::InvalidBuiltin; diff --git a/lib/ubsan/ubsan_handlers.h b/lib/ubsan/ubsan_handlers.h index ed3c8f0b1..07644b7ea 100644 --- a/lib/ubsan/ubsan_handlers.h +++ b/lib/ubsan/ubsan_handlers.h @@ -122,6 +122,23 @@ struct InvalidValueData { /// \brief Handle a load of an invalid value for the type. RECOVERABLE(load_invalid_value, InvalidValueData *Data, ValueHandle Val) +/// Known implicit conversion check kinds. +/// Keep in sync with the enum of the same name in CGExprScalar.cpp +enum ImplicitConversionCheckKind : unsigned char { + ICCK_IntegerTruncation = 0, +}; + +struct ImplicitConversionData { + SourceLocation Loc; + const TypeDescriptor &FromType; + const TypeDescriptor &ToType; + /* ImplicitConversionCheckKind */ unsigned char Kind; +}; + +/// \brief Implict conversion that changed the value. +RECOVERABLE(implicit_conversion, ImplicitConversionData *Data, ValueHandle Src, + ValueHandle Dst) + /// Known builtin check kinds. /// Keep in sync with the enum of the same name in CodeGenFunction.h enum BuiltinCheckKind : unsigned char { diff --git a/lib/ubsan/ubsan_interface.inc b/lib/ubsan/ubsan_interface.inc index 782c621a2..0be6010ad 100644 --- a/lib/ubsan/ubsan_interface.inc +++ b/lib/ubsan/ubsan_interface.inc @@ -22,6 +22,8 @@ INTERFACE_FUNCTION(__ubsan_handle_float_cast_overflow) INTERFACE_FUNCTION(__ubsan_handle_float_cast_overflow_abort) INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch) INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch_abort) +INTERFACE_FUNCTION(__ubsan_handle_implicit_conversion) +INTERFACE_FUNCTION(__ubsan_handle_implicit_conversion_abort) INTERFACE_FUNCTION(__ubsan_handle_invalid_builtin) INTERFACE_FUNCTION(__ubsan_handle_invalid_builtin_abort) INTERFACE_FUNCTION(__ubsan_handle_load_invalid_value) diff --git a/lib/ubsan_minimal/ubsan_minimal_handlers.cc b/lib/ubsan_minimal/ubsan_minimal_handlers.cc index f25a75a86..e8fc3a849 100644 --- a/lib/ubsan_minimal/ubsan_minimal_handlers.cc +++ b/lib/ubsan_minimal/ubsan_minimal_handlers.cc @@ -109,6 +109,7 @@ HANDLER(float_cast_overflow, "float-cast-overflow") HANDLER(load_invalid_value, "load-invalid-value") HANDLER(invalid_builtin, "invalid-builtin") HANDLER(function_type_mismatch, "function-type-mismatch") +HANDLER(implicit_conversion, "implicit-conversion") HANDLER(nonnull_arg, "nonnull-arg") HANDLER(nonnull_return, "nonnull-return") HANDLER(nullability_arg, "nullability-arg") diff --git a/test/fuzzer/ImplicitIntegerTruncationTest.cpp b/test/fuzzer/ImplicitIntegerTruncationTest.cpp new file mode 100644 index 000000000..cb935da0c --- /dev/null +++ b/test/fuzzer/ImplicitIntegerTruncationTest.cpp @@ -0,0 +1,27 @@ +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. + +// Test for signed-integer-overflow. +#include <assert.h> +#include <climits> +#include <cstddef> +#include <cstdint> +#include <cstdlib> +#include <iostream> + +static volatile int Sink; +static unsigned char Large = UINT8_MAX; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + assert(Data); + if (Size > 0 && Data[0] == 'H') { + Sink = 1; + if (Size > 1 && Data[1] == 'i') { + Sink = 2; + if (Size > 2 && Data[2] == '!') { + Large = Large + 1; // 'char overflow'. + } + } + } + return 0; +} diff --git a/test/fuzzer/fuzzer-implicit-integer-truncation.test b/test/fuzzer/fuzzer-implicit-integer-truncation.test new file mode 100644 index 000000000..212559bdc --- /dev/null +++ b/test/fuzzer/fuzzer-implicit-integer-truncation.test @@ -0,0 +1,5 @@ +RUN: rm -f %t-ImplicitIntegerTruncationTest-Ubsan +RUN: %cpp_compiler -fsanitize=implicit-integer-truncation -fno-sanitize-recover=all %S/ImplicitIntegerTruncationTest.cpp -o %t-ImplicitIntegerTruncationTest-Ubsan +RUN: not %run %t-ImplicitIntegerTruncationTest-Ubsan 2>&1 | FileCheck %s +CHECK: runtime error: implicit conversion from type 'int' of value 256 (32-bit, signed) to type 'unsigned char' changed the value to 0 (8-bit, unsigned) +CHECK: Test unit written to ./crash- diff --git a/test/ubsan/TestCases/ImplicitCast/integer-truncation-blacklist.c b/test/ubsan/TestCases/ImplicitCast/integer-truncation-blacklist.c new file mode 100644 index 000000000..221c04b60 --- /dev/null +++ b/test/ubsan/TestCases/ImplicitCast/integer-truncation-blacklist.c @@ -0,0 +1,15 @@ +// RUN: rm -f %tmp +// RUN: echo "[implicit-integer-truncation]" >> %tmp +// RUN: echo "fun:*implicitTruncation*" >> %tmp +// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -fsanitize-blacklist=%tmp -O0 %s -o %t && not %run %t 2>&1 +// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -fsanitize-blacklist=%tmp -O1 %s -o %t && not %run %t 2>&1 +// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -fsanitize-blacklist=%tmp -O2 %s -o %t && not %run %t 2>&1 +// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -fsanitize-blacklist=%tmp -O3 %s -o %t && not %run %t 2>&1 + +unsigned char implicitTruncation(unsigned int argc) { + return argc; // BOOM +} + +int main(int argc, char **argv) { + return implicitTruncation(~0U); +} diff --git a/test/ubsan/TestCases/ImplicitCast/integer-truncation-summary.cpp b/test/ubsan/TestCases/ImplicitCast/integer-truncation-summary.cpp new file mode 100644 index 000000000..a92e01fb4 --- /dev/null +++ b/test/ubsan/TestCases/ImplicitCast/integer-truncation-summary.cpp @@ -0,0 +1,13 @@ +// RUN: %clangxx -fsanitize=implicit-integer-truncation %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOTYPE +// RUN: %env_ubsan_opts=report_error_type=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TYPE +// REQUIRES: !ubsan-standalone && !ubsan-standalone-static + +#include <stdint.h> + +int main() { + uint8_t t0 = (~(uint32_t(0))); + // CHECK-NOTYPE: SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]]:16 + // CHECK-TYPE: SUMMARY: UndefinedBehaviorSanitizer: implicit-integer-truncation {{.*}}summary.cpp:[[@LINE-2]]:16 + return 0; +} diff --git a/test/ubsan/TestCases/ImplicitCast/integer-truncation.c b/test/ubsan/TestCases/ImplicitCast/integer-truncation.c new file mode 100644 index 000000000..995eb7d0f --- /dev/null +++ b/test/ubsan/TestCases/ImplicitCast/integer-truncation.c @@ -0,0 +1,63 @@ +// RUN: %clang -x c -fsanitize=implicit-integer-truncation %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK +// RUN: %clangxx -x c++ -fsanitize=implicit-integer-truncation %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK + +#include <stdint.h> + +#if !defined(__cplusplus) +#define bool _Bool +#endif + +int main() { +// CHECK-NOT: integer-truncation.c + + // Negative tests. Even if they produce unexpected results, this sanitizer does not care. + int8_t n0 = (~((uint32_t)0)); // ~0 -> -1, but do not warn. + uint8_t n2 = 128; + uint8_t n3 = 255; + // Bools do not count + bool b0 = (~((uint32_t)0)); + bool b1 = 255; + + // Explicit and-ing of bits will silence it. + uint8_t nc0 = (~((uint32_t)0)) & 255; + + // Explicit casts + uint8_t i0 = (uint8_t)(~((uint32_t)0)); + +#if defined(__cplusplus) + uint8_t i1 = uint8_t(~(uint32_t(0))); + uint8_t i2 = static_cast<uint8_t>(~(uint32_t(0))); +#endif + + // Positive tests. + + uint8_t t_b0 = (~((uint16_t)(0))); +// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:18: runtime error: implicit conversion from type 'int' of value -1 (32-bit, signed) to type 'uint8_t' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned) + + uint8_t t_b1 = (~((uint32_t)0)); +// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:18: runtime error: implicit conversion from type 'uint32_t' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type 'uint8_t' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned) + uint16_t t_b2 = (~((uint32_t)0)); +// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:19: runtime error: implicit conversion from type 'uint32_t' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type 'uint16_t' (aka 'unsigned short') changed the value to 65535 (16-bit, unsigned) + + uint8_t t_b3 = ~((uint64_t)0); +// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:18: runtime error: implicit conversion from type 'uint64_t' (aka 'unsigned long{{[^']*}}') of value 18446744073709551615 (64-bit, unsigned) to type 'uint8_t' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned) + uint16_t t_b4 = ~((uint64_t)0); +// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:19: runtime error: implicit conversion from type 'uint64_t' (aka 'unsigned long{{[^']*}}') of value 18446744073709551615 (64-bit, unsigned) to type 'uint16_t' (aka 'unsigned short') changed the value to 65535 (16-bit, unsigned) + uint32_t t_b5 = ~((uint64_t)0); +// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:19: runtime error: implicit conversion from type 'uint64_t' (aka 'unsigned long{{[^']*}}') of value 18446744073709551615 (64-bit, unsigned) to type 'uint32_t' (aka 'unsigned int') changed the value to 4294967295 (32-bit, unsigned) + + int8_t t1 = 255; +// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:15: runtime error: implicit conversion from type 'int' of value 255 (32-bit, signed) to type 'int8_t' (aka 'signed char') changed the value to -1 (8-bit, signed) + uint8_t t2 = 256; +// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:16: runtime error: implicit conversion from type 'int' of value 256 (32-bit, signed) to type 'uint8_t' (aka 'unsigned char') changed the value to 0 (8-bit, unsigned) + int8_t t3 = 256; +// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:15: runtime error: implicit conversion from type 'int' of value 256 (32-bit, signed) to type 'int8_t' (aka 'signed char') changed the value to 0 (8-bit, signed) + uint8_t t4 = 257; +// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:16: runtime error: implicit conversion from type 'int' of value 257 (32-bit, signed) to type 'uint8_t' (aka 'unsigned char') changed the value to 1 (8-bit, unsigned) + int8_t t5 = 257; +// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:15: runtime error: implicit conversion from type 'int' of value 257 (32-bit, signed) to type 'int8_t' (aka 'signed char') changed the value to 1 (8-bit, signed) + int8_t t6 = 128; +// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:15: runtime error: implicit conversion from type 'int' of value 128 (32-bit, signed) to type 'int8_t' (aka 'signed char') changed the value to -128 (8-bit, signed) + + return 0; +} diff --git a/test/ubsan_minimal/TestCases/implicit-integer-truncation.c b/test/ubsan_minimal/TestCases/implicit-integer-truncation.c new file mode 100644 index 000000000..1db6e6976 --- /dev/null +++ b/test/ubsan_minimal/TestCases/implicit-integer-truncation.c @@ -0,0 +1,24 @@ +// RUN: %clang -fsanitize=implicit-integer-truncation %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK + +#include <stdint.h> + +int main() { +// CHECK-NOT: integer-truncation.c + + // Negative tests. Even if they produce unexpected results, this sanitizer does not care. + int8_t n0 = (~((uint32_t)(0))); // ~0 -> -1, but do not warn. + uint8_t n2 = 128; + uint8_t n3 = 255; + // Bools do not count + _Bool b0 = (~((uint32_t)(0))); + _Bool b1 = 255; + + // Explicit and-ing of bits will silence it. + uint8_t nc0 = ((~((uint32_t)(0))) & 255); + + // Positive tests. + uint8_t t0 = (~((uint32_t)(0))); +// CHECK: implicit-conversion + + return 0; +} |