diff options
Diffstat (limited to 'test/ubsan')
33 files changed, 1049 insertions, 0 deletions
diff --git a/test/ubsan/CMakeLists.txt b/test/ubsan/CMakeLists.txt new file mode 100644 index 000000000000..1c0c92903298 --- /dev/null +++ b/test/ubsan/CMakeLists.txt @@ -0,0 +1,25 @@ +set(UBSAN_LIT_TESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +set(UBSAN_LIT_TEST_MODE "Standalone") +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/UbsanConfig/lit.site.cfg) +set(UBSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/UbsanConfig) + +if(COMPILER_RT_HAS_ASAN) + set(UBSAN_LIT_TEST_MODE "AddressSanitizer") + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig/lit.site.cfg) + list(APPEND UBSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig) +endif() + +set(UBSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) +if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND UBSAN_TEST_DEPS ubsan asan) +endif() + +add_lit_testsuite(check-ubsan "Running UndefinedBehaviorSanitizer tests" + ${UBSAN_TESTSUITES} + DEPENDS ${UBSAN_TEST_DEPS}) +set_target_properties(check-ubsan PROPERTIES FOLDER "UBSan unittests") diff --git a/test/ubsan/TestCases/Float/cast-overflow.cpp b/test/ubsan/TestCases/Float/cast-overflow.cpp new file mode 100644 index 000000000000..22991e0a6c55 --- /dev/null +++ b/test/ubsan/TestCases/Float/cast-overflow.cpp @@ -0,0 +1,134 @@ +// FIXME: run this (and other) UBSan tests in both 32- and 64-bit modes (?). +// RUN: %clangxx -fsanitize=float-cast-overflow %s -o %t +// RUN: %run %t _ +// RUN: %run %t 0 2>&1 | FileCheck %s --check-prefix=CHECK-0 +// RUN: %run %t 1 2>&1 | FileCheck %s --check-prefix=CHECK-1 +// RUN: %run %t 2 2>&1 | FileCheck %s --check-prefix=CHECK-2 +// RUN: %run %t 3 2>&1 | FileCheck %s --check-prefix=CHECK-3 +// RUN: %run %t 4 2>&1 | FileCheck %s --check-prefix=CHECK-4 +// RUN: %run %t 5 2>&1 | FileCheck %s --check-prefix=CHECK-5 +// RUN: %run %t 6 2>&1 | FileCheck %s --check-prefix=CHECK-6 +// FIXME: %run %t 7 2>&1 | FileCheck %s --check-prefix=CHECK-7 +// FIXME: not %run %t 8 2>&1 | FileCheck %s --check-prefix=CHECK-8 +// RUN: not %run %t 9 2>&1 | FileCheck %s --check-prefix=CHECK-9 + +// This test assumes float and double are IEEE-754 single- and double-precision. +// XFAIL: armv7l-unknown-linux-gnueabihf + +#if defined(__APPLE__) +# include <machine/endian.h> +# define BYTE_ORDER __DARWIN_BYTE_ORDER +# define BIG_ENDIAN __DARWIN_BIG_ENDIAN +# define LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN +#elif defined(__FreeBSD__) +# include <sys/endian.h> +# define BYTE_ORDER _BYTE_ORDER +# define BIG_ENDIAN _BIG_ENDIAN +# define LITTLE_ENDIAN _LITTLE_ENDIAN +#else +# include <endian.h> +# define BYTE_ORDER __BYTE_ORDER +# define BIG_ENDIAN __BIG_ENDIAN +# define LITTLE_ENDIAN __LITTLE_ENDIAN +#endif // __APPLE__ +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +float Inf; +float NaN; + +int main(int argc, char **argv) { + float MaxFloatRepresentableAsInt = 0x7fffff80; + (int)MaxFloatRepresentableAsInt; // ok + (int)-MaxFloatRepresentableAsInt; // ok + + float MinFloatRepresentableAsInt = -0x7fffffff - 1; + (int)MinFloatRepresentableAsInt; // ok + + float MaxFloatRepresentableAsUInt = 0xffffff00u; + (unsigned int)MaxFloatRepresentableAsUInt; // ok + +#ifdef __SIZEOF_INT128__ + unsigned __int128 FloatMaxAsUInt128 = -((unsigned __int128)1 << 104); + (void)(float)FloatMaxAsUInt128; // ok +#endif + + float NearlyMinusOne = -0.99999; + unsigned Zero = NearlyMinusOne; // ok + + // Build a '+Inf'. +#if BYTE_ORDER == LITTLE_ENDIAN + char InfVal[] = { 0x00, 0x00, 0x80, 0x7f }; +#else + char InfVal[] = { 0x7f, 0x80, 0x00, 0x00 }; +#endif + float Inf; + memcpy(&Inf, InfVal, 4); + + // Build a 'NaN'. +#if BYTE_ORDER == LITTLE_ENDIAN + char NaNVal[] = { 0x01, 0x00, 0x80, 0x7f }; +#else + char NaNVal[] = { 0x7f, 0x80, 0x00, 0x01 }; +#endif + float NaN; + memcpy(&NaN, NaNVal, 4); + + double DblInf = (double)Inf; // ok + + switch (argv[1][0]) { + // FIXME: Produce a source location for these checks and test for it here. + + // Floating point -> integer overflow. + case '0': + // Note that values between 0x7ffffe00 and 0x80000000 may or may not + // successfully round-trip, depending on the rounding mode. + // CHECK-0: runtime error: value 2.14748{{.*}} is outside the range of representable values of type 'int' + return MaxFloatRepresentableAsInt + 0x80; + case '1': + // CHECK-1: runtime error: value -2.14748{{.*}} is outside the range of representable values of type 'int' + return MinFloatRepresentableAsInt - 0x100; + case '2': { + // CHECK-2: runtime error: value -1 is outside the range of representable values of type 'unsigned int' + volatile float f = -1.0; + volatile unsigned u = (unsigned)f; + return 0; + } + case '3': + // CHECK-3: runtime error: value 4.2949{{.*}} is outside the range of representable values of type 'unsigned int' + return (unsigned)(MaxFloatRepresentableAsUInt + 0x100); + + case '4': + // CHECK-4: runtime error: value {{.*}} is outside the range of representable values of type 'int' + return Inf; + case '5': + // CHECK-5: runtime error: value {{.*}} is outside the range of representable values of type 'int' + return NaN; + + // Integer -> floating point overflow. + case '6': + // CHECK-6: {{runtime error: value 0xffffff00000000000000000000000001 is outside the range of representable values of type 'float'|__int128 not supported}} +#ifdef __SIZEOF_INT128__ + return (float)(FloatMaxAsUInt128 + 1); +#else + puts("__int128 not supported"); + return 0; +#endif + // FIXME: The backend cannot lower __fp16 operations on x86 yet. + //case '7': + // (__fp16)65504; // ok + // // CHECK-7: runtime error: value 65505 is outside the range of representable values of type '__fp16' + // return (__fp16)65505; + + // Floating point -> floating point overflow. + case '8': + // CHECK-8: runtime error: value 1e+39 is outside the range of representable values of type 'float' + return (float)1e39; + case '9': + volatile long double ld = 300.0; + // CHECK-9: runtime error: value 300 is outside the range of representable values of type 'char' + char c = ld; + return c; + } +} diff --git a/test/ubsan/TestCases/Integer/add-overflow.cpp b/test/ubsan/TestCases/Integer/add-overflow.cpp new file mode 100644 index 000000000000..d3425828ec88 --- /dev/null +++ b/test/ubsan/TestCases/Integer/add-overflow.cpp @@ -0,0 +1,32 @@ +// RUN: %clangxx -DADD_I32 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I32 +// RUN: %clangxx -DADD_I64 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I64 +// RUN: %clangxx -DADD_I128 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I128 + +#include <stdint.h> +#include <stdio.h> + +int main() { + // These promote to 'int'. + (void)(int8_t(0x7f) + int8_t(0x7f)); + (void)(int16_t(0x3fff) + int16_t(0x4000)); + +#ifdef ADD_I32 + int32_t k = 0x12345678; + k += 0x789abcde; + // CHECK-ADD_I32: add-overflow.cpp:[[@LINE-1]]:5: runtime error: signed integer overflow: 305419896 + 2023406814 cannot be represented in type 'int' +#endif + +#ifdef ADD_I64 + (void)(int64_t(8000000000000000000ll) + int64_t(2000000000000000000ll)); + // CHECK-ADD_I64: 8000000000000000000 + 2000000000000000000 cannot be represented in type '{{long( long)?}}' +#endif + +#ifdef ADD_I128 +# ifdef __SIZEOF_INT128__ + (void)((__int128_t(1) << 126) + (__int128_t(1) << 126)); +# else + puts("__int128 not supported"); +# endif + // CHECK-ADD_I128: {{0x40000000000000000000000000000000 \+ 0x40000000000000000000000000000000 cannot be represented in type '__int128'|__int128 not supported}} +#endif +} diff --git a/test/ubsan/TestCases/Integer/div-overflow.cpp b/test/ubsan/TestCases/Integer/div-overflow.cpp new file mode 100644 index 000000000000..76dd60de45ce --- /dev/null +++ b/test/ubsan/TestCases/Integer/div-overflow.cpp @@ -0,0 +1,10 @@ +// RUN: %clangxx -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s + +#include <stdint.h> + +int main() { + unsigned(0x80000000) / -1; + + // CHECK: div-overflow.cpp:9:23: runtime error: division of -2147483648 by -1 cannot be represented in type 'int' + int32_t(0x80000000) / -1; +} diff --git a/test/ubsan/TestCases/Integer/div-zero.cpp b/test/ubsan/TestCases/Integer/div-zero.cpp new file mode 100644 index 000000000000..9a223312e8e7 --- /dev/null +++ b/test/ubsan/TestCases/Integer/div-zero.cpp @@ -0,0 +1,15 @@ +// RUN: %clangxx -fsanitize=integer-divide-by-zero -DDIVIDEND=0 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=integer-divide-by-zero -DDIVIDEND=1U %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=float-divide-by-zero -DDIVIDEND=1.5 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=integer-divide-by-zero -DDIVIDEND='intmax(123)' %s -o %t && %run %t 2>&1 | FileCheck %s + +#ifdef __SIZEOF_INT128__ +typedef __int128 intmax; +#else +typedef long long intmax; +#endif + +int main() { + // CHECK: div-zero.cpp:[[@LINE+1]]:12: runtime error: division by zero + DIVIDEND / 0; +} diff --git a/test/ubsan/TestCases/Integer/incdec-overflow.cpp b/test/ubsan/TestCases/Integer/incdec-overflow.cpp new file mode 100644 index 000000000000..fc7141c803d8 --- /dev/null +++ b/test/ubsan/TestCases/Integer/incdec-overflow.cpp @@ -0,0 +1,16 @@ +// RUN: %clangxx -DOP=n++ -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -DOP=++n -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -DOP=m-- -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -DOP=--m -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s + +#include <stdint.h> + +int main() { + int n = 0x7ffffffd; + n++; + n++; + int m = -n - 1; + // CHECK: incdec-overflow.cpp:15:3: runtime error: signed integer overflow: [[MINUS:-?]]214748364 + // CHECK: + [[MINUS]]1 cannot be represented in type 'int' + OP; +} diff --git a/test/ubsan/TestCases/Integer/mul-overflow.cpp b/test/ubsan/TestCases/Integer/mul-overflow.cpp new file mode 100644 index 000000000000..20fece5f9fe2 --- /dev/null +++ b/test/ubsan/TestCases/Integer/mul-overflow.cpp @@ -0,0 +1,14 @@ +// RUN: %clangxx -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s + +#include <stdint.h> + +int main() { + // These promote to 'int'. + (void)(int8_t(-2) * int8_t(0x7f)); + (void)(int16_t(0x7fff) * int16_t(0x7fff)); + (void)(uint16_t(0xffff) * int16_t(0x7fff)); + (void)(uint16_t(0xffff) * uint16_t(0x8000)); + + // CHECK: mul-overflow.cpp:13:27: runtime error: signed integer overflow: 65535 * 32769 cannot be represented in type 'int' + (void)(uint16_t(0xffff) * uint16_t(0x8001)); +} diff --git a/test/ubsan/TestCases/Integer/negate-overflow.cpp b/test/ubsan/TestCases/Integer/negate-overflow.cpp new file mode 100644 index 000000000000..bde0bdabb292 --- /dev/null +++ b/test/ubsan/TestCases/Integer/negate-overflow.cpp @@ -0,0 +1,12 @@ +// RUN: %clangxx -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECKS +// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECKU + +int main() { + // CHECKS-NOT: runtime error + // CHECKU: negate-overflow.cpp:[[@LINE+2]]:3: runtime error: negation of 2147483648 cannot be represented in type 'unsigned int' + // CHECKU-NOT: cast to an unsigned + -unsigned(-0x7fffffff - 1); // ok + // CHECKS: negate-overflow.cpp:[[@LINE+2]]:10: runtime error: negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself + // CHECKU-NOT: runtime error + return -(-0x7fffffff - 1); +} diff --git a/test/ubsan/TestCases/Integer/no-recover.cpp b/test/ubsan/TestCases/Integer/no-recover.cpp new file mode 100644 index 000000000000..575bd0a553fb --- /dev/null +++ b/test/ubsan/TestCases/Integer/no-recover.cpp @@ -0,0 +1,22 @@ +// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=RECOVER +// RUN: %clangxx -fsanitize=unsigned-integer-overflow -fsanitize-recover %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=RECOVER +// RUN: %clangxx -fsanitize=unsigned-integer-overflow -fno-sanitize-recover %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=ABORT + +#include <stdint.h> + +int main() { + // These promote to 'int'. + (void)(uint8_t(0xff) + uint8_t(0xff)); + (void)(uint16_t(0xf0fff) + uint16_t(0x0fff)); + // RECOVER-NOT: runtime error + // ABORT-NOT: runtime error + + uint32_t k = 0x87654321; + k += 0xedcba987; + // RECOVER: no-recover.cpp:[[@LINE-1]]:5: runtime error: unsigned integer overflow: 2271560481 + 3989547399 cannot be represented in type 'unsigned int' + // ABORT: no-recover.cpp:[[@LINE-2]]:5: runtime error: unsigned integer overflow: 2271560481 + 3989547399 cannot be represented in type 'unsigned int' + + (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull)); + // RECOVER: 10000000000000000000 + 9000000000000000000 cannot be represented in type 'unsigned {{long( long)?}}' + // ABORT-NOT: runtime error +} diff --git a/test/ubsan/TestCases/Integer/shift.cpp b/test/ubsan/TestCases/Integer/shift.cpp new file mode 100644 index 000000000000..e86fac8d574a --- /dev/null +++ b/test/ubsan/TestCases/Integer/shift.cpp @@ -0,0 +1,37 @@ +// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-LSH_OVERFLOW +// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<=' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-LSH_OVERFLOW +// RUN: %clangxx -DTOO_LOW -DOP='<<' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='>>' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='<<=' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='>>=' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_HIGH -DOP='<<' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='>>' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='<<=' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='>>=' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH + +#include <stdint.h> + +int main() { + int a = 1; + unsigned b = 1; + + a <<= 31; // ok in C++11, not ok in C99/C11 + b <<= 31; // ok + b <<= 1; // still ok, unsigned + +#ifdef LSH_OVERFLOW + // CHECK-LSH_OVERFLOW: shift.cpp:24:5: runtime error: left shift of negative value -2147483648 + a OP 1; +#endif + +#ifdef TOO_LOW + // CHECK-TOO_LOW: shift.cpp:29:5: runtime error: shift exponent -3 is negative + a OP (-3); +#endif + +#ifdef TOO_HIGH + a = 0; + // CHECK-TOO_HIGH: shift.cpp:35:5: runtime error: shift exponent 32 is too large for 32-bit type 'int' + a OP 32; +#endif +} diff --git a/test/ubsan/TestCases/Integer/sub-overflow.cpp b/test/ubsan/TestCases/Integer/sub-overflow.cpp new file mode 100644 index 000000000000..15e64d951603 --- /dev/null +++ b/test/ubsan/TestCases/Integer/sub-overflow.cpp @@ -0,0 +1,31 @@ +// RUN: %clangxx -DSUB_I32 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I32 +// RUN: %clangxx -DSUB_I64 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I64 +// RUN: %clangxx -DSUB_I128 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I128 + +#include <stdint.h> +#include <stdio.h> + +int main() { + // These promote to 'int'. + (void)(int8_t(-2) - int8_t(0x7f)); + (void)(int16_t(-2) - int16_t(0x7fff)); + +#ifdef SUB_I32 + (void)(int32_t(-2) - int32_t(0x7fffffff)); + // CHECK-SUB_I32: sub-overflow.cpp:[[@LINE-1]]:22: runtime error: signed integer overflow: -2 - 2147483647 cannot be represented in type 'int' +#endif + +#ifdef SUB_I64 + (void)(int64_t(-8000000000000000000ll) - int64_t(2000000000000000000ll)); + // CHECK-SUB_I64: -8000000000000000000 - 2000000000000000000 cannot be represented in type '{{long( long)?}}' +#endif + +#ifdef SUB_I128 +# ifdef __SIZEOF_INT128__ + (void)(-(__int128_t(1) << 126) - (__int128_t(1) << 126) - 1); +# else + puts("__int128 not supported"); +# endif + // CHECK-SUB_I128: {{0x80000000000000000000000000000000 - 1 cannot be represented in type '__int128'|__int128 not supported}} +#endif +} diff --git a/test/ubsan/TestCases/Integer/summary.cpp b/test/ubsan/TestCases/Integer/summary.cpp new file mode 100644 index 000000000000..6e9aec63ca74 --- /dev/null +++ b/test/ubsan/TestCases/Integer/summary.cpp @@ -0,0 +1,10 @@ +// RUN: %clangxx -fsanitize=integer %s -o %t && %t 2>&1 | FileCheck %s +// REQUIRES: ubsan-asan + +#include <stdint.h> + +int main() { + (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull)); + // CHECK: SUMMARY: AddressSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]] + return 0; +} diff --git a/test/ubsan/TestCases/Integer/uadd-overflow.cpp b/test/ubsan/TestCases/Integer/uadd-overflow.cpp new file mode 100644 index 000000000000..7a96880fe636 --- /dev/null +++ b/test/ubsan/TestCases/Integer/uadd-overflow.cpp @@ -0,0 +1,32 @@ +// RUN: %clangxx -DADD_I32 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I32 +// RUN: %clangxx -DADD_I64 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I64 +// RUN: %clangxx -DADD_I128 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I128 + +#include <stdint.h> +#include <stdio.h> + +int main() { + // These promote to 'int'. + (void)(uint8_t(0xff) + uint8_t(0xff)); + (void)(uint16_t(0xf0fff) + uint16_t(0x0fff)); + +#ifdef ADD_I32 + uint32_t k = 0x87654321; + k += 0xedcba987; + // CHECK-ADD_I32: uadd-overflow.cpp:[[@LINE-1]]:5: runtime error: unsigned integer overflow: 2271560481 + 3989547399 cannot be represented in type 'unsigned int' +#endif + +#ifdef ADD_I64 + (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull)); + // CHECK-ADD_I64: 10000000000000000000 + 9000000000000000000 cannot be represented in type 'unsigned {{long( long)?}}' +#endif + +#ifdef ADD_I128 +# ifdef __SIZEOF_INT128__ + (void)((__uint128_t(1) << 127) + (__uint128_t(1) << 127)); +# else + puts("__int128 not supported"); +# endif + // CHECK-ADD_I128: {{0x80000000000000000000000000000000 \+ 0x80000000000000000000000000000000 cannot be represented in type 'unsigned __int128'|__int128 not supported}} +#endif +} diff --git a/test/ubsan/TestCases/Integer/uincdec-overflow.cpp b/test/ubsan/TestCases/Integer/uincdec-overflow.cpp new file mode 100644 index 000000000000..a236d21fcf1f --- /dev/null +++ b/test/ubsan/TestCases/Integer/uincdec-overflow.cpp @@ -0,0 +1,16 @@ +// RUN: %clangxx -DOP=n++ -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck --check-prefix=CHECK-INC %s +// RUN: %clangxx -DOP=++n -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck --check-prefix=CHECK-INC %s +// RUN: %clangxx -DOP=m-- -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck --check-prefix=CHECK-DEC %s +// RUN: %clangxx -DOP=--m -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck --check-prefix=CHECK-DEC %s + +#include <stdint.h> + +int main() { + unsigned n = 0xfffffffd; + n++; + n++; + unsigned m = 0; + // CHECK-INC: uincdec-overflow.cpp:15:3: runtime error: unsigned integer overflow: 4294967295 + 1 cannot be represented in type 'unsigned int' + // CHECK-DEC: uincdec-overflow.cpp:15:3: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'unsigned int' + OP; +} diff --git a/test/ubsan/TestCases/Integer/umul-overflow.cpp b/test/ubsan/TestCases/Integer/umul-overflow.cpp new file mode 100644 index 000000000000..ad5d1bd0d13c --- /dev/null +++ b/test/ubsan/TestCases/Integer/umul-overflow.cpp @@ -0,0 +1,19 @@ +// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s + +#include <stdint.h> + +int main() { + // These promote to 'int'. + (void)(int8_t(-2) * int8_t(0x7f)); + (void)(int16_t(0x7fff) * int16_t(0x7fff)); + (void)(uint16_t(0xffff) * int16_t(0x7fff)); + (void)(uint16_t(0xffff) * uint16_t(0x8000)); + + // Not an unsigned overflow + (void)(uint16_t(0xffff) * uint16_t(0x8001)); + + (void)(uint32_t(0xffffffff) * uint32_t(0x2)); + // CHECK: umul-overflow.cpp:15:31: runtime error: unsigned integer overflow: 4294967295 * 2 cannot be represented in type 'unsigned int' + + return 0; +} diff --git a/test/ubsan/TestCases/Integer/usub-overflow.cpp b/test/ubsan/TestCases/Integer/usub-overflow.cpp new file mode 100644 index 000000000000..e5de7de54eaa --- /dev/null +++ b/test/ubsan/TestCases/Integer/usub-overflow.cpp @@ -0,0 +1,31 @@ +// RUN: %clangxx -DSUB_I32 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I32 +// RUN: %clangxx -DSUB_I64 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I64 +// RUN: %clangxx -DSUB_I128 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I128 + +#include <stdint.h> +#include <stdio.h> + +int main() { + // These promote to 'int'. + (void)(uint8_t(0) - uint8_t(0x7f)); + (void)(uint16_t(0) - uint16_t(0x7fff)); + +#ifdef SUB_I32 + (void)(uint32_t(1) - uint32_t(2)); + // CHECK-SUB_I32: usub-overflow.cpp:[[@LINE-1]]:22: runtime error: unsigned integer overflow: 1 - 2 cannot be represented in type 'unsigned int' +#endif + +#ifdef SUB_I64 + (void)(uint64_t(8000000000000000000ll) - uint64_t(9000000000000000000ll)); + // CHECK-SUB_I64: 8000000000000000000 - 9000000000000000000 cannot be represented in type 'unsigned {{long( long)?}}' +#endif + +#ifdef SUB_I128 +# ifdef __SIZEOF_INT128__ + (void)((__uint128_t(1) << 126) - (__uint128_t(1) << 127)); +# else + puts("__int128 not supported\n"); +# endif + // CHECK-SUB_I128: {{0x40000000000000000000000000000000 - 0x80000000000000000000000000000000 cannot be represented in type 'unsigned __int128'|__int128 not supported}} +#endif +} diff --git a/test/ubsan/TestCases/Misc/bool.cpp b/test/ubsan/TestCases/Misc/bool.cpp new file mode 100644 index 000000000000..37ecea27c941 --- /dev/null +++ b/test/ubsan/TestCases/Misc/bool.cpp @@ -0,0 +1,10 @@ +// RUN: %clangxx -fsanitize=bool %s -O3 -o %t && not %run %t 2>&1 | FileCheck %s + +unsigned char NotABool = 123; + +int main(int argc, char **argv) { + bool *p = (bool*)&NotABool; + + // CHECK: bool.cpp:9:10: runtime error: load of value 123, which is not a valid value for type 'bool' + return *p; +} diff --git a/test/ubsan/TestCases/Misc/bounds.cpp b/test/ubsan/TestCases/Misc/bounds.cpp new file mode 100644 index 000000000000..ffcac528be90 --- /dev/null +++ b/test/ubsan/TestCases/Misc/bounds.cpp @@ -0,0 +1,15 @@ +// RUN: %clangxx -fsanitize=bounds %s -O3 -o %t +// RUN: %run %t 0 0 0 +// RUN: %run %t 1 2 3 +// RUN: not --crash %run %t 2 0 0 2>&1 | FileCheck %s --check-prefix=CHECK-A-2 +// RUN: %run %t 0 3 0 2>&1 | FileCheck %s --check-prefix=CHECK-B-3 +// RUN: %run %t 0 0 4 2>&1 | FileCheck %s --check-prefix=CHECK-C-4 + +int main(int argc, char **argv) { + int arr[2][3][4] = {}; + + return arr[argv[1][0] - '0'][argv[2][0] - '0'][argv[3][0] - '0']; + // CHECK-A-2: bounds.cpp:[[@LINE-1]]:10: runtime error: index 2 out of bounds for type 'int [2][3][4]' + // CHECK-B-3: bounds.cpp:[[@LINE-2]]:10: runtime error: index 3 out of bounds for type 'int [3][4]' + // CHECK-C-4: bounds.cpp:[[@LINE-3]]:10: runtime error: index 4 out of bounds for type 'int [4]' +} diff --git a/test/ubsan/TestCases/Misc/deduplication.cpp b/test/ubsan/TestCases/Misc/deduplication.cpp new file mode 100644 index 000000000000..7d7b0bd58c6e --- /dev/null +++ b/test/ubsan/TestCases/Misc/deduplication.cpp @@ -0,0 +1,25 @@ +// RUN: %clangxx -fsanitize=undefined %s -o %t && %run %t 2>&1 | FileCheck %s +// Verify deduplication works by ensuring only one diag is emitted. +#include <limits.h> +#include <stdio.h> + +void overflow() { + int i = INT_MIN; + --i; +} + +int main() { + // CHECK: Start + fprintf(stderr, "Start\n"); + + // CHECK: runtime error + // CHECK-NOT: runtime error + // CHECK-NOT: runtime error + overflow(); + overflow(); + overflow(); + + // CHECK: End + fprintf(stderr, "End\n"); + return 0; +} diff --git a/test/ubsan/TestCases/Misc/enum.cpp b/test/ubsan/TestCases/Misc/enum.cpp new file mode 100644 index 000000000000..49ac7c6bb187 --- /dev/null +++ b/test/ubsan/TestCases/Misc/enum.cpp @@ -0,0 +1,17 @@ +// RUN: %clangxx -fsanitize=enum %s -O3 -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-PLAIN +// RUN: %clangxx -fsanitize=enum -std=c++11 -DE="class E" %s -O3 -o %t && %run %t +// RUN: %clangxx -fsanitize=enum -std=c++11 -DE="class E : bool" %s -O3 -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-BOOL + +enum E { a = 1 } e; +#undef E + +int main(int argc, char **argv) { + // memset(&e, 0xff, sizeof(e)); + for (unsigned char *p = (unsigned char*)&e; p != (unsigned char*)(&e + 1); ++p) + *p = 0xff; + + // CHECK-PLAIN: error: load of value 4294967295, which is not a valid value for type 'enum E' + // FIXME: Support marshalling and display of enum class values. + // CHECK-BOOL: error: load of value <unknown>, which is not a valid value for type 'enum E' + return (int)e != -1; +} diff --git a/test/ubsan/TestCases/Misc/missing_return.cpp b/test/ubsan/TestCases/Misc/missing_return.cpp new file mode 100644 index 000000000000..5d3d54de17dd --- /dev/null +++ b/test/ubsan/TestCases/Misc/missing_return.cpp @@ -0,0 +1,17 @@ +// RUN: %clangxx -fsanitize=return -g %s -O3 -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: UBSAN_OPTIONS=print_stacktrace=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os-STACKTRACE + +// CHECK: missing_return.cpp:[[@LINE+1]]:5: runtime error: execution reached the end of a value-returning function without returning a value +int f() { +// Slow stack unwinding is disabled on Darwin for now, see +// https://code.google.com/p/address-sanitizer/issues/detail?id=137 +// CHECK-Linux-STACKTRACE: #0 {{.*}} in f(){{.*}}missing_return.cpp:[[@LINE-3]] +// CHECK-FreeBSD-STACKTRACE: #0 {{.*}} in f(void){{.*}}missing_return.cpp:[[@LINE-4]] +// Check for already checked line to avoid lit error reports. +// CHECK-Darwin-STACKTRACE: missing_return.cpp +} + +int main(int, char **argv) { + return f(); +} diff --git a/test/ubsan/TestCases/Misc/nonnull-arg.cpp b/test/ubsan/TestCases/Misc/nonnull-arg.cpp new file mode 100644 index 000000000000..b1061b757899 --- /dev/null +++ b/test/ubsan/TestCases/Misc/nonnull-arg.cpp @@ -0,0 +1,58 @@ +// RUN: %clangxx -fsanitize=nonnull-attribute -fno-sanitize-recover %s -O3 -o %t +// RUN: %run %t nc +// RUN: %run %t nm +// RUN: %run %t nf +// RUN: %run %t nv +// RUN: not %run %t 0c 2>&1 | FileCheck %s --check-prefix=CTOR +// RUN: not %run %t 0m 2>&1 | FileCheck %s --check-prefix=METHOD +// RUN: not %run %t 0f 2>&1 | FileCheck %s --check-prefix=FUNC +// RUN: not %run %t 0v 2>&1 | FileCheck %s --check-prefix=VARIADIC + +class C { + int *null_; + int *nonnull_; + +public: + C(int *null, __attribute__((nonnull)) int *nonnull) + : null_(null), nonnull_(nonnull) {} + int value() { return *nonnull_; } + int method(int *nonnull, int *null) __attribute__((nonnull(2))) { + return *nonnull_ + *nonnull; + } +}; + +__attribute__((nonnull)) int func(int *nonnull) { return *nonnull; } + +#include <stdarg.h> +__attribute__((nonnull)) int variadic(int x, ...) { + va_list args; + va_start(args, x); + int *nonnull = va_arg(args, int*); + int res = *nonnull; + va_end(args); + return res; +} + +int main(int argc, char *argv[]) { + int local = 0; + int *arg = (argv[1][0] == '0') ? 0x0 : &local; + switch (argv[1][1]) { + case 'c': + return C(0x0, arg).value(); + // CTOR: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:21: runtime error: null pointer passed as argument 2, which is declared to never be null + // CTOR-NEXT: {{.*}}nonnull-arg.cpp:16:31: note: nonnull attribute specified here + case 'm': + return C(0x0, &local).method(arg, 0x0); + // METHOD: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:36: runtime error: null pointer passed as argument 1, which is declared to never be null + // METHOD-NEXT: {{.*}}nonnull-arg.cpp:19:54: note: nonnull attribute specified here + case 'f': + return func(arg); + // FUNC: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:19: runtime error: null pointer passed as argument 1, which is declared to never be null + // FUNC-NEXT: {{.*}}nonnull-arg.cpp:24:16: note: nonnull attribute specified here + case 'v': + return variadic(42, arg); + // VARIADIC: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:27: runtime error: null pointer passed as argument 2, which is declared to never be null + // VARIADIC-NEXT: {{.*}}nonnull-arg.cpp:27:16: note: nonnull attribute specified here + } + return 0; +} diff --git a/test/ubsan/TestCases/Misc/nonnull.cpp b/test/ubsan/TestCases/Misc/nonnull.cpp new file mode 100644 index 000000000000..c3ab49c11df7 --- /dev/null +++ b/test/ubsan/TestCases/Misc/nonnull.cpp @@ -0,0 +1,15 @@ +// RUN: %clangxx -fsanitize=returns-nonnull-attribute %s -O3 -o %t +// RUN: %run %t foo +// RUN: %run %t 2>&1 | FileCheck %s + +__attribute__((returns_nonnull)) char *foo(char *a); + +char *foo(char *a) { + return a; + // CHECK: nonnull.cpp:[[@LINE+2]]:1: runtime error: null pointer returned from function declared to never return null + // CHECK-NEXT: nonnull.cpp:[[@LINE-5]]:16: note: returns_nonnull attribute specified here +} + +int main(int argc, char **argv) { + return foo(argv[1]) == 0; +} diff --git a/test/ubsan/TestCases/Misc/unreachable.cpp b/test/ubsan/TestCases/Misc/unreachable.cpp new file mode 100644 index 000000000000..e1206edb30d5 --- /dev/null +++ b/test/ubsan/TestCases/Misc/unreachable.cpp @@ -0,0 +1,6 @@ +// RUN: %clangxx -fsanitize=unreachable %s -O3 -o %t && not %run %t 2>&1 | FileCheck %s + +int main(int, char **argv) { + // CHECK: unreachable.cpp:5:3: runtime error: execution reached a __builtin_unreachable() call + __builtin_unreachable(); +} diff --git a/test/ubsan/TestCases/Misc/vla.c b/test/ubsan/TestCases/Misc/vla.c new file mode 100644 index 000000000000..10721537bbbc --- /dev/null +++ b/test/ubsan/TestCases/Misc/vla.c @@ -0,0 +1,11 @@ +// RUN: %clang -fsanitize=vla-bound %s -O3 -o %t +// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-MINUS-ONE +// RUN: %run %t a 2>&1 | FileCheck %s --check-prefix=CHECK-ZERO +// RUN: %run %t a b + +int main(int argc, char **argv) { + // CHECK-MINUS-ONE: vla.c:9:11: runtime error: variable length array bound evaluates to non-positive value -1 + // CHECK-ZERO: vla.c:9:11: runtime error: variable length array bound evaluates to non-positive value 0 + int arr[argc - 2]; + return 0; +} diff --git a/test/ubsan/TestCases/TypeCheck/Function/function.cpp b/test/ubsan/TestCases/TypeCheck/Function/function.cpp new file mode 100644 index 000000000000..deca77d459c0 --- /dev/null +++ b/test/ubsan/TestCases/TypeCheck/Function/function.cpp @@ -0,0 +1,24 @@ +// RUN: %clangxx -fsanitize=function %s -O3 -g -o %t +// RUN: %run %t 2>&1 | FileCheck %s +// Verify that we can disable symbolization if needed: +// RUN: UBSAN_OPTIONS=symbolize=0 ASAN_OPTIONS=symbolize=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOSYM + +// -fsanitize=function is unsupported on Darwin yet. +// XFAIL: darwin + +#include <stdint.h> + +void f() {} + +void g(int x) {} + +int main(void) { + // CHECK: runtime error: call to function f() through pointer to incorrect function type 'void (*)(int)' + // CHECK-NEXT: function.cpp:11: note: f() defined here + // NOSYM: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int)' + // NOSYM-NEXT: ({{.*}}+0x{{.*}}): note: (unknown) defined here + reinterpret_cast<void (*)(int)>(reinterpret_cast<uintptr_t>(f))(42); + + // CHECK-NOT: runtime error: call to function g + reinterpret_cast<void (*)(int)>(reinterpret_cast<uintptr_t>(g))(42); +} diff --git a/test/ubsan/TestCases/TypeCheck/Function/lit.local.cfg b/test/ubsan/TestCases/TypeCheck/Function/lit.local.cfg new file mode 100644 index 000000000000..27c61a34387c --- /dev/null +++ b/test/ubsan/TestCases/TypeCheck/Function/lit.local.cfg @@ -0,0 +1,3 @@ +# The function type checker is only supported on x86 and x86_64 for now. +if config.root.host_arch not in ['x86', 'x86_64']: + config.unsupported = True diff --git a/test/ubsan/TestCases/TypeCheck/misaligned.cpp b/test/ubsan/TestCases/TypeCheck/misaligned.cpp new file mode 100644 index 000000000000..79f5136db963 --- /dev/null +++ b/test/ubsan/TestCases/TypeCheck/misaligned.cpp @@ -0,0 +1,105 @@ +// RUN: %clangxx -fsanitize=alignment -g %s -O3 -o %t +// RUN: %run %t l0 && %run %t s0 && %run %t r0 && %run %t m0 && %run %t f0 && %run %t n0 && %run %t u0 +// RUN: %run %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --strict-whitespace +// RUN: %run %t s1 2>&1 | FileCheck %s --check-prefix=CHECK-STORE +// RUN: %run %t r1 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE +// RUN: %run %t m1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER +// RUN: %run %t f1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN +// RUN: %run %t n1 2>&1 | FileCheck %s --check-prefix=CHECK-NEW +// RUN: %run %t u1 2>&1 | FileCheck %s --check-prefix=CHECK-UPCAST +// RUN: UBSAN_OPTIONS=print_stacktrace=1 %run %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --check-prefix=CHECK-%os-STACK-LOAD + +// RUN: %clangxx -fsanitize=alignment -fno-sanitize-recover %s -O3 -o %t +// RUN: not %run %t w1 2>&1 | FileCheck %s --check-prefix=CHECK-WILD +// XFAIL: armv7l-unknown-linux-gnueabihf + +#include <new> + +struct S { + S() {} + int f() { return 0; } + int k; +}; + +struct T : S { + int t; +}; + +int main(int, char **argv) { + char c[] __attribute__((aligned(8))) = { 0, 0, 0, 0, 1, 2, 3, 4, 5 }; + + // Pointer value may be unspecified here, but behavior is not undefined. + int *p = (int*)&c[4 + argv[1][1] - '0']; + S *s = (S*)p; + T *t = (T*)p; + + void *wild = reinterpret_cast<void *>(0x123L); + + (void)*p; // ok! + + switch (argv[1][0]) { + case 'l': + // CHECK-LOAD: misaligned.cpp:[[@LINE+4]]:12: runtime error: load of misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment + // CHECK-LOAD-NEXT: [[PTR]]: note: pointer points here + // CHECK-LOAD-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-LOAD-NEXT: {{^ \^}} + return *p && 0; + // Slow stack unwinding is disabled on Darwin for now, see + // https://code.google.com/p/address-sanitizer/issues/detail?id=137 + // CHECK-Linux-STACK-LOAD: #0 {{.*}} in main{{.*}}misaligned.cpp + // Check for the already checked line to avoid lit error reports. + // CHECK-Darwin-STACK-LOAD: {{ }} + + case 's': + // CHECK-STORE: misaligned.cpp:[[@LINE+4]]:5: runtime error: store to misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment + // CHECK-STORE-NEXT: [[PTR]]: note: pointer points here + // CHECK-STORE-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-STORE-NEXT: {{^ \^}} + *p = 1; + break; + + case 'r': + // CHECK-REFERENCE: misaligned.cpp:[[@LINE+4]]:15: runtime error: reference binding to misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment + // CHECK-REFERENCE-NEXT: [[PTR]]: note: pointer points here + // CHECK-REFERENCE-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-REFERENCE-NEXT: {{^ \^}} + {int &r = *p;} + break; + + case 'm': + // CHECK-MEMBER: misaligned.cpp:[[@LINE+4]]:15: runtime error: member access within misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-MEMBER-NEXT: [[PTR]]: note: pointer points here + // CHECK-MEMBER-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-MEMBER-NEXT: {{^ \^}} + return s->k && 0; + + case 'f': + // CHECK-MEMFUN: misaligned.cpp:[[@LINE+4]]:12: runtime error: member call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-MEMFUN-NEXT: [[PTR]]: note: pointer points here + // CHECK-MEMFUN-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-MEMFUN-NEXT: {{^ \^}} + return s->f() && 0; + + case 'n': + // CHECK-NEW: misaligned.cpp:[[@LINE+4]]:5: runtime error: constructor call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-NEW-NEXT: [[PTR]]: note: pointer points here + // CHECK-NEW-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-NEW-NEXT: {{^ \^}} + return (new (s) S)->k && 0; + + case 'u': { + // CHECK-UPCAST: misaligned.cpp:[[@LINE+4]]:17: runtime error: upcast of misaligned address [[PTR:0x[0-9a-f]*]] for type 'T', which requires 4 byte alignment + // CHECK-UPCAST-NEXT: [[PTR]]: note: pointer points here + // CHECK-UPCAST-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-UPCAST-NEXT: {{^ \^}} + S *s2 = (S*)t; + return s2->f(); + } + + case 'w': + // CHECK-WILD: misaligned.cpp:[[@LINE+3]]:35: runtime error: member access within misaligned address 0x000000000123 for type 'S', which requires 4 byte alignment + // CHECK-WILD-NEXT: 0x000000000123: note: pointer points here + // CHECK-WILD-NEXT: <memory cannot be printed> + return static_cast<S*>(wild)->k; + } +} diff --git a/test/ubsan/TestCases/TypeCheck/null.cpp b/test/ubsan/TestCases/TypeCheck/null.cpp new file mode 100644 index 000000000000..2a90f7fb956b --- /dev/null +++ b/test/ubsan/TestCases/TypeCheck/null.cpp @@ -0,0 +1,38 @@ +// RUN: %clangxx -fsanitize=null %s -O3 -o %t +// RUN: %run %t l 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD +// RUN: not --crash %run %t s 2>&1 | FileCheck %s --check-prefix=CHECK-STORE +// RUN: %run %t r 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE +// RUN: %run %t m 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER +// RUN: %run %t f 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN + +struct S { + int f() { return 0; } + int k; +}; + +int main(int, char **argv) { + int *p = 0; + S *s = 0; + + (void)*p; // ok! + + switch (argv[1][0]) { + case 'l': + // CHECK-LOAD: null.cpp:22:12: runtime error: load of null pointer of type 'int' + return *p; + case 's': + // CHECK-STORE: null.cpp:25:5: runtime error: store to null pointer of type 'int' + *p = 1; + break; + case 'r': + // CHECK-REFERENCE: null.cpp:29:15: runtime error: reference binding to null pointer of type 'int' + {int &r = *p;} + break; + case 'm': + // CHECK-MEMBER: null.cpp:33:15: runtime error: member access within null pointer of type 'S' + return s->k; + case 'f': + // CHECK-MEMFUN: null.cpp:36:12: runtime error: member call on null pointer of type 'S' + return s->f(); + } +} diff --git a/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp new file mode 100644 index 000000000000..5261e71c2d3d --- /dev/null +++ b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp @@ -0,0 +1,19 @@ +// RUN: %clangxx -fsanitize=vptr -fno-sanitize-recover -g %s -O3 -o %t +// RUN: not %run %t 2>&1 | FileCheck %s + +// FIXME: This test produces linker errors on Darwin. +// XFAIL: darwin + +struct S { virtual int f() { return 0; } }; +struct T : virtual S {}; + +struct Foo { virtual int f() { return 0; } }; + +int main(int argc, char **argv) { + Foo foo; + T *t = (T*)&foo; + S *s = t; + // CHECK: vptr-virtual-base.cpp:[[@LINE-1]]:10: runtime error: cast to virtual base of address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-NEXT: [[PTR]]: note: object is of type 'Foo' + return s->f(); +} diff --git a/test/ubsan/TestCases/TypeCheck/vptr.cpp b/test/ubsan/TestCases/TypeCheck/vptr.cpp new file mode 100644 index 000000000000..3a6b1553f9b3 --- /dev/null +++ b/test/ubsan/TestCases/TypeCheck/vptr.cpp @@ -0,0 +1,166 @@ +// RUN: %clangxx -fsanitize=vptr -g %s -O3 -o %t +// RUN: %run %t rT && %run %t mT && %run %t fT && %run %t cT +// RUN: %run %t rU && %run %t mU && %run %t fU && %run %t cU +// RUN: %run %t rS && %run %t rV && %run %t oV +// RUN: %run %t mS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace +// RUN: %run %t fS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace +// RUN: %run %t cS 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --strict-whitespace +// RUN: %run %t mV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace +// RUN: %run %t fV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace +// RUN: %run %t cV 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --strict-whitespace +// RUN: %run %t oU 2>&1 | FileCheck %s --check-prefix=CHECK-OFFSET --strict-whitespace +// RUN: %run %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-MEMBER --strict-whitespace + +// RUN: (echo "vptr_check:S"; echo "vptr_check:T"; echo "vptr_check:U") > %t.supp +// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t mS 2>&1 +// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t fS 2>&1 +// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t cS 2>&1 +// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t mV 2>&1 +// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t fV 2>&1 +// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t cV 2>&1 +// RUN: ASAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.supp:halt_on_error=1 %run %t oU 2>&1 + +// RUN: echo "vptr_check:S" > %t.loc-supp +// RUN: ASAN_OPTIONS=suppressions=%t.loc-supp:halt_on_error=1 UBSAN_OPTIONS=suppressions=%t.loc-supp:halt_on_error=1 not %run %t x- 2>&1 | FileCheck %s --check-prefix=CHECK-LOC-SUPPRESS + +// FIXME: This test produces linker errors on Darwin. +// XFAIL: darwin +// REQUIRES: stable-runtime + +extern "C" { +const char *__ubsan_default_options() { + return "print_stacktrace=1"; +} +} + +struct S { + S() : a(0) {} + ~S() {} + int a; + int f() { return 0; } + virtual int v() { return 0; } +}; + +struct T : S { + T() : b(0) {} + int b; + int g() { return 0; } + virtual int v() { return 1; } +}; + +struct X {}; +struct U : S, T, virtual X { virtual int v() { return 2; } }; + +struct V : S {}; + +// Make p global so that lsan does not complain. +T *p = 0; + +int access_p(T *p, char type); + +int main(int, char **argv) { + T t; + (void)t.a; + (void)t.b; + (void)t.f(); + (void)t.g(); + (void)t.v(); + (void)t.S::v(); + + U u; + (void)u.T::a; + (void)u.b; + (void)u.T::f(); + (void)u.g(); + (void)u.v(); + (void)u.T::v(); + (void)((T&)u).S::v(); + + char Buffer[sizeof(U)] = {}; + switch (argv[1][1]) { + case '0': + p = reinterpret_cast<T*>(Buffer); + break; + case 'S': + p = reinterpret_cast<T*>(new S); + break; + case 'T': + p = new T; + break; + case 'U': + p = new U; + break; + case 'V': + p = reinterpret_cast<T*>(new U); + break; + } + + access_p(p, argv[1][0]); + return 0; +} + +int access_p(T *p, char type) { + switch (type) { + case 'r': + // Binding a reference to storage of appropriate size and alignment is OK. + {T &r = *p;} + break; + + case 'x': + for (int i = 0; i < 2; i++) { + // Check that the first iteration ("S") succeeds, while the second ("V") fails. + p = reinterpret_cast<T*>((i == 0) ? new S : new V); + // CHECK-LOC-SUPPRESS: vptr.cpp:[[@LINE+5]]:7: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-LOC-SUPPRESS-NEXT: [[PTR]]: note: object is of type 'V' + // CHECK-LOC-SUPPRESS-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-LOC-SUPPRESS-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-LOC-SUPPRESS-NEXT: {{^ vptr for 'V'}} + p->g(); + } + return 0; + + case 'm': + // CHECK-MEMBER: vptr.cpp:[[@LINE+6]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-MEMBER-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']] + // CHECK-MEMBER-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-MEMBER-NEXT: {{^ vptr for}} [[DYN_TYPE]] + // CHECK-MEMBER-NEXT: #0 {{.*}} in access_p{{.*}}vptr.cpp:[[@LINE+1]] + return p->b; + + // CHECK-NULL-MEMBER: vptr.cpp:[[@LINE-2]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-NULL-MEMBER-NEXT: [[PTR]]: note: object has invalid vptr + // CHECK-NULL-MEMBER-NEXT: {{^ ?.. .. .. .. ?00 00 00 00 ?00 00 00 00 ?}} + // CHECK-NULL-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-NULL-MEMBER-NEXT: {{^ invalid vptr}} + // CHECK-NULL-MEMBER-NEXT: #0 {{.*}} in access_p{{.*}}vptr.cpp:[[@LINE-7]] + + case 'f': + // CHECK-MEMFUN: vptr.cpp:[[@LINE+6]]:12: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-MEMFUN-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']] + // CHECK-MEMFUN-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-MEMFUN-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-MEMFUN-NEXT: {{^ vptr for}} [[DYN_TYPE]] + // TODO: Add check for stacktrace here. + return p->g(); + + case 'o': + // CHECK-OFFSET: vptr.cpp:[[@LINE+6]]:12: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'U' + // CHECK-OFFSET-NEXT: 0x{{[0-9a-f]*}}: note: object is base class subobject at offset {{8|16}} within object of type [[DYN_TYPE:'U']] + // CHECK-OFFSET-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-OFFSET-NEXT: {{^ \^ ( ~~~~~~~~~~~~)?~~~~~~~~~~~ *$}} + // CHECK-OFFSET-NEXT: {{^ ( )?vptr for}} 'T' base class of [[DYN_TYPE]] + // CHECK-OFFSET-NEXT: #0 {{.*}} in access_p{{.*}}vptr.cpp:[[@LINE+1]] + return reinterpret_cast<U*>(p)->v() - 2; + + case 'c': + // CHECK-DOWNCAST: vptr.cpp:[[@LINE+6]]:5: runtime error: downcast of address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-DOWNCAST-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']] + // CHECK-DOWNCAST-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-DOWNCAST-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-DOWNCAST-NEXT: {{^ vptr for}} [[DYN_TYPE]] + // CHECK-DOWNCAST-NEXT: #0 {{.*}} in access_p{{.*}}vptr.cpp:[[@LINE+1]] + static_cast<T*>(reinterpret_cast<S*>(p)); + return 0; + } +} diff --git a/test/ubsan/lit.common.cfg b/test/ubsan/lit.common.cfg new file mode 100644 index 000000000000..d28733a61cf8 --- /dev/null +++ b/test/ubsan/lit.common.cfg @@ -0,0 +1,56 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if attr_value == None: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# Choose between standalone and UBSan+ASan modes. +ubsan_lit_test_mode = get_required_attr(config, 'ubsan_lit_test_mode') +if ubsan_lit_test_mode == "Standalone": + config.name = 'UndefinedBehaviorSanitizer-Standalone' + config.available_features.add("ubsan-standalone") + clang_ubsan_cflags = [] +elif ubsan_lit_test_mode == "AddressSanitizer": + if config.host_os == 'Darwin': + # ubsan-asan doesn't yet work on Darwin, + # see http://llvm.org/bugs/show_bug.cgi?id=21112. + config.unsupported = True + config.name = 'UndefinedBehaviorSanitizer-AddressSanitizer' + config.available_features.add("ubsan-asan") + clang_ubsan_cflags = ["-fsanitize=address"] + config.environment['ASAN_OPTIONS'] = 'detect_leaks=0' +else: + lit_config.fatal("Unknown UBSan test mode: %r" % ubsan_lit_test_mode) + +def build_invocation(compile_flags): + return " " + " ".join([config.clang] + compile_flags) + " " + +target_cflags = [get_required_attr(config, "target_cflags")] +clang_ubsan_cflags += target_cflags +clang_ubsan_cxxflags = config.cxx_mode_flags + clang_ubsan_cflags + +# Define %clang and %clangxx substitutions to use in test RUN lines. +config.substitutions.append( ("%clang ", build_invocation(clang_ubsan_cflags)) ) +config.substitutions.append( ("%clangxx ", build_invocation(clang_ubsan_cxxflags)) ) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# Check that the host supports UndefinedBehaviorSanitizer tests +if config.host_os not in ['Linux', 'Darwin', 'FreeBSD']: + config.unsupported = True + +# Allow tests to use REQUIRES=stable-runtime. For use when you cannot use XFAIL +# because the test hangs or fails on one configuration and not the other. +if config.target_arch.startswith('arm') == False: + config.available_features.add('stable-runtime') diff --git a/test/ubsan/lit.site.cfg.in b/test/ubsan/lit.site.cfg.in new file mode 100644 index 000000000000..ef72d2bbb42f --- /dev/null +++ b/test/ubsan/lit.site.cfg.in @@ -0,0 +1,8 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Tool-specific config options. +config.ubsan_lit_test_mode = "@UBSAN_LIT_TEST_MODE@" + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@UBSAN_LIT_TESTS_DIR@/lit.common.cfg") |