diff options
Diffstat (limited to 'lib/ubsan')
38 files changed, 2386 insertions, 0 deletions
diff --git a/lib/ubsan/CMakeLists.txt b/lib/ubsan/CMakeLists.txt new file mode 100644 index 000000000000..40d0e897179d --- /dev/null +++ b/lib/ubsan/CMakeLists.txt @@ -0,0 +1,49 @@ +# Build for the undefined behavior sanitizer runtime support library. + +set(UBSAN_SOURCES + ubsan_diag.cc + ubsan_handlers.cc + ubsan_handlers_cxx.cc + ubsan_type_hash.cc + ubsan_value.cc + ) + +include_directories(..) + +set(UBSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS}) + +filter_available_targets(UBSAN_SUPPORTED_ARCH + x86_64 i386) + +set(UBSAN_RUNTIME_LIBRARIES) + +if(APPLE) + # Build universal binary on APPLE. + add_library(clang_rt.ubsan_osx STATIC + ${UBSAN_SOURCES} + $<TARGET_OBJECTS:RTSanitizerCommon.osx> + ) + set_target_compile_flags(clang_rt.ubsan_osx ${UBSAN_CFLAGS}) + set_target_properties(clang_rt.ubsan_osx PROPERTIES + OSX_ARCHITECTURES "${UBSAN_SUPPORTED_ARCH}") + list(APPEND UBSAN_RUNTIME_LIBRARIES clang_rt.ubsan_osx) +else() + # Build separate libraries for each target. + foreach(arch ${UBSAN_SUPPORTED_ARCH}) + add_library(clang_rt.ubsan-${arch} STATIC + ${UBSAN_SOURCES} + $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + ) + set_target_compile_flags(clang_rt.ubsan-${arch} + ${UBSAN_CFLAGS} ${TARGET_${arch}_CFLAGS} + ) + list(APPEND UBSAN_RUNTIME_LIBRARIES clang_rt.ubsan-${arch}) + endforeach() +endif() + + +set_property(TARGET ${UBSAN_RUNTIME_LIBRARIES} APPEND PROPERTY + COMPILE_DEFINITIONS ${UBSAN_COMMON_DEFINITIONS}) +add_clang_compiler_rt_libraries(${UBSAN_RUNTIME_LIBRARIES}) + +add_subdirectory(lit_tests) diff --git a/lib/ubsan/Makefile.mk b/lib/ubsan/Makefile.mk new file mode 100644 index 000000000000..5702e0e752d8 --- /dev/null +++ b/lib/ubsan/Makefile.mk @@ -0,0 +1,23 @@ +#===- lib/ubsan/Makefile.mk ---------------------------------*- Makefile -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +ModuleName := ubsan +SubDirs := + +Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) +ObjNames := $(Sources:%.cc=%.o) + +Implementation := Generic + +# FIXME: use automatic dependencies? +Dependencies := $(wildcard $(Dir)/*.h) +Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h) + +# Define a convenience variable for all the ubsan functions. +UbsanFunctions := $(Sources:%.cc=%) diff --git a/lib/ubsan/lit_tests/CMakeLists.txt b/lib/ubsan/lit_tests/CMakeLists.txt new file mode 100644 index 000000000000..565c523ceb49 --- /dev/null +++ b/lib/ubsan/lit_tests/CMakeLists.txt @@ -0,0 +1,22 @@ +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + ) + +if(COMPILER_RT_CAN_EXECUTE_TESTS) + # Run UBSan output tests only if we're sure that clang would produce + # working binaries. + set(UBSAN_TEST_DEPS + clang clang-headers FileCheck count not + ${UBSAN_RUNTIME_LIBRARIES} + ) + set(UBSAN_TEST_PARAMS + ubsan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + ) + add_lit_testsuite(check-ubsan "Running UndefinedBehaviorSanitizer tests" + ${CMAKE_CURRENT_BINARY_DIR} + PARAMS ${UBSAN_TEST_PARAMS} + DEPENDS ${UBSAN_TEST_DEPS} + ) + set_target_properties(check-ubsan PROPERTIES FOLDER "UBSan unittests") +endif() diff --git a/lib/ubsan/lit_tests/Float/cast-overflow.cpp b/lib/ubsan/lit_tests/Float/cast-overflow.cpp new file mode 100644 index 000000000000..63410dc87140 --- /dev/null +++ b/lib/ubsan/lit_tests/Float/cast-overflow.cpp @@ -0,0 +1,98 @@ +// RUN: %clang -fsanitize=float-cast-overflow %s -o %t +// RUN: %t _ +// RUN: %t 0 2>&1 | FileCheck %s --check-prefix=CHECK-0 +// RUN: %t 1 2>&1 | FileCheck %s --check-prefix=CHECK-1 +// RUN: %t 2 2>&1 | FileCheck %s --check-prefix=CHECK-2 +// RUN: %t 3 2>&1 | FileCheck %s --check-prefix=CHECK-3 +// RUN: %t 4 2>&1 | FileCheck %s --check-prefix=CHECK-4 +// RUN: %t 5 2>&1 | FileCheck %s --check-prefix=CHECK-5 +// RUN: %t 6 2>&1 | FileCheck %s --check-prefix=CHECK-6 +// FIXME: %t 7 2>&1 | FileCheck %s --check-prefix=CHECK-7 +// RUN: %t 8 2>&1 | FileCheck %s --check-prefix=CHECK-8 +// RUN: %t 9 2>&1 | FileCheck %s --check-prefix=CHECK-9 + +// This test assumes float and double are IEEE-754 single- and double-precision. + +#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 + + // Build a '+Inf'. + char InfVal[] = { 0x00, 0x00, 0x80, 0x7f }; + float Inf; + memcpy(&Inf, InfVal, 4); + + // Build a 'NaN'. + char NaNVal[] = { 0x01, 0x00, 0x80, 0x7f }; + float NaN; + memcpy(&NaN, NaNVal, 4); + + 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 -0.001 is outside the range of representable values of type 'unsigned int' + return (unsigned)-0.001; + 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': + // CHECK-9: runtime error: value {{.*}} is outside the range of representable values of type 'double' + return (double)Inf; + } +} diff --git a/lib/ubsan/lit_tests/Integer/add-overflow.cpp b/lib/ubsan/lit_tests/Integer/add-overflow.cpp new file mode 100644 index 000000000000..80543524f51d --- /dev/null +++ b/lib/ubsan/lit_tests/Integer/add-overflow.cpp @@ -0,0 +1,32 @@ +// RUN: %clang -DADD_I32 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I32 +// RUN: %clang -DADD_I64 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I64 +// RUN: %clang -DADD_I128 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=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/lib/ubsan/lit_tests/Integer/div-overflow.cpp b/lib/ubsan/lit_tests/Integer/div-overflow.cpp new file mode 100644 index 000000000000..dd82427f9d5b --- /dev/null +++ b/lib/ubsan/lit_tests/Integer/div-overflow.cpp @@ -0,0 +1,10 @@ +// RUN: %clang -fsanitize=signed-integer-overflow %s -o %t && %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/lib/ubsan/lit_tests/Integer/div-zero.cpp b/lib/ubsan/lit_tests/Integer/div-zero.cpp new file mode 100644 index 000000000000..b2a839566c5f --- /dev/null +++ b/lib/ubsan/lit_tests/Integer/div-zero.cpp @@ -0,0 +1,15 @@ +// RUN: %clang -fsanitize=integer-divide-by-zero -DDIVIDEND=0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang -fsanitize=integer-divide-by-zero -DDIVIDEND=1U %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang -fsanitize=float-divide-by-zero -DDIVIDEND=1.5 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang -fsanitize=integer-divide-by-zero -DDIVIDEND='intmax(123)' %s -o %t && %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/lib/ubsan/lit_tests/Integer/incdec-overflow.cpp b/lib/ubsan/lit_tests/Integer/incdec-overflow.cpp new file mode 100644 index 000000000000..48b68b6365c6 --- /dev/null +++ b/lib/ubsan/lit_tests/Integer/incdec-overflow.cpp @@ -0,0 +1,16 @@ +// RUN: %clang -DOP=n++ -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang -DOP=++n -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang -DOP=m-- -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang -DOP=--m -fsanitize=signed-integer-overflow %s -o %t && %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/lib/ubsan/lit_tests/Integer/mul-overflow.cpp b/lib/ubsan/lit_tests/Integer/mul-overflow.cpp new file mode 100644 index 000000000000..8d1e70d6ad48 --- /dev/null +++ b/lib/ubsan/lit_tests/Integer/mul-overflow.cpp @@ -0,0 +1,14 @@ +// RUN: %clang -fsanitize=signed-integer-overflow %s -o %t && %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/lib/ubsan/lit_tests/Integer/negate-overflow.cpp b/lib/ubsan/lit_tests/Integer/negate-overflow.cpp new file mode 100644 index 000000000000..2ee4f10115e8 --- /dev/null +++ b/lib/ubsan/lit_tests/Integer/negate-overflow.cpp @@ -0,0 +1,12 @@ +// RUN: %clang -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECKS +// RUN: %clang -fsanitize=unsigned-integer-overflow %s -o %t && %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/lib/ubsan/lit_tests/Integer/no-recover.cpp b/lib/ubsan/lit_tests/Integer/no-recover.cpp new file mode 100644 index 000000000000..e200feaa79ae --- /dev/null +++ b/lib/ubsan/lit_tests/Integer/no-recover.cpp @@ -0,0 +1,22 @@ +// RUN: %clang -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=RECOVER +// RUN: %clang -fsanitize=unsigned-integer-overflow -fsanitize-recover %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=RECOVER +// RUN: %clang -fsanitize=unsigned-integer-overflow -fno-sanitize-recover %s -o %t && %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' + // ABORT-NOT: runtime error +} diff --git a/lib/ubsan/lit_tests/Integer/shift.cpp b/lib/ubsan/lit_tests/Integer/shift.cpp new file mode 100644 index 000000000000..19101c53e75e --- /dev/null +++ b/lib/ubsan/lit_tests/Integer/shift.cpp @@ -0,0 +1,37 @@ +// RUN: %clang -DLSH_OVERFLOW -DOP='<<' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=LSH_OVERFLOW +// RUN: %clang -DLSH_OVERFLOW -DOP='<<=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=LSH_OVERFLOW +// RUN: %clang -DTOO_LOW -DOP='<<' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_LOW +// RUN: %clang -DTOO_LOW -DOP='>>' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_LOW +// RUN: %clang -DTOO_LOW -DOP='<<=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_LOW +// RUN: %clang -DTOO_LOW -DOP='>>=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_LOW +// RUN: %clang -DTOO_HIGH -DOP='<<' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_HIGH +// RUN: %clang -DTOO_HIGH -DOP='>>' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_HIGH +// RUN: %clang -DTOO_HIGH -DOP='<<=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_HIGH +// RUN: %clang -DTOO_HIGH -DOP='>>=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=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/lib/ubsan/lit_tests/Integer/sub-overflow.cpp b/lib/ubsan/lit_tests/Integer/sub-overflow.cpp new file mode 100644 index 000000000000..b43a69bee4e6 --- /dev/null +++ b/lib/ubsan/lit_tests/Integer/sub-overflow.cpp @@ -0,0 +1,31 @@ +// RUN: %clang -DSUB_I32 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I32 +// RUN: %clang -DSUB_I64 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I64 +// RUN: %clang -DSUB_I128 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=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/lib/ubsan/lit_tests/Integer/uadd-overflow.cpp b/lib/ubsan/lit_tests/Integer/uadd-overflow.cpp new file mode 100644 index 000000000000..0edb10092e2c --- /dev/null +++ b/lib/ubsan/lit_tests/Integer/uadd-overflow.cpp @@ -0,0 +1,32 @@ +// RUN: %clang -DADD_I32 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I32 +// RUN: %clang -DADD_I64 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I64 +// RUN: %clang -DADD_I128 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=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' +#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/lib/ubsan/lit_tests/Integer/uincdec-overflow.cpp b/lib/ubsan/lit_tests/Integer/uincdec-overflow.cpp new file mode 100644 index 000000000000..6b677ca5bd35 --- /dev/null +++ b/lib/ubsan/lit_tests/Integer/uincdec-overflow.cpp @@ -0,0 +1,16 @@ +// RUN: %clang -DOP=n++ -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=INC %s +// RUN: %clang -DOP=++n -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=INC %s +// RUN: %clang -DOP=m-- -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=DEC %s +// RUN: %clang -DOP=--m -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=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/lib/ubsan/lit_tests/Integer/umul-overflow.cpp b/lib/ubsan/lit_tests/Integer/umul-overflow.cpp new file mode 100644 index 000000000000..42cf3a780ed0 --- /dev/null +++ b/lib/ubsan/lit_tests/Integer/umul-overflow.cpp @@ -0,0 +1,19 @@ +// RUN: %clang -fsanitize=unsigned-integer-overflow %s -o %t && %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/lib/ubsan/lit_tests/Integer/usub-overflow.cpp b/lib/ubsan/lit_tests/Integer/usub-overflow.cpp new file mode 100644 index 000000000000..357d662ad63e --- /dev/null +++ b/lib/ubsan/lit_tests/Integer/usub-overflow.cpp @@ -0,0 +1,31 @@ +// RUN: %clang -DSUB_I32 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I32 +// RUN: %clang -DSUB_I64 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I64 +// RUN: %clang -DSUB_I128 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=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' +#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/lib/ubsan/lit_tests/Misc/bool.cpp b/lib/ubsan/lit_tests/Misc/bool.cpp new file mode 100644 index 000000000000..8fafe7eac053 --- /dev/null +++ b/lib/ubsan/lit_tests/Misc/bool.cpp @@ -0,0 +1,11 @@ +// RUN: %clang -fsanitize=bool %s -O3 -o %T/bool.exe && %T/bool.exe 2>&1 | FileCheck %s + +unsigned char NotABool = 123; + +int main(int argc, char **argv) { + bool *p = (bool*)&NotABool; + + // FIXME: Provide a better source location here. + // CHECK: bool.exe:0x{{[0-9a-f]*}}: runtime error: load of value 123, which is not a valid value for type 'bool' + return *p; +} diff --git a/lib/ubsan/lit_tests/Misc/deduplication.cpp b/lib/ubsan/lit_tests/Misc/deduplication.cpp new file mode 100644 index 000000000000..d9c909f9af4f --- /dev/null +++ b/lib/ubsan/lit_tests/Misc/deduplication.cpp @@ -0,0 +1,25 @@ +// RUN: %clang -fsanitize=undefined %s -o %t && %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/lib/ubsan/lit_tests/Misc/enum.cpp b/lib/ubsan/lit_tests/Misc/enum.cpp new file mode 100644 index 000000000000..b363fea3487f --- /dev/null +++ b/lib/ubsan/lit_tests/Misc/enum.cpp @@ -0,0 +1,17 @@ +// RUN: %clang -fsanitize=enum %s -O3 -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-PLAIN +// RUN: %clang -fsanitize=enum -std=c++11 -DE="class E" %s -O3 -o %t && %t +// RUN: %clang -fsanitize=enum -std=c++11 -DE="class E : bool" %s -O3 -o %t && %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/lib/ubsan/lit_tests/Misc/missing_return.cpp b/lib/ubsan/lit_tests/Misc/missing_return.cpp new file mode 100644 index 000000000000..9997b8386f21 --- /dev/null +++ b/lib/ubsan/lit_tests/Misc/missing_return.cpp @@ -0,0 +1,9 @@ +// RUN: %clang -fsanitize=return %s -O3 -o %t && %t 2>&1 | FileCheck %s + +// CHECK: missing_return.cpp:4:5: runtime error: execution reached the end of a value-returning function without returning a value +int f() { +} + +int main(int, char **argv) { + return f(); +} diff --git a/lib/ubsan/lit_tests/Misc/unreachable.cpp b/lib/ubsan/lit_tests/Misc/unreachable.cpp new file mode 100644 index 000000000000..5ca4e5fd8b0c --- /dev/null +++ b/lib/ubsan/lit_tests/Misc/unreachable.cpp @@ -0,0 +1,6 @@ +// RUN: %clang -fsanitize=unreachable %s -O3 -o %t && %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/lib/ubsan/lit_tests/Misc/vla.c b/lib/ubsan/lit_tests/Misc/vla.c new file mode 100644 index 000000000000..2fa88addc0d3 --- /dev/null +++ b/lib/ubsan/lit_tests/Misc/vla.c @@ -0,0 +1,11 @@ +// RUN: %clang -fsanitize=vla-bound %s -O3 -o %t +// RUN: %t 2>&1 | FileCheck %s --check-prefix=CHECK-MINUS-ONE +// RUN: %t a 2>&1 | FileCheck %s --check-prefix=CHECK-ZERO +// 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/lib/ubsan/lit_tests/TypeCheck/misaligned.cpp b/lib/ubsan/lit_tests/TypeCheck/misaligned.cpp new file mode 100644 index 000000000000..3abacae8be89 --- /dev/null +++ b/lib/ubsan/lit_tests/TypeCheck/misaligned.cpp @@ -0,0 +1,73 @@ +// RUN: %clang -fsanitize=alignment %s -O3 -o %t +// RUN: %t l0 && %t s0 && %t r0 && %t m0 && %t f0 && %t n0 +// RUN: %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --strict-whitespace +// RUN: %t s1 2>&1 | FileCheck %s --check-prefix=CHECK-STORE +// RUN: %t r1 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE +// RUN: %t m1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER +// RUN: %t f1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN +// RUN: %t n1 2>&1 | FileCheck %s --check-prefix=CHECK-NEW + +#include <new> + +struct S { + S() {} + int f() { return 0; } + int k; +}; + +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; + + (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; + + 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': + // FIXME: Provide a better source location here. + // CHECK-NEW: misaligned{{.*}}:0x{{[0-9a-f]*}}: 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; + } +} diff --git a/lib/ubsan/lit_tests/TypeCheck/null.cpp b/lib/ubsan/lit_tests/TypeCheck/null.cpp new file mode 100644 index 000000000000..f72af28ce160 --- /dev/null +++ b/lib/ubsan/lit_tests/TypeCheck/null.cpp @@ -0,0 +1,38 @@ +// RUN: %clang -fsanitize=null %s -O3 -o %t +// RUN: %t l 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD +// RUN: %t s 2>&1 | FileCheck %s --check-prefix=CHECK-STORE +// RUN: %t r 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE +// RUN: %t m 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER +// 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/lib/ubsan/lit_tests/TypeCheck/vptr.cpp b/lib/ubsan/lit_tests/TypeCheck/vptr.cpp new file mode 100644 index 000000000000..574a7bef9622 --- /dev/null +++ b/lib/ubsan/lit_tests/TypeCheck/vptr.cpp @@ -0,0 +1,106 @@ +// RUN: %clang -ccc-cxx -fsanitize=vptr %s -O3 -o %t +// RUN: %t rT && %t mT && %t fT +// RUN: %t rU && %t mU && %t fU +// RUN: %t rS && %t rV && %t oV +// RUN: %t mS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace +// RUN: %t fS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace +// RUN: %t mV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace +// RUN: %t fV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace +// RUN: %t oU 2>&1 | FileCheck %s --check-prefix=CHECK-OFFSET --strict-whitespace +// RUN: %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-MEMBER --strict-whitespace + +// FIXME: This test produces linker errors on Darwin. +// XFAIL: darwin + +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 U : S, T { virtual int v() { return 2; } }; + +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(); + + T *p = 0; + 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; + } + + switch (argv[1][0]) { + case 'r': + // Binding a reference to storage of appropriate size and alignment is OK. + {T &r = *p;} + break; + + case 'm': + // CHECK-MEMBER: vptr.cpp:[[@LINE+5]]: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]] + 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}} + + case 'f': + // CHECK-MEMFUN: vptr.cpp:[[@LINE+5]]: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]] + return p->g(); + + case 'o': + // CHECK-OFFSET: vptr.cpp:[[@LINE+5]]: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]] + return reinterpret_cast<U*>(p)->v() - 2; + } +} diff --git a/lib/ubsan/lit_tests/lit.cfg b/lib/ubsan/lit_tests/lit.cfg new file mode 100644 index 000000000000..9fd3a1aeaa16 --- /dev/null +++ b/lib/ubsan/lit_tests/lit.cfg @@ -0,0 +1,65 @@ +# -*- Python -*- + +import os + +# Setup config name. +config.name = 'UndefinedBehaviorSanitizer' + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +def DisplayNoConfigMessage(): + lit.fatal("No site specific configuration available! " + + "Try running your test from the build tree or running " + + "make check-ubsan") + +# Figure out LLVM source root. +llvm_src_root = getattr(config, 'llvm_src_root', None) +if llvm_src_root is None: + # We probably haven't loaded the site-specific configuration: the user + # is likely trying to run a test file directly, and the site configuration + # wasn't created by the build system or we're performing an out-of-tree build. + ubsan_site_cfg = lit.params.get('ubsan_site_config', None) + if ubsan_site_cfg and os.path.exists(ubsan_site_cfg): + lit.load_config(config, ubsan_site_cfg) + raise SystemExit + + # Try to guess the location of site-specific configuration using llvm-config + # util that can point where the build tree is. + llvm_config = lit.util.which("llvm-config", config.environment["PATH"]) + if not llvm_config: + DisplayNoConfigMessage() + + # Validate that llvm-config points to the same source tree. + llvm_src_root = lit.util.capture(["llvm-config", "--src-root"]).strip() + ubsan_test_src_root = os.path.join(llvm_src_root, "projects", "compiler-rt", + "lib", "ubsan", "lit_tests") + if (os.path.realpath(ubsan_test_src_root) != + os.path.realpath(config.test_source_root)): + DisplayNoConfigMessage() + + # Find out the presumed location of generated site config. + llvm_obj_root = lit.util.capture(["llvm-config", "--obj-root"]).strip() + ubsan_site_cfg = os.path.join(llvm_obj_root, "projects", "compiler-rt", + "lib", "ubsan", "lit_tests", "lit.site.cfg") + if not ubsan_site_cfg or not os.path.exists(ubsan_site_cfg): + DisplayNoConfigMessage() + + lit.load_config(config, ubsan_site_cfg) + raise SystemExit + +# Setup attributes common for all compiler-rt projects. +compiler_rt_lit_cfg = os.path.join(llvm_src_root, "projects", "compiler-rt", + "lib", "lit.common.cfg") +if not compiler_rt_lit_cfg or not os.path.exists(compiler_rt_lit_cfg): + lit.fatal("Can't find common compiler-rt lit config at: %r" + % compiler_rt_lit_cfg) +lit.load_config(config, compiler_rt_lit_cfg) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# UndefinedBehaviorSanitizer tests are currently supported on +# Linux and Darwin only. +if config.host_os not in ['Linux', 'Darwin']: + config.unsupported = True diff --git a/lib/ubsan/lit_tests/lit.site.cfg.in b/lib/ubsan/lit_tests/lit.site.cfg.in new file mode 100644 index 000000000000..b1c6ccf544ea --- /dev/null +++ b/lib/ubsan/lit_tests/lit.site.cfg.in @@ -0,0 +1,19 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +config.clang = "@LLVM_BINARY_DIR@/bin/clang" +config.host_os = "@HOST_OS@" +config.llvm_src_root = "@LLVM_SOURCE_DIR@" +config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" +config.target_triple = "@TARGET_TRIPLE@" + +# LLVM tools dir can be passed in lit parameters, so try to +# apply substitution. +try: + config.llvm_tools_dir = config.llvm_tools_dir % lit.params +except KeyError,e: + key, = e.args + lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) + +# Let the main config do the real work. +lit.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") diff --git a/lib/ubsan/ubsan_diag.cc b/lib/ubsan/ubsan_diag.cc new file mode 100644 index 000000000000..57c98e669e90 --- /dev/null +++ b/lib/ubsan/ubsan_diag.cc @@ -0,0 +1,271 @@ +//===-- ubsan_diag.cc -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Diagnostic reporting for the UBSan runtime. +// +//===----------------------------------------------------------------------===// + +#include "ubsan_diag.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_symbolizer.h" +#include <stdio.h> + +using namespace __ubsan; + +Location __ubsan::getCallerLocation(uptr CallerLoc) { + if (!CallerLoc) + return Location(); + + uptr Loc = StackTrace::GetPreviousInstructionPc(CallerLoc); + + AddressInfo Info; + if (!SymbolizeCode(Loc, &Info, 1) || !Info.module || !*Info.module) + return Location(Loc); + + if (!Info.function) + return ModuleLocation(Info.module, Info.module_offset); + + return SourceLocation(Info.file, Info.line, Info.column); +} + +Diag &Diag::operator<<(const TypeDescriptor &V) { + return AddArg(V.getTypeName()); +} + +Diag &Diag::operator<<(const Value &V) { + if (V.getType().isSignedIntegerTy()) + AddArg(V.getSIntValue()); + else if (V.getType().isUnsignedIntegerTy()) + AddArg(V.getUIntValue()); + else if (V.getType().isFloatTy()) + AddArg(V.getFloatValue()); + else + AddArg("<unknown>"); + return *this; +} + +/// Hexadecimal printing for numbers too large for Printf to handle directly. +static void PrintHex(UIntMax Val) { +#if HAVE_INT128_T + Printf("0x%08x%08x%08x%08x", + (unsigned int)(Val >> 96), + (unsigned int)(Val >> 64), + (unsigned int)(Val >> 32), + (unsigned int)(Val)); +#else + UNREACHABLE("long long smaller than 64 bits?"); +#endif +} + +static void renderLocation(Location Loc) { + switch (Loc.getKind()) { + case Location::LK_Source: { + SourceLocation SLoc = Loc.getSourceLocation(); + if (SLoc.isInvalid()) + RawWrite("<unknown>:"); + else { + Printf("%s:%d:", SLoc.getFilename(), SLoc.getLine()); + if (SLoc.getColumn()) + Printf("%d:", SLoc.getColumn()); + } + break; + } + case Location::LK_Module: + Printf("%s:0x%zx:", Loc.getModuleLocation().getModuleName(), + Loc.getModuleLocation().getOffset()); + break; + case Location::LK_Memory: + Printf("%p:", Loc.getMemoryLocation()); + break; + case Location::LK_Null: + RawWrite("<unknown>:"); + break; + } +} + +static void renderText(const char *Message, const Diag::Arg *Args) { + for (const char *Msg = Message; *Msg; ++Msg) { + if (*Msg != '%') { + char Buffer[64]; + unsigned I; + for (I = 0; Msg[I] && Msg[I] != '%' && I != 63; ++I) + Buffer[I] = Msg[I]; + Buffer[I] = '\0'; + RawWrite(Buffer); + Msg += I - 1; + } else { + const Diag::Arg &A = Args[*++Msg - '0']; + switch (A.Kind) { + case Diag::AK_String: + Printf("%s", A.String); + break; + case Diag::AK_Mangled: { + RawWrite("'"); + RawWrite(Demangle(A.String)); + RawWrite("'"); + break; + } + case Diag::AK_SInt: + // 'long long' is guaranteed to be at least 64 bits wide. + if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX) + Printf("%lld", (long long)A.SInt); + else + PrintHex(A.SInt); + break; + case Diag::AK_UInt: + if (A.UInt <= UINT64_MAX) + Printf("%llu", (unsigned long long)A.UInt); + else + PrintHex(A.UInt); + break; + case Diag::AK_Float: { + // FIXME: Support floating-point formatting in sanitizer_common's + // printf, and stop using snprintf here. + char Buffer[32]; + snprintf(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float); + Printf("%s", Buffer); + break; + } + case Diag::AK_Pointer: + Printf("%p", A.Pointer); + break; + } + } + } +} + +/// Find the earliest-starting range in Ranges which ends after Loc. +static Range *upperBound(MemoryLocation Loc, Range *Ranges, + unsigned NumRanges) { + Range *Best = 0; + for (unsigned I = 0; I != NumRanges; ++I) + if (Ranges[I].getEnd().getMemoryLocation() > Loc && + (!Best || + Best->getStart().getMemoryLocation() > + Ranges[I].getStart().getMemoryLocation())) + Best = &Ranges[I]; + return Best; +} + +/// Render a snippet of the address space near a location. +static void renderMemorySnippet(MemoryLocation Loc, + Range *Ranges, unsigned NumRanges, + const Diag::Arg *Args) { + const unsigned BytesToShow = 32; + const unsigned MinBytesNearLoc = 4; + + // Show at least the 8 bytes surrounding Loc. + MemoryLocation Min = Loc - MinBytesNearLoc, Max = Loc + MinBytesNearLoc; + for (unsigned I = 0; I < NumRanges; ++I) { + Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min); + Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max); + } + + // If we have too many interesting bytes, prefer to show bytes after Loc. + if (Max - Min > BytesToShow) + Min = __sanitizer::Min(Max - BytesToShow, Loc - MinBytesNearLoc); + Max = Min + BytesToShow; + + // Emit data. + for (uptr P = Min; P != Max; ++P) { + // FIXME: Check that the address is readable before printing it. + unsigned char C = *reinterpret_cast<const unsigned char*>(P); + Printf("%s%02x", (P % 8 == 0) ? " " : " ", C); + } + RawWrite("\n"); + + // Emit highlights. + Range *InRange = upperBound(Min, Ranges, NumRanges); + for (uptr P = Min; P != Max; ++P) { + char Pad = ' ', Byte = ' '; + if (InRange && InRange->getEnd().getMemoryLocation() == P) + InRange = upperBound(P, Ranges, NumRanges); + if (!InRange && P > Loc) + break; + if (InRange && InRange->getStart().getMemoryLocation() < P) + Pad = '~'; + if (InRange && InRange->getStart().getMemoryLocation() <= P) + Byte = '~'; + char Buffer[] = { Pad, Pad, P == Loc ? '^' : Byte, Byte, 0 }; + RawWrite((P % 8 == 0) ? Buffer : &Buffer[1]); + } + RawWrite("\n"); + + // Go over the line again, and print names for the ranges. + InRange = 0; + unsigned Spaces = 0; + for (uptr P = Min; P != Max; ++P) { + if (!InRange || InRange->getEnd().getMemoryLocation() == P) + InRange = upperBound(P, Ranges, NumRanges); + if (!InRange) + break; + + Spaces += (P % 8) == 0 ? 2 : 1; + + if (InRange && InRange->getStart().getMemoryLocation() == P) { + while (Spaces--) + RawWrite(" "); + renderText(InRange->getText(), Args); + RawWrite("\n"); + // FIXME: We only support naming one range for now! + break; + } + + Spaces += 2; + } + + // FIXME: Print names for anything we can identify within the line: + // + // * If we can identify the memory itself as belonging to a particular + // global, stack variable, or dynamic allocation, then do so. + // + // * If we have a pointer-size, pointer-aligned range highlighted, + // determine whether the value of that range is a pointer to an + // entity which we can name, and if so, print that name. + // + // This needs an external symbolizer, or (preferably) ASan instrumentation. +} + +Diag::~Diag() { + bool UseAnsiColor = PrintsToTty(); + if (UseAnsiColor) + RawWrite("\033[1m"); + + renderLocation(Loc); + + switch (Level) { + case DL_Error: + if (UseAnsiColor) + RawWrite("\033[31m"); + RawWrite(" runtime error: "); + if (UseAnsiColor) + RawWrite("\033[0;1m"); + break; + + case DL_Note: + if (UseAnsiColor) + RawWrite("\033[30m"); + RawWrite(" note: "); + if (UseAnsiColor) + RawWrite("\033[0m"); + break; + } + + renderText(Message, Args); + + if (UseAnsiColor) + RawWrite("\033[0m"); + + RawWrite("\n"); + + if (Loc.isMemoryLocation()) + renderMemorySnippet(Loc.getMemoryLocation(), Ranges, NumRanges, Args); +} diff --git a/lib/ubsan/ubsan_diag.h b/lib/ubsan/ubsan_diag.h new file mode 100644 index 000000000000..16afffdb0a76 --- /dev/null +++ b/lib/ubsan/ubsan_diag.h @@ -0,0 +1,202 @@ +//===-- ubsan_diag.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Diagnostics emission for Clang's undefined behavior sanitizer. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_DIAG_H +#define UBSAN_DIAG_H + +#include "ubsan_value.h" + +namespace __ubsan { + +/// \brief A location within a loaded module in the program. These are used when +/// the location can't be resolved to a SourceLocation. +class ModuleLocation { + const char *ModuleName; + uptr Offset; + +public: + ModuleLocation() : ModuleName(0), Offset(0) {} + ModuleLocation(const char *ModuleName, uptr Offset) + : ModuleName(ModuleName), Offset(Offset) {} + const char *getModuleName() const { return ModuleName; } + uptr getOffset() const { return Offset; } +}; + +/// A location of some data within the program's address space. +typedef uptr MemoryLocation; + +/// \brief Location at which a diagnostic can be emitted. Either a +/// SourceLocation, a ModuleLocation, or a MemoryLocation. +class Location { +public: + enum LocationKind { LK_Null, LK_Source, LK_Module, LK_Memory }; + +private: + LocationKind Kind; + // FIXME: In C++11, wrap these in an anonymous union. + SourceLocation SourceLoc; + ModuleLocation ModuleLoc; + MemoryLocation MemoryLoc; + +public: + Location() : Kind(LK_Null) {} + Location(SourceLocation Loc) : + Kind(LK_Source), SourceLoc(Loc) {} + Location(ModuleLocation Loc) : + Kind(LK_Module), ModuleLoc(Loc) {} + Location(MemoryLocation Loc) : + Kind(LK_Memory), MemoryLoc(Loc) {} + + LocationKind getKind() const { return Kind; } + + bool isSourceLocation() const { return Kind == LK_Source; } + bool isModuleLocation() const { return Kind == LK_Module; } + bool isMemoryLocation() const { return Kind == LK_Memory; } + + SourceLocation getSourceLocation() const { + CHECK(isSourceLocation()); + return SourceLoc; + } + ModuleLocation getModuleLocation() const { + CHECK(isModuleLocation()); + return ModuleLoc; + } + MemoryLocation getMemoryLocation() const { + CHECK(isMemoryLocation()); + return MemoryLoc; + } +}; + +/// Try to obtain a location for the caller. This might fail, and produce either +/// an invalid location or a module location for the caller. +Location getCallerLocation(uptr CallerLoc = GET_CALLER_PC()); + +/// A diagnostic severity level. +enum DiagLevel { + DL_Error, ///< An error. + DL_Note ///< A note, attached to a prior diagnostic. +}; + +/// \brief Annotation for a range of locations in a diagnostic. +class Range { + Location Start, End; + const char *Text; + +public: + Range() : Start(), End(), Text() {} + Range(MemoryLocation Start, MemoryLocation End, const char *Text) + : Start(Start), End(End), Text(Text) {} + Location getStart() const { return Start; } + Location getEnd() const { return End; } + const char *getText() const { return Text; } +}; + +/// \brief A mangled C++ name. Really just a strong typedef for 'const char*'. +class MangledName { + const char *Name; +public: + MangledName(const char *Name) : Name(Name) {} + const char *getName() const { return Name; } +}; + +/// \brief Representation of an in-flight diagnostic. +/// +/// Temporary \c Diag instances are created by the handler routines to +/// accumulate arguments for a diagnostic. The destructor emits the diagnostic +/// message. +class Diag { + /// The location at which the problem occurred. + Location Loc; + + /// The diagnostic level. + DiagLevel Level; + + /// The message which will be emitted, with %0, %1, ... placeholders for + /// arguments. + const char *Message; + +public: + /// Kinds of arguments, corresponding to members of \c Arg's union. + enum ArgKind { + AK_String, ///< A string argument, displayed as-is. + AK_Mangled,///< A C++ mangled name, demangled before display. + AK_UInt, ///< An unsigned integer argument. + AK_SInt, ///< A signed integer argument. + AK_Float, ///< A floating-point argument. + AK_Pointer ///< A pointer argument, displayed in hexadecimal. + }; + + /// An individual diagnostic message argument. + struct Arg { + Arg() {} + Arg(const char *String) : Kind(AK_String), String(String) {} + Arg(MangledName MN) : Kind(AK_Mangled), String(MN.getName()) {} + Arg(UIntMax UInt) : Kind(AK_UInt), UInt(UInt) {} + Arg(SIntMax SInt) : Kind(AK_SInt), SInt(SInt) {} + Arg(FloatMax Float) : Kind(AK_Float), Float(Float) {} + Arg(const void *Pointer) : Kind(AK_Pointer), Pointer(Pointer) {} + + ArgKind Kind; + union { + const char *String; + UIntMax UInt; + SIntMax SInt; + FloatMax Float; + const void *Pointer; + }; + }; + +private: + static const unsigned MaxArgs = 5; + static const unsigned MaxRanges = 1; + + /// The arguments which have been added to this diagnostic so far. + Arg Args[MaxArgs]; + unsigned NumArgs; + + /// The ranges which have been added to this diagnostic so far. + Range Ranges[MaxRanges]; + unsigned NumRanges; + + Diag &AddArg(Arg A) { + CHECK(NumArgs != MaxArgs); + Args[NumArgs++] = A; + return *this; + } + + Diag &AddRange(Range A) { + CHECK(NumRanges != MaxRanges); + Ranges[NumRanges++] = A; + return *this; + } + + /// \c Diag objects are not copyable. + Diag(const Diag &); // NOT IMPLEMENTED + Diag &operator=(const Diag &); + +public: + Diag(Location Loc, DiagLevel Level, const char *Message) + : Loc(Loc), Level(Level), Message(Message), NumArgs(0), NumRanges(0) {} + ~Diag(); + + Diag &operator<<(const char *Str) { return AddArg(Str); } + Diag &operator<<(MangledName MN) { return AddArg(MN); } + Diag &operator<<(unsigned long long V) { return AddArg(UIntMax(V)); } + Diag &operator<<(const void *V) { return AddArg(V); } + Diag &operator<<(const TypeDescriptor &V); + Diag &operator<<(const Value &V); + Diag &operator<<(const Range &R) { return AddRange(R); } +}; + +} // namespace __ubsan + +#endif // UBSAN_DIAG_H diff --git a/lib/ubsan/ubsan_handlers.cc b/lib/ubsan/ubsan_handlers.cc new file mode 100644 index 000000000000..1b02aa0fadf3 --- /dev/null +++ b/lib/ubsan/ubsan_handlers.cc @@ -0,0 +1,244 @@ +//===-- ubsan_handlers.cc -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Error logging entry points for the UBSan runtime. +// +//===----------------------------------------------------------------------===// + +#include "ubsan_handlers.h" +#include "ubsan_diag.h" + +#include "sanitizer_common/sanitizer_common.h" + +using namespace __sanitizer; +using namespace __ubsan; + +namespace __ubsan { + const char *TypeCheckKinds[] = { + "load of", "store to", "reference binding to", "member access within", + "member call on", "constructor call on" + }; +} + +static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, + Location FallbackLoc) { + Location Loc = Data->Loc.acquire(); + + // Use the SourceLocation from Data to track deduplication, even if 'invalid' + if (Loc.getSourceLocation().isDisabled()) + return; + if (Data->Loc.isInvalid()) + Loc = FallbackLoc; + + if (!Pointer) + Diag(Loc, DL_Error, "%0 null pointer of type %1") + << TypeCheckKinds[Data->TypeCheckKind] << Data->Type; + else if (Data->Alignment && (Pointer & (Data->Alignment - 1))) + Diag(Loc, DL_Error, "%0 misaligned address %1 for type %3, " + "which requires %2 byte alignment") + << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer + << Data->Alignment << Data->Type; + else + Diag(Loc, DL_Error, "%0 address %1 with insufficient space " + "for an object of type %2") + << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type; + if (Pointer) + Diag(Pointer, DL_Note, "pointer points here"); +} +void __ubsan::__ubsan_handle_type_mismatch(TypeMismatchData *Data, + ValueHandle Pointer) { + handleTypeMismatchImpl(Data, Pointer, getCallerLocation()); +} +void __ubsan::__ubsan_handle_type_mismatch_abort(TypeMismatchData *Data, + ValueHandle Pointer) { + handleTypeMismatchImpl(Data, Pointer, getCallerLocation()); + Die(); +} + +/// \brief Common diagnostic emission for various forms of integer overflow. +template<typename T> static void HandleIntegerOverflow(OverflowData *Data, + ValueHandle LHS, + const char *Operator, + T RHS) { + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + Diag(Loc, DL_Error, "%0 integer overflow: " + "%1 %2 %3 cannot be represented in type %4") + << (Data->Type.isSignedIntegerTy() ? "signed" : "unsigned") + << Value(Data->Type, LHS) << Operator << RHS << Data->Type; +} + +void __ubsan::__ubsan_handle_add_overflow(OverflowData *Data, + ValueHandle LHS, ValueHandle RHS) { + HandleIntegerOverflow(Data, LHS, "+", Value(Data->Type, RHS)); +} +void __ubsan::__ubsan_handle_add_overflow_abort(OverflowData *Data, + ValueHandle LHS, + ValueHandle RHS) { + __ubsan_handle_add_overflow(Data, LHS, RHS); + Die(); +} + +void __ubsan::__ubsan_handle_sub_overflow(OverflowData *Data, + ValueHandle LHS, ValueHandle RHS) { + HandleIntegerOverflow(Data, LHS, "-", Value(Data->Type, RHS)); +} +void __ubsan::__ubsan_handle_sub_overflow_abort(OverflowData *Data, + ValueHandle LHS, + ValueHandle RHS) { + __ubsan_handle_sub_overflow(Data, LHS, RHS); + Die(); +} + +void __ubsan::__ubsan_handle_mul_overflow(OverflowData *Data, + ValueHandle LHS, ValueHandle RHS) { + HandleIntegerOverflow(Data, LHS, "*", Value(Data->Type, RHS)); +} +void __ubsan::__ubsan_handle_mul_overflow_abort(OverflowData *Data, + ValueHandle LHS, + ValueHandle RHS) { + __ubsan_handle_mul_overflow(Data, LHS, RHS); + Die(); +} + +void __ubsan::__ubsan_handle_negate_overflow(OverflowData *Data, + ValueHandle OldVal) { + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + if (Data->Type.isSignedIntegerTy()) + Diag(Loc, DL_Error, + "negation of %0 cannot be represented in type %1; " + "cast to an unsigned type to negate this value to itself") + << Value(Data->Type, OldVal) << Data->Type; + else + Diag(Loc, DL_Error, + "negation of %0 cannot be represented in type %1") + << Value(Data->Type, OldVal) << Data->Type; +} +void __ubsan::__ubsan_handle_negate_overflow_abort(OverflowData *Data, + ValueHandle OldVal) { + __ubsan_handle_negate_overflow(Data, OldVal); + Die(); +} + +void __ubsan::__ubsan_handle_divrem_overflow(OverflowData *Data, + ValueHandle LHS, ValueHandle RHS) { + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + Value LHSVal(Data->Type, LHS); + Value RHSVal(Data->Type, RHS); + if (RHSVal.isMinusOne()) + Diag(Loc, DL_Error, + "division of %0 by -1 cannot be represented in type %1") + << LHSVal << Data->Type; + else + Diag(Loc, DL_Error, "division by zero"); +} +void __ubsan::__ubsan_handle_divrem_overflow_abort(OverflowData *Data, + ValueHandle LHS, + ValueHandle RHS) { + __ubsan_handle_divrem_overflow(Data, LHS, RHS); + Die(); +} + +void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data, + ValueHandle LHS, + ValueHandle RHS) { + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + Value LHSVal(Data->LHSType, LHS); + Value RHSVal(Data->RHSType, RHS); + if (RHSVal.isNegative()) + Diag(Loc, DL_Error, "shift exponent %0 is negative") << RHSVal; + else if (RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth()) + Diag(Loc, DL_Error, + "shift exponent %0 is too large for %1-bit type %2") + << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType; + else if (LHSVal.isNegative()) + Diag(Loc, DL_Error, "left shift of negative value %0") << LHSVal; + else + Diag(Loc, DL_Error, + "left shift of %0 by %1 places cannot be represented in type %2") + << LHSVal << RHSVal << Data->LHSType; +} +void __ubsan::__ubsan_handle_shift_out_of_bounds_abort( + ShiftOutOfBoundsData *Data, + ValueHandle LHS, + ValueHandle RHS) { + __ubsan_handle_shift_out_of_bounds(Data, LHS, RHS); + Die(); +} + +void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) { + Diag(Data->Loc, DL_Error, "execution reached a __builtin_unreachable() call"); + Die(); +} + +void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) { + Diag(Data->Loc, DL_Error, + "execution reached the end of a value-returning function " + "without returning a value"); + Die(); +} + +void __ubsan::__ubsan_handle_vla_bound_not_positive(VLABoundData *Data, + ValueHandle Bound) { + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + Diag(Loc, DL_Error, "variable length array bound evaluates to " + "non-positive value %0") + << Value(Data->Type, Bound); +} +void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data, + ValueHandle Bound) { + __ubsan_handle_vla_bound_not_positive(Data, Bound); + Die(); +} + + +void __ubsan::__ubsan_handle_float_cast_overflow(FloatCastOverflowData *Data, + ValueHandle From) { + // TODO: Add deduplication once a SourceLocation is generated for this check. + Diag(getCallerLocation(), DL_Error, + "value %0 is outside the range of representable values of type %2") + << Value(Data->FromType, From) << Data->FromType << Data->ToType; +} +void __ubsan::__ubsan_handle_float_cast_overflow_abort( + FloatCastOverflowData *Data, + ValueHandle From) { + Diag(getCallerLocation(), DL_Error, + "value %0 is outside the range of representable values of type %2") + << Value(Data->FromType, From) << Data->FromType << Data->ToType; + Die(); +} + +void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData *Data, + ValueHandle Val) { + // TODO: Add deduplication once a SourceLocation is generated for this check. + Diag(getCallerLocation(), DL_Error, + "load of value %0, which is not a valid value for type %1") + << Value(Data->Type, Val) << Data->Type; +} +void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data, + ValueHandle Val) { + Diag(getCallerLocation(), DL_Error, + "load of value %0, which is not a valid value for type %1") + << Value(Data->Type, Val) << Data->Type; + Die(); +} diff --git a/lib/ubsan/ubsan_handlers.h b/lib/ubsan/ubsan_handlers.h new file mode 100644 index 000000000000..d6a042481ffa --- /dev/null +++ b/lib/ubsan/ubsan_handlers.h @@ -0,0 +1,108 @@ +//===-- ubsan_handlers.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Entry points to the runtime library for Clang's undefined behavior sanitizer. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_HANDLERS_H +#define UBSAN_HANDLERS_H + +#include "ubsan_value.h" + +namespace __ubsan { + +struct TypeMismatchData { + SourceLocation Loc; + const TypeDescriptor &Type; + uptr Alignment; + unsigned char TypeCheckKind; +}; + +#define RECOVERABLE(checkname, ...) \ + extern "C" SANITIZER_INTERFACE_ATTRIBUTE \ + void __ubsan_handle_ ## checkname( __VA_ARGS__ ); \ + extern "C" SANITIZER_INTERFACE_ATTRIBUTE \ + void __ubsan_handle_ ## checkname ## _abort( __VA_ARGS__ ); + +/// \brief Handle a runtime type check failure, caused by either a misaligned +/// pointer, a null pointer, or a pointer to insufficient storage for the +/// type. +RECOVERABLE(type_mismatch, TypeMismatchData *Data, ValueHandle Pointer) + +struct OverflowData { + SourceLocation Loc; + const TypeDescriptor &Type; +}; + +/// \brief Handle an integer addition overflow. +RECOVERABLE(add_overflow, OverflowData *Data, ValueHandle LHS, ValueHandle RHS) + +/// \brief Handle an integer subtraction overflow. +RECOVERABLE(sub_overflow, OverflowData *Data, ValueHandle LHS, ValueHandle RHS) + +/// \brief Handle an integer multiplication overflow. +RECOVERABLE(mul_overflow, OverflowData *Data, ValueHandle LHS, ValueHandle RHS) + +/// \brief Handle a signed integer overflow for a unary negate operator. +RECOVERABLE(negate_overflow, OverflowData *Data, ValueHandle OldVal) + +/// \brief Handle an INT_MIN/-1 overflow or division by zero. +RECOVERABLE(divrem_overflow, OverflowData *Data, + ValueHandle LHS, ValueHandle RHS) + +struct ShiftOutOfBoundsData { + SourceLocation Loc; + const TypeDescriptor &LHSType; + const TypeDescriptor &RHSType; +}; + +/// \brief Handle a shift where the RHS is out of bounds or a left shift where +/// the LHS is negative or overflows. +RECOVERABLE(shift_out_of_bounds, ShiftOutOfBoundsData *Data, + ValueHandle LHS, ValueHandle RHS) + +struct UnreachableData { + SourceLocation Loc; +}; + +/// \brief Handle a __builtin_unreachable which is reached. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __ubsan_handle_builtin_unreachable(UnreachableData *Data); +/// \brief Handle reaching the end of a value-returning function. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __ubsan_handle_missing_return(UnreachableData *Data); + +struct VLABoundData { + SourceLocation Loc; + const TypeDescriptor &Type; +}; + +/// \brief Handle a VLA with a non-positive bound. +RECOVERABLE(vla_bound_not_positive, VLABoundData *Data, ValueHandle Bound) + +struct FloatCastOverflowData { + // FIXME: SourceLocation Loc; + const TypeDescriptor &FromType; + const TypeDescriptor &ToType; +}; + +/// \brief Handle overflow in a conversion to or from a floating-point type. +RECOVERABLE(float_cast_overflow, FloatCastOverflowData *Data, ValueHandle From) + +struct InvalidValueData { + // FIXME: SourceLocation Loc; + const TypeDescriptor &Type; +}; + +/// \brief Handle a load of an invalid value for the type. +RECOVERABLE(load_invalid_value, InvalidValueData *Data, ValueHandle Val) + +} + +#endif // UBSAN_HANDLERS_H diff --git a/lib/ubsan/ubsan_handlers_cxx.cc b/lib/ubsan/ubsan_handlers_cxx.cc new file mode 100644 index 000000000000..dcc1f60078d2 --- /dev/null +++ b/lib/ubsan/ubsan_handlers_cxx.cc @@ -0,0 +1,75 @@ +//===-- ubsan_handlers_cxx.cc ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Error logging entry points for the UBSan runtime, which are only used for C++ +// compilations. This file is permitted to use language features which require +// linking against a C++ ABI library. +// +//===----------------------------------------------------------------------===// + +#include "ubsan_handlers_cxx.h" +#include "ubsan_diag.h" +#include "ubsan_type_hash.h" + +#include "sanitizer_common/sanitizer_common.h" + +using namespace __sanitizer; +using namespace __ubsan; + +namespace __ubsan { + extern const char *TypeCheckKinds[]; +} + +static void HandleDynamicTypeCacheMiss( + DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash, + bool Abort) { + if (checkDynamicType((void*)Pointer, Data->TypeInfo, Hash)) + // Just a cache miss. The type matches after all. + return; + + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + Diag(Loc, DL_Error, + "%0 address %1 which does not point to an object of type %2") + << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type; + + // If possible, say what type it actually points to. + // FIXME: Demangle the type names. + DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer); + if (!DTI.isValid()) + Diag(Pointer, DL_Note, "object has invalid vptr") + << MangledName(DTI.getMostDerivedTypeName()) + << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr"); + else if (!DTI.getOffset()) + Diag(Pointer, DL_Note, "object is of type %0") + << MangledName(DTI.getMostDerivedTypeName()) + << Range(Pointer, Pointer + sizeof(uptr), "vptr for %0"); + else + // FIXME: Find the type at the specified offset, and include that + // in the note. + Diag(Pointer - DTI.getOffset(), DL_Note, + "object is base class subobject at offset %0 within object of type %1") + << DTI.getOffset() << MangledName(DTI.getMostDerivedTypeName()) + << MangledName(DTI.getSubobjectTypeName()) + << Range(Pointer, Pointer + sizeof(uptr), "vptr for %2 base class of %1"); + + if (Abort) + Die(); +} + +void __ubsan::__ubsan_handle_dynamic_type_cache_miss( + DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) { + HandleDynamicTypeCacheMiss(Data, Pointer, Hash, false); +} +void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort( + DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) { + HandleDynamicTypeCacheMiss(Data, Pointer, Hash, true); +} diff --git a/lib/ubsan/ubsan_handlers_cxx.h b/lib/ubsan/ubsan_handlers_cxx.h new file mode 100644 index 000000000000..cb1bca78b833 --- /dev/null +++ b/lib/ubsan/ubsan_handlers_cxx.h @@ -0,0 +1,40 @@ +//===-- ubsan_handlers_cxx.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Entry points to the runtime library for Clang's undefined behavior sanitizer, +// for C++-specific checks. This code is not linked into C binaries. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_HANDLERS_CXX_H +#define UBSAN_HANDLERS_CXX_H + +#include "ubsan_value.h" + +namespace __ubsan { + +struct DynamicTypeCacheMissData { + SourceLocation Loc; + const TypeDescriptor &Type; + void *TypeInfo; + unsigned char TypeCheckKind; +}; + +/// \brief Handle a runtime type check failure, caused by an incorrect vptr. +/// When this handler is called, all we know is that the type was not in the +/// cache; this does not necessarily imply the existence of a bug. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __ubsan_handle_dynamic_type_cache_miss( + DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash); +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __ubsan_handle_dynamic_type_cache_miss_abort( + DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash); + +} + +#endif // UBSAN_HANDLERS_H diff --git a/lib/ubsan/ubsan_type_hash.cc b/lib/ubsan/ubsan_type_hash.cc new file mode 100644 index 000000000000..7a9cd28f6ec0 --- /dev/null +++ b/lib/ubsan/ubsan_type_hash.cc @@ -0,0 +1,248 @@ +//===-- ubsan_type_hash.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of a hash table for fast checking of inheritance +// relationships. This file is only linked into C++ compilations, and is +// permitted to use language features which require a C++ ABI library. +// +//===----------------------------------------------------------------------===// + +#include "ubsan_type_hash.h" + +#include "sanitizer_common/sanitizer_common.h" + +// The following are intended to be binary compatible with the definitions +// given in the Itanium ABI. We make no attempt to be ODR-compatible with +// those definitions, since existing ABI implementations aren't. + +namespace std { + class type_info { + public: + virtual ~type_info(); + + const char *__type_name; + }; +} + +namespace __cxxabiv1 { + +/// Type info for classes with no bases, and base class for type info for +/// classes with bases. +class __class_type_info : public std::type_info { + virtual ~__class_type_info(); +}; + +/// Type info for classes with simple single public inheritance. +class __si_class_type_info : public __class_type_info { +public: + virtual ~__si_class_type_info(); + + const __class_type_info *__base_type; +}; + +class __base_class_type_info { +public: + const __class_type_info *__base_type; + long __offset_flags; + + enum __offset_flags_masks { + __virtual_mask = 0x1, + __public_mask = 0x2, + __offset_shift = 8 + }; +}; + +/// Type info for classes with multiple, virtual, or non-public inheritance. +class __vmi_class_type_info : public __class_type_info { +public: + virtual ~__vmi_class_type_info(); + + unsigned int flags; + unsigned int base_count; + __base_class_type_info base_info[1]; +}; + +} + +namespace abi = __cxxabiv1; + +// We implement a simple two-level cache for type-checking results. For each +// (vptr,type) pair, a hash is computed. This hash is assumed to be globally +// unique; if it collides, we will get false negatives, but: +// * such a collision would have to occur on the *first* bad access, +// * the probability of such a collision is low (and for a 64-bit target, is +// negligible), and +// * the vptr, and thus the hash, can be affected by ASLR, so multiple runs +// give better coverage. +// +// The first caching layer is a small hash table with no chaining; buckets are +// reused as needed. The second caching layer is a large hash table with open +// chaining. We can freely evict from either layer since this is just a cache. +// +// FIXME: Make these hash table accesses thread-safe. The races here are benign +// (worst-case, we could miss a bug or see a slowdown) but we should +// avoid upsetting race detectors. + +/// Find a bucket to store the given hash value in. +static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) { + static const unsigned HashTableSize = 65537; + static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize] = { 1 }; + + unsigned Probe = V & 65535; + for (int Tries = 5; Tries; --Tries) { + if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V) + return &__ubsan_vptr_hash_set[Probe]; + Probe += ((V >> 16) & 65535) + 1; + if (Probe >= HashTableSize) + Probe -= HashTableSize; + } + // FIXME: Pick a random entry from the probe sequence to evict rather than + // just taking the first. + return &__ubsan_vptr_hash_set[V]; +} + +/// A cache of recently-checked hashes. Mini hash table with "random" evictions. +__ubsan::HashValue +__ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize] = { 1 }; + +/// \brief Determine whether \p Derived has a \p Base base class subobject at +/// offset \p Offset. +static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived, + const abi::__class_type_info *Base, + sptr Offset) { + if (Derived == Base) + return Offset == 0; + + if (const abi::__si_class_type_info *SI = + dynamic_cast<const abi::__si_class_type_info*>(Derived)) + return isDerivedFromAtOffset(SI->__base_type, Base, Offset); + + const abi::__vmi_class_type_info *VTI = + dynamic_cast<const abi::__vmi_class_type_info*>(Derived); + if (!VTI) + // No base class subobjects. + return false; + + // Look for a base class which is derived from \p Base at the right offset. + for (unsigned int base = 0; base != VTI->base_count; ++base) { + // FIXME: Curtail the recursion if this base can't possibly contain the + // given offset. + sptr OffsetHere = VTI->base_info[base].__offset_flags >> + abi::__base_class_type_info::__offset_shift; + if (VTI->base_info[base].__offset_flags & + abi::__base_class_type_info::__virtual_mask) + // For now, just punt on virtual bases and say 'yes'. + // FIXME: OffsetHere is the offset in the vtable of the virtual base + // offset. Read the vbase offset out of the vtable and use it. + return true; + if (isDerivedFromAtOffset(VTI->base_info[base].__base_type, + Base, Offset - OffsetHere)) + return true; + } + + return false; +} + +/// \brief Find the derived-most dynamic base class of \p Derived at offset +/// \p Offset. +static const abi::__class_type_info *findBaseAtOffset( + const abi::__class_type_info *Derived, sptr Offset) { + if (!Offset) + return Derived; + + if (const abi::__si_class_type_info *SI = + dynamic_cast<const abi::__si_class_type_info*>(Derived)) + return findBaseAtOffset(SI->__base_type, Offset); + + const abi::__vmi_class_type_info *VTI = + dynamic_cast<const abi::__vmi_class_type_info*>(Derived); + if (!VTI) + // No base class subobjects. + return 0; + + for (unsigned int base = 0; base != VTI->base_count; ++base) { + sptr OffsetHere = VTI->base_info[base].__offset_flags >> + abi::__base_class_type_info::__offset_shift; + if (VTI->base_info[base].__offset_flags & + abi::__base_class_type_info::__virtual_mask) + // FIXME: Can't handle virtual bases yet. + continue; + if (const abi::__class_type_info *Base = + findBaseAtOffset(VTI->base_info[base].__base_type, + Offset - OffsetHere)) + return Base; + } + + return 0; +} + +namespace { + +struct VtablePrefix { + /// The offset from the vptr to the start of the most-derived object. + /// This should never be greater than zero, and will usually be exactly + /// zero. + sptr Offset; + /// The type_info object describing the most-derived class type. + std::type_info *TypeInfo; +}; +VtablePrefix *getVtablePrefix(void *Object) { + VtablePrefix **VptrPtr = reinterpret_cast<VtablePrefix**>(Object); + if (!*VptrPtr) + return 0; + VtablePrefix *Prefix = *VptrPtr - 1; + if (Prefix->Offset > 0 || !Prefix->TypeInfo) + // This can't possibly be a valid vtable. + return 0; + return Prefix; +} + +} + +bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) { + // A crash anywhere within this function probably means the vptr is corrupted. + // FIXME: Perform these checks more cautiously. + + // Check whether this is something we've evicted from the cache. + HashValue *Bucket = getTypeCacheHashTableBucket(Hash); + if (*Bucket == Hash) { + __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash; + return true; + } + + VtablePrefix *Vtable = getVtablePrefix(Object); + if (!Vtable) + return false; + + // Check that this is actually a type_info object for a class type. + abi::__class_type_info *Derived = + dynamic_cast<abi::__class_type_info*>(Vtable->TypeInfo); + if (!Derived) + return false; + + abi::__class_type_info *Base = (abi::__class_type_info*)Type; + if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset)) + return false; + + // Success. Cache this result. + __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash; + *Bucket = Hash; + return true; +} + +__ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfo(void *Object) { + VtablePrefix *Vtable = getVtablePrefix(Object); + if (!Vtable) + return DynamicTypeInfo(0, 0, 0); + const abi::__class_type_info *ObjectType = findBaseAtOffset( + static_cast<const abi::__class_type_info*>(Vtable->TypeInfo), + -Vtable->Offset); + return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset, + ObjectType ? ObjectType->__type_name : "<unknown>"); +} diff --git a/lib/ubsan/ubsan_type_hash.h b/lib/ubsan/ubsan_type_hash.h new file mode 100644 index 000000000000..58ecd3de9864 --- /dev/null +++ b/lib/ubsan/ubsan_type_hash.h @@ -0,0 +1,63 @@ +//===-- ubsan_type_hash.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Hashing of types for Clang's undefined behavior checker. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_TYPE_HASH_H +#define UBSAN_TYPE_HASH_H + +#include "sanitizer_common/sanitizer_common.h" + +namespace __ubsan { + +typedef uptr HashValue; + +/// \brief Information about the dynamic type of an object (extracted from its +/// vptr). +class DynamicTypeInfo { + const char *MostDerivedTypeName; + sptr Offset; + const char *SubobjectTypeName; + +public: + DynamicTypeInfo(const char *MDTN, sptr Offset, const char *STN) + : MostDerivedTypeName(MDTN), Offset(Offset), SubobjectTypeName(STN) {} + + /// Determine whether the object had a valid dynamic type. + bool isValid() const { return MostDerivedTypeName; } + /// Get the name of the most-derived type of the object. + const char *getMostDerivedTypeName() const { return MostDerivedTypeName; } + /// Get the offset from the most-derived type to this base class. + sptr getOffset() const { return Offset; } + /// Get the name of the most-derived type at the specified offset. + const char *getSubobjectTypeName() const { return SubobjectTypeName; } +}; + +/// \brief Get information about the dynamic type of an object. +DynamicTypeInfo getDynamicTypeInfo(void *Object); + +/// \brief Check whether the dynamic type of \p Object has a \p Type subobject +/// at offset 0. +/// \return \c true if the type matches, \c false if not. +bool checkDynamicType(void *Object, void *Type, HashValue Hash); + +const unsigned VptrTypeCacheSize = 128; + +/// \brief A cache of the results of checkDynamicType. \c checkDynamicType would +/// return \c true (modulo hash collisions) if +/// \code +/// __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] == Hash +/// \endcode +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +HashValue __ubsan_vptr_type_cache[VptrTypeCacheSize]; + +} // namespace __ubsan + +#endif // UBSAN_TYPE_HASH_H diff --git a/lib/ubsan/ubsan_value.cc b/lib/ubsan/ubsan_value.cc new file mode 100644 index 000000000000..f17c58989db9 --- /dev/null +++ b/lib/ubsan/ubsan_value.cc @@ -0,0 +1,81 @@ +//===-- ubsan_value.cc ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Representation of a runtime value, as marshaled from the generated code to +// the ubsan runtime. +// +//===----------------------------------------------------------------------===// + +#include "ubsan_value.h" + +using namespace __ubsan; + +SIntMax Value::getSIntValue() const { + CHECK(getType().isSignedIntegerTy()); + if (isInlineInt()) { + // Val was zero-extended to ValueHandle. Sign-extend from original width + // to SIntMax. + const unsigned ExtraBits = + sizeof(SIntMax) * 8 - getType().getIntegerBitWidth(); + return SIntMax(Val) << ExtraBits >> ExtraBits; + } + if (getType().getIntegerBitWidth() == 64) + return *reinterpret_cast<s64*>(Val); +#if HAVE_INT128_T + if (getType().getIntegerBitWidth() == 128) + return *reinterpret_cast<s128*>(Val); +#else + if (getType().getIntegerBitWidth() == 128) + UNREACHABLE("libclang_rt.ubsan was built without __int128 support"); +#endif + UNREACHABLE("unexpected bit width"); +} + +UIntMax Value::getUIntValue() const { + CHECK(getType().isUnsignedIntegerTy()); + if (isInlineInt()) + return Val; + if (getType().getIntegerBitWidth() == 64) + return *reinterpret_cast<u64*>(Val); +#if HAVE_INT128_T + if (getType().getIntegerBitWidth() == 128) + return *reinterpret_cast<u128*>(Val); +#else + if (getType().getIntegerBitWidth() == 128) + UNREACHABLE("libclang_rt.ubsan was built without __int128 support"); +#endif + UNREACHABLE("unexpected bit width"); +} + +UIntMax Value::getPositiveIntValue() const { + if (getType().isUnsignedIntegerTy()) + return getUIntValue(); + SIntMax Val = getSIntValue(); + CHECK(Val >= 0); + return Val; +} + +/// Get the floating-point value of this object, extended to a long double. +/// These are always passed by address (our calling convention doesn't allow +/// them to be passed in floating-point registers, so this has little cost). +FloatMax Value::getFloatValue() const { + CHECK(getType().isFloatTy()); + switch (getType().getFloatBitWidth()) { +#if 0 + // FIXME: OpenCL / NEON 'half' type. LLVM can't lower the conversion + // from this to 'long double'. + case 16: return *reinterpret_cast<__fp16*>(Val); +#endif + case 32: return *reinterpret_cast<float*>(Val); + case 64: return *reinterpret_cast<double*>(Val); + case 80: return *reinterpret_cast<long double*>(Val); + case 128: return *reinterpret_cast<long double*>(Val); + } + UNREACHABLE("unexpected floating point bit width"); +} diff --git a/lib/ubsan/ubsan_value.h b/lib/ubsan/ubsan_value.h new file mode 100644 index 000000000000..e673f7af1d83 --- /dev/null +++ b/lib/ubsan/ubsan_value.h @@ -0,0 +1,195 @@ +//===-- ubsan_value.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Representation of data which is passed from the compiler-generated calls into +// the ubsan runtime. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_VALUE_H +#define UBSAN_VALUE_H + +// For now, only support linux and darwin. Other platforms should be easy to +// add, and probably work as-is. +#if !defined(__linux__) && !defined(__APPLE__) +#error "UBSan not supported for this platform!" +#endif + +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" + +// FIXME: Move this out to a config header. +#if __SIZEOF_INT128__ +typedef __int128 s128; +typedef unsigned __int128 u128; +#define HAVE_INT128_T 1 +#else +#define HAVE_INT128_T 0 +#endif + + +namespace __ubsan { + +/// \brief Largest integer types we support. +#if HAVE_INT128_T +typedef s128 SIntMax; +typedef u128 UIntMax; +#else +typedef s64 SIntMax; +typedef u64 UIntMax; +#endif + +/// \brief Largest floating-point type we support. +typedef long double FloatMax; + +/// \brief A description of a source location. This corresponds to Clang's +/// \c PresumedLoc type. +class SourceLocation { + const char *Filename; + u32 Line; + u32 Column; + +public: + SourceLocation() : Filename(), Line(), Column() {} + SourceLocation(const char *Filename, unsigned Line, unsigned Column) + : Filename(Filename), Line(Line), Column(Column) {} + + /// \brief Determine whether the source location is known. + bool isInvalid() const { return !Filename; } + + /// \brief Atomically acquire a copy, disabling original in-place. + /// Exactly one call to acquire() returns a copy that isn't disabled. + SourceLocation acquire() { + u32 OldColumn = __sanitizer::atomic_exchange( + (__sanitizer::atomic_uint32_t *)&Column, ~u32(0), + __sanitizer::memory_order_relaxed); + return SourceLocation(Filename, Line, OldColumn); + } + + /// \brief Determine if this Location has been disabled. + /// Disabled SourceLocations are invalid to use. + bool isDisabled() { + return Column == ~u32(0); + } + + /// \brief Get the presumed filename for the source location. + const char *getFilename() const { return Filename; } + /// \brief Get the presumed line number. + unsigned getLine() const { return Line; } + /// \brief Get the column within the presumed line. + unsigned getColumn() const { return Column; } +}; + + +/// \brief A description of a type. +class TypeDescriptor { + /// A value from the \c Kind enumeration, specifying what flavor of type we + /// have. + u16 TypeKind; + + /// A \c Type-specific value providing information which allows us to + /// interpret the meaning of a ValueHandle of this type. + u16 TypeInfo; + + /// The name of the type follows, in a format suitable for including in + /// diagnostics. + char TypeName[1]; + +public: + enum Kind { + /// An integer type. Lowest bit is 1 for a signed value, 0 for an unsigned + /// value. Remaining bits are log_2(bit width). The value representation is + /// the integer itself if it fits into a ValueHandle, and a pointer to the + /// integer otherwise. + TK_Integer = 0x0000, + /// A floating-point type. Low 16 bits are bit width. The value + /// representation is a pointer to the floating-point value. + TK_Float = 0x0001, + /// Any other type. The value representation is unspecified. + TK_Unknown = 0xffff + }; + + const char *getTypeName() const { return TypeName; } + + Kind getKind() const { + return static_cast<Kind>(TypeKind); + } + + bool isIntegerTy() const { return getKind() == TK_Integer; } + bool isSignedIntegerTy() const { + return isIntegerTy() && (TypeInfo & 1); + } + bool isUnsignedIntegerTy() const { + return isIntegerTy() && !(TypeInfo & 1); + } + unsigned getIntegerBitWidth() const { + CHECK(isIntegerTy()); + return 1 << (TypeInfo >> 1); + } + + bool isFloatTy() const { return getKind() == TK_Float; } + unsigned getFloatBitWidth() const { + CHECK(isFloatTy()); + return TypeInfo; + } +}; + +/// \brief An opaque handle to a value. +typedef uptr ValueHandle; + + +/// \brief Representation of an operand value provided by the instrumented code. +/// +/// This is a combination of a TypeDescriptor (which is emitted as constant data +/// as an operand to a handler function) and a ValueHandle (which is passed at +/// runtime when a check failure occurs). +class Value { + /// The type of the value. + const TypeDescriptor &Type; + /// The encoded value itself. + ValueHandle Val; + + /// Is \c Val a (zero-extended) integer? + bool isInlineInt() const { + CHECK(getType().isIntegerTy()); + const unsigned InlineBits = sizeof(ValueHandle) * 8; + const unsigned Bits = getType().getIntegerBitWidth(); + return Bits <= InlineBits; + } + +public: + Value(const TypeDescriptor &Type, ValueHandle Val) : Type(Type), Val(Val) {} + + const TypeDescriptor &getType() const { return Type; } + + /// \brief Get this value as a signed integer. + SIntMax getSIntValue() const; + + /// \brief Get this value as an unsigned integer. + UIntMax getUIntValue() const; + + /// \brief Decode this value, which must be a positive or unsigned integer. + UIntMax getPositiveIntValue() const; + + /// Is this an integer with value -1? + bool isMinusOne() const { + return getType().isSignedIntegerTy() && getSIntValue() == -1; + } + + /// Is this a negative integer? + bool isNegative() const { + return getType().isSignedIntegerTy() && getSIntValue() < 0; + } + + /// \brief Get this value as a floating-point quantity. + FloatMax getFloatValue() const; +}; + +} // namespace __ubsan + +#endif // UBSAN_VALUE_H |