diff --git a/compiler-rt/lib/CMakeLists.txt b/compiler-rt/lib/CMakeLists.txt index a1dc48c467f8..6febaf54c6d9 100644 --- a/compiler-rt/lib/CMakeLists.txt +++ b/compiler-rt/lib/CMakeLists.txt @@ -10,6 +10,8 @@ endif() if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") # ThreadSanitizer is supported on Linux only. add_subdirectory(tsan) + # UndefinedBehaviorSanitizer has been tested on Linux only. + add_subdirectory(ubsan) endif() # FIXME: Add support for the profile library. diff --git a/compiler-rt/lib/ubsan/CMakeLists.txt b/compiler-rt/lib/ubsan/CMakeLists.txt new file mode 100644 index 000000000000..fa6393f84b6f --- /dev/null +++ b/compiler-rt/lib/ubsan/CMakeLists.txt @@ -0,0 +1,35 @@ +# Build for the undefined behavior sanitizer runtime support library. + +set(UBSAN_SOURCES + ubsan_diag.cc + ubsan_handlers.cc + ubsan_value.cc + ) + +include_directories(..) + +set(UBSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS}) + +set(UBSAN_RUNTIME_LIBRARIES) + +if(CAN_TARGET_X86_64) + add_library(clang_rt.ubsan-x86_64 STATIC ${UBSAN_SOURCES}) + set_target_compile_flags(clang_rt.ubsan-x86_64 + ${UBSAN_CFLAGS} ${TARGET_X86_64_CFLAGS} + ) + list(APPEND UBSAN_RUNTIME_LIBRARIES clang_rt.ubsan-x86_64) +endif() + +if(CAN_TARGET_I386) + add_library(clang_rt.ubsan-i386 STATIC ${UBSAN_SOURCES}) + set_target_compile_flags(clang_rt.ubsan-i386 + ${UBSAN_CFLAGS} ${TARGET_I386_CFLAGS} + ) + list(APPEND UBSAN_RUNTIME_LIBRARIES clang_rt.ubsan-i386) +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/compiler-rt/lib/ubsan/lit_tests/CMakeLists.txt b/compiler-rt/lib/ubsan/lit_tests/CMakeLists.txt new file mode 100644 index 000000000000..67d786dd9597 --- /dev/null +++ b/compiler-rt/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("${CMAKE_HOST_SYSTEM}" STREQUAL "${CMAKE_SYSTEM}") + # Run UBSan output tests only if we're not cross-compiling, + # and can be 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/compiler-rt/lib/ubsan/lit_tests/Integer/add-overflow.cpp b/compiler-rt/lib/ubsan/lit_tests/Integer/add-overflow.cpp new file mode 100644 index 000000000000..781ccf3a6b90 --- /dev/null +++ b/compiler-rt/lib/ubsan/lit_tests/Integer/add-overflow.cpp @@ -0,0 +1,27 @@ +// RUN: %clang -DADD_I32 -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I32 +// RUN: %clang -DADD_I64 -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I64 +// RUN: %clang -DADD_I128 -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I128 + +#include + +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:14:5: fatal error: signed integer overflow: 305419896 + 2023406814 cannot be represented in type 'int32_t' (aka 'int') +#endif + +#ifdef ADD_I64 + (void)(int64_t(8000000000000000000ll) + int64_t(2000000000000000000ll)); + // CHECK-ADD_I64: 8000000000000000000 + 2000000000000000000 cannot be represented in type 'long' +#endif + +#ifdef ADD_I128 + (void)((__int128_t(1) << 126) + (__int128_t(1) << 126)); + // CHECK-ADD_I128: 0x40000000000000000000000000000000 + 0x40000000000000000000000000000000 cannot be represented in type '__int128' +#endif +} diff --git a/compiler-rt/lib/ubsan/lit_tests/Integer/div-overflow.cpp b/compiler-rt/lib/ubsan/lit_tests/Integer/div-overflow.cpp new file mode 100644 index 000000000000..fb0e7d1cec2b --- /dev/null +++ b/compiler-rt/lib/ubsan/lit_tests/Integer/div-overflow.cpp @@ -0,0 +1,10 @@ +// RUN: %clang -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s + +#include + +int main() { + unsigned(0x80000000) / -1; + + // CHECK: div-overflow.cpp:9:23: fatal error: division of -2147483648 by -1 cannot be represented in type 'int' + int32_t(0x80000000) / -1; +} diff --git a/compiler-rt/lib/ubsan/lit_tests/Integer/div-zero.cpp b/compiler-rt/lib/ubsan/lit_tests/Integer/div-zero.cpp new file mode 100644 index 000000000000..0c1c3f886df2 --- /dev/null +++ b/compiler-rt/lib/ubsan/lit_tests/Integer/div-zero.cpp @@ -0,0 +1,9 @@ +// RUN: %clang -fcatch-undefined-behavior -DDIVIDEND=0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang -fcatch-undefined-behavior -DDIVIDEND=1U %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang -fcatch-undefined-behavior -DDIVIDEND=1.5 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang -fcatch-undefined-behavior -DDIVIDEND='__int128(123)' %s -o %t && %t 2>&1 | FileCheck %s + +int main() { + // CHECK: div-zero.cpp:8:12: fatal error: division by zero + DIVIDEND / 0; +} diff --git a/compiler-rt/lib/ubsan/lit_tests/Integer/incdec-overflow.cpp b/compiler-rt/lib/ubsan/lit_tests/Integer/incdec-overflow.cpp new file mode 100644 index 000000000000..8c8800b5b717 --- /dev/null +++ b/compiler-rt/lib/ubsan/lit_tests/Integer/incdec-overflow.cpp @@ -0,0 +1,16 @@ +// RUN: %clang -DOP=n++ -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang -DOP=++n -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang -DOP=m-- -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clang -DOP=--m -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s + +#include + +int main() { + int n = 0x7ffffffd; + n++; + n++; + int m = -n - 1; + // CHECK: incdec-overflow.cpp:15:3: fatal error: signed integer overflow: [[MINUS:-?]]214748364 + // CHECK: + [[MINUS]]1 cannot be represented in type 'int' + OP; +} diff --git a/compiler-rt/lib/ubsan/lit_tests/Integer/mul-overflow.cpp b/compiler-rt/lib/ubsan/lit_tests/Integer/mul-overflow.cpp new file mode 100644 index 000000000000..1ba968da52c4 --- /dev/null +++ b/compiler-rt/lib/ubsan/lit_tests/Integer/mul-overflow.cpp @@ -0,0 +1,14 @@ +// RUN: %clang -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s + +#include + +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: fatal error: signed integer overflow: 65535 * 32769 cannot be represented in type 'int' + (void)(uint16_t(0xffff) * uint16_t(0x8001)); +} diff --git a/compiler-rt/lib/ubsan/lit_tests/Integer/negate-overflow.cpp b/compiler-rt/lib/ubsan/lit_tests/Integer/negate-overflow.cpp new file mode 100644 index 000000000000..58f1b4870dd0 --- /dev/null +++ b/compiler-rt/lib/ubsan/lit_tests/Integer/negate-overflow.cpp @@ -0,0 +1,7 @@ +// RUN: %clang -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s + +int main() { + -unsigned(-0x7fffffff - 1); // ok + // CHECK: negate-overflow.cpp:6:10: fatal error: negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself + return -(-0x7fffffff - 1); +} diff --git a/compiler-rt/lib/ubsan/lit_tests/Integer/shift.cpp b/compiler-rt/lib/ubsan/lit_tests/Integer/shift.cpp new file mode 100644 index 000000000000..33a15313f3d8 --- /dev/null +++ b/compiler-rt/lib/ubsan/lit_tests/Integer/shift.cpp @@ -0,0 +1,37 @@ +// RUN: %clang -DLSH_OVERFLOW -DOP='<<' -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=LSH_OVERFLOW +// RUN: %clang -DLSH_OVERFLOW -DOP='<<=' -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=LSH_OVERFLOW +// RUN: %clang -DTOO_LOW -DOP='<<' -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_LOW +// RUN: %clang -DTOO_LOW -DOP='>>' -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_LOW +// RUN: %clang -DTOO_LOW -DOP='<<=' -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_LOW +// RUN: %clang -DTOO_LOW -DOP='>>=' -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_LOW +// RUN: %clang -DTOO_HIGH -DOP='<<' -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_HIGH +// RUN: %clang -DTOO_HIGH -DOP='>>' -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_HIGH +// RUN: %clang -DTOO_HIGH -DOP='<<=' -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_HIGH +// RUN: %clang -DTOO_HIGH -DOP='>>=' -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_HIGH + +#include + +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: fatal error: left shift of negative value -2147483648 + a OP 1; +#endif + +#ifdef TOO_LOW + // CHECK-TOO_LOW: shift.cpp:29:5: fatal error: shift exponent -3 is negative + a OP (-3); +#endif + +#ifdef TOO_HIGH + a = 0; + // CHECK-TOO_HIGH: shift.cpp:35:5: fatal error: shift exponent 32 is too large for 32-bit type 'int' + a OP 32; +#endif +} diff --git a/compiler-rt/lib/ubsan/lit_tests/Integer/sub-overflow.cpp b/compiler-rt/lib/ubsan/lit_tests/Integer/sub-overflow.cpp new file mode 100644 index 000000000000..71925c9bde8a --- /dev/null +++ b/compiler-rt/lib/ubsan/lit_tests/Integer/sub-overflow.cpp @@ -0,0 +1,26 @@ +// RUN: %clang -DSUB_I32 -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I32 +// RUN: %clang -DSUB_I64 -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I64 +// RUN: %clang -DSUB_I128 -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I128 + +#include + +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:13:22: fatal 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' +#endif + +#ifdef SUB_I128 + (void)(-(__int128_t(1) << 126) - (__int128_t(1) << 126) - 1); + // CHECK-SUB_I128: 0x80000000000000000000000000000000 - 1 cannot be represented in type '__int128' +#endif +} diff --git a/compiler-rt/lib/ubsan/lit_tests/Misc/missing_return.cpp b/compiler-rt/lib/ubsan/lit_tests/Misc/missing_return.cpp new file mode 100644 index 000000000000..73febea4633d --- /dev/null +++ b/compiler-rt/lib/ubsan/lit_tests/Misc/missing_return.cpp @@ -0,0 +1,9 @@ +// RUN: %clang -fcatch-undefined-behavior %s -O3 -o %t && %t 2>&1 | FileCheck %s + +// CHECK: missing_return.cpp:4:5: fatal 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/compiler-rt/lib/ubsan/lit_tests/Misc/unreachable.cpp b/compiler-rt/lib/ubsan/lit_tests/Misc/unreachable.cpp new file mode 100644 index 000000000000..ded1de63aeaf --- /dev/null +++ b/compiler-rt/lib/ubsan/lit_tests/Misc/unreachable.cpp @@ -0,0 +1,6 @@ +// RUN: %clang -fcatch-undefined-behavior %s -O3 -o %t && %t 2>&1 | FileCheck %s + +int main(int, char **argv) { + // CHECK: unreachable.cpp:5:3: fatal error: execution reached a __builtin_unreachable() call + __builtin_unreachable(); +} diff --git a/compiler-rt/lib/ubsan/lit_tests/TypeCheck/misaligned.cpp b/compiler-rt/lib/ubsan/lit_tests/TypeCheck/misaligned.cpp new file mode 100644 index 000000000000..acc73e0fffcf --- /dev/null +++ b/compiler-rt/lib/ubsan/lit_tests/TypeCheck/misaligned.cpp @@ -0,0 +1,42 @@ +// RUN: %clang -fcatch-undefined-behavior %s -O3 -o %t +// RUN: %t l0 && %t s0 && %t r0 && %t m0 && %t f0 +// RUN: %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD +// 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 + +struct S { + int f() { return 0; } + int k; +}; + +int main(int, char **argv) { + char c[5] __attribute__((aligned(4))) = {}; + + // Pointer value may be unspecified here, but behavior is not undefined. + int *p = (int*)&c[argv[1][1] - '0']; + S *s = (S*)p; + + (void)*p; // ok! + + switch (argv[1][0]) { + case 'l': + // CHECK-LOAD: misaligned.cpp:26:12: fatal error: load of misaligned address 0x{{[0-9a-f]*}} for type 'int', which requires 4 byte alignment + return *p; + case 's': + // CHECK-STORE: misaligned.cpp:29:5: fatal error: store to misaligned address 0x{{[0-9a-f]*}} for type 'int', which requires 4 byte alignment + *p = 1; + break; + case 'r': + // CHECK-REFERENCE: misaligned.cpp:33:15: fatal error: reference binding to misaligned address 0x{{[0-9a-f]*}} for type 'int', which requires 4 byte alignment + {int &r = *p;} + break; + case 'm': + // CHECK-MEMBER: misaligned.cpp:37:15: fatal error: member access within misaligned address 0x{{[0-9a-f]*}} for type 'S', which requires 4 byte alignment + return s->k; + case 'f': + // CHECK-MEMFUN: misaligned.cpp:40:12: fatal error: member call on misaligned address 0x{{[0-9a-f]*}} for type 'S', which requires 4 byte alignment + return s->f(); + } +} diff --git a/compiler-rt/lib/ubsan/lit_tests/TypeCheck/null.cpp b/compiler-rt/lib/ubsan/lit_tests/TypeCheck/null.cpp new file mode 100644 index 000000000000..8e17388fb63a --- /dev/null +++ b/compiler-rt/lib/ubsan/lit_tests/TypeCheck/null.cpp @@ -0,0 +1,38 @@ +// RUN: %clang -fcatch-undefined-behavior %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: fatal error: load of null pointer of type 'int' + return *p; + case 's': + // CHECK-STORE: null.cpp:25:5: fatal error: store to null pointer of type 'int' + *p = 1; + break; + case 'r': + // CHECK-REFERENCE: null.cpp:29:15: fatal error: reference binding to null pointer of type 'int' + {int &r = *p;} + break; + case 'm': + // CHECK-MEMBER: null.cpp:33:15: fatal error: member access within null pointer of type 'S' + return s->k; + case 'f': + // CHECK-MEMFUN: null.cpp:36:12: fatal error: member call on null pointer of type 'S' + return s->f(); + } +} diff --git a/compiler-rt/lib/ubsan/lit_tests/lit.cfg b/compiler-rt/lib/ubsan/lit_tests/lit.cfg new file mode 100644 index 000000000000..50a8cbd601eb --- /dev/null +++ b/compiler-rt/lib/ubsan/lit_tests/lit.cfg @@ -0,0 +1,64 @@ +# -*- 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 only. +if config.host_os not in ['Linux']: + config.unsupported = True diff --git a/compiler-rt/lib/ubsan/lit_tests/lit.site.cfg.in b/compiler-rt/lib/ubsan/lit_tests/lit.site.cfg.in new file mode 100644 index 000000000000..b1c6ccf544ea --- /dev/null +++ b/compiler-rt/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/compiler-rt/lib/ubsan/ubsan_diag.cc b/compiler-rt/lib/ubsan/ubsan_diag.cc new file mode 100644 index 000000000000..769fd95bc0cb --- /dev/null +++ b/compiler-rt/lib/ubsan/ubsan_diag.cc @@ -0,0 +1,97 @@ +//===-- 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 +#include +#include + +using namespace __ubsan; + +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 + AddArg(""); + return *this; +} + +/// Hexadecimal printing for numbers too large for fprintf to handle directly. +static void PrintHex(UIntMax Val) { +#ifdef HAVE_INT128_T + fprintf(stderr, "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 +} + +Diag::~Diag() { + // FIXME: This is non-portable. + bool UseAnsiColor = isatty(STDERR_FILENO); + if (UseAnsiColor) + fprintf(stderr, "\033[1m"); + if (Loc.isInvalid()) + fprintf(stderr, ":"); + else { + fprintf(stderr, "%s:%d:", Loc.getFilename(), Loc.getLine()); + if (Loc.getColumn()) + fprintf(stderr, "%d:", Loc.getColumn()); + } + if (UseAnsiColor) + fprintf(stderr, "\033[31m"); + fprintf(stderr, " fatal error: "); + if (UseAnsiColor) + fprintf(stderr, "\033[0;1m"); + for (const char *Msg = Message; *Msg; ++Msg) { + if (*Msg != '%') + fputc((unsigned char)*Msg, stderr); + else { + const Arg &A = Args[*++Msg - '0']; + switch (A.Kind) { + case AK_String: + fprintf(stderr, "%s", A.String); + break; + case AK_SInt: + // 'long long' is guaranteed to be at least 64 bits wide. + if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX) + fprintf(stderr, "%lld", (long long)A.SInt); + else + PrintHex(A.SInt); + break; + case AK_UInt: + if (A.UInt <= UINT64_MAX) + fprintf(stderr, "%llu", (unsigned long long)A.UInt); + else + PrintHex(A.UInt); + break; + case AK_Pointer: + fprintf(stderr, "0x%zx", (uptr)A.Pointer); + break; + } + } + } + fputc('\n', stderr); + if (UseAnsiColor) + fprintf(stderr, "\033[0m"); + fflush(stderr); +} diff --git a/compiler-rt/lib/ubsan/ubsan_diag.h b/compiler-rt/lib/ubsan/ubsan_diag.h new file mode 100644 index 000000000000..b88d001b82b7 --- /dev/null +++ b/compiler-rt/lib/ubsan/ubsan_diag.h @@ -0,0 +1,88 @@ +//===-- 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 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 source location at which the problem occurred. + const SourceLocation &Loc; + + /// The message which will be emitted, with %0, %1, ... placeholders for + /// arguments. + const char *Message; + + /// Kinds of arguments, corresponding to members of \c Arg's union. + enum ArgKind { + AK_String, ///< A string argument, displayed as-is. + AK_UInt, ///< An unsigned integer argument. + AK_SInt, ///< A signed integer 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(UIntMax UInt) : Kind(AK_UInt), UInt(UInt) {} + Arg(SIntMax SInt) : Kind(AK_SInt), SInt(SInt) {} + Arg(const void *Pointer) : Kind(AK_Pointer), Pointer(Pointer) {} + + ArgKind Kind; + union { + const char *String; + UIntMax UInt; + SIntMax SInt; + const void *Pointer; + }; + }; + + static const unsigned MaxArgs = 5; + + /// The arguments which have been added to this diagnostic so far. + Arg Args[MaxArgs]; + unsigned NumArgs; + + Diag &AddArg(Arg A) { + CHECK(NumArgs != MaxArgs); + Args[NumArgs++] = A; + return *this; + } + + /// \c Diag objects are not copyable. + Diag(const Diag &); // NOT IMPLEMENTED + Diag &operator=(const Diag &); + +public: + Diag(const SourceLocation &Loc, const char *Message) + : Loc(Loc), Message(Message), NumArgs(0) {} + ~Diag(); + + Diag &operator<<(const char *Str) { return AddArg(Str); } + 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); +}; + +} // namespace __ubsan + +#endif // UBSAN_DIAG_H diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.cc b/compiler-rt/lib/ubsan/ubsan_handlers.cc new file mode 100644 index 000000000000..8aec335b875e --- /dev/null +++ b/compiler-rt/lib/ubsan/ubsan_handlers.cc @@ -0,0 +1,128 @@ +//===-- ubsan_report.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; + +NORETURN void __sanitizer::Die() { + __builtin_trap(); +} + +NORETURN void __sanitizer::CheckFailed(const char *File, int Line, + const char *Cond, u64 V1, u64 V2) { + Diag(SourceLocation(File, Line, 0), + "CHECK failed: %0 (with values %1 and %2)") + << Cond << V1 << V2; + Die(); +} + +void __ubsan::__ubsan_handle_type_mismatch(TypeMismatchData *Data, + ValueHandle Pointer) { + const char *TypeCheckKinds[] = { + "load of", "store to", "reference binding to", "member access within", + "member call on" + }; + if (!Pointer) + Diag(Data->Loc, "%0 null pointer of type %1") + << TypeCheckKinds[Data->TypeCheckKind] << Data->Type; + else if (Data->Alignment && (Pointer & (Data->Alignment - 1))) + Diag(Data->Loc, "%0 misaligned address %1 for type %3, " + "which requires %2 byte alignment") + << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer + << Data->Alignment << Data->Type; + else + Diag(Data->Loc, "%0 address %1 with insufficient space " + "for an object of type %2") + << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type; + Die(); +} + +/// \brief Common diagnostic emission for various forms of signed overflow. +template static void HandleSignedOverflow(OverflowData *Data, + ValueHandle LHS, + const char *Operator, + T RHS) { + Diag(Data->Loc, "signed integer overflow: " + "%0 %1 %2 cannot be represented in type %3") + << Value(Data->Type, LHS) << Operator << RHS << Data->Type; + Die(); +} + +void __ubsan::__ubsan_handle_add_overflow(OverflowData *Data, + ValueHandle LHS, ValueHandle RHS) { + HandleSignedOverflow(Data, LHS, "+", Value(Data->Type, RHS)); +} + +void __ubsan::__ubsan_handle_sub_overflow(OverflowData *Data, + ValueHandle LHS, ValueHandle RHS) { + HandleSignedOverflow(Data, LHS, "-", Value(Data->Type, RHS)); +} + +void __ubsan::__ubsan_handle_mul_overflow(OverflowData *Data, + ValueHandle LHS, ValueHandle RHS) { + HandleSignedOverflow(Data, LHS, "*", Value(Data->Type, RHS)); +} + +void __ubsan::__ubsan_handle_negate_overflow(OverflowData *Data, + ValueHandle OldVal) { + Diag(Data->Loc, "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; + Die(); +} + +void __ubsan::__ubsan_handle_divrem_overflow(OverflowData *Data, + ValueHandle LHS, ValueHandle RHS) { + Value LHSVal(Data->Type, LHS); + Value RHSVal(Data->Type, RHS); + if (RHSVal.isMinusOne()) + Diag(Data->Loc, "division of %0 by -1 cannot be represented in type %1") + << LHSVal << Data->Type; + else + Diag(Data->Loc, "division by zero"); + Die(); +} + +void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data, + ValueHandle LHS, + ValueHandle RHS) { + Value LHSVal(Data->LHSType, LHS); + Value RHSVal(Data->RHSType, RHS); + if (RHSVal.isNegative()) + Diag(Data->Loc, "shift exponent %0 is negative") << RHSVal; + else if (RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth()) + Diag(Data->Loc, "shift exponent %0 is too large for %1-bit type %2") + << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType; + else if (LHSVal.isNegative()) + Diag(Data->Loc, "left shift of negative value %0") << LHSVal; + else + Diag(Data->Loc, "left shift of %0 by %1 places cannot be represented " + "in type %2") << LHSVal << RHSVal << Data->LHSType; + Die(); +} + +void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) { + Diag(Data->Loc, "execution reached a __builtin_unreachable() call"); + Die(); +} + +void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) { + Diag(Data->Loc, "execution reached the end of a value-returning function " + "without returning a value"); + Die(); +} diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.h b/compiler-rt/lib/ubsan/ubsan_handlers.h new file mode 100644 index 000000000000..8b12bd8454b1 --- /dev/null +++ b/compiler-rt/lib/ubsan/ubsan_handlers.h @@ -0,0 +1,81 @@ +//===-- 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; +}; + +/// \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. +extern "C" void __ubsan_handle_type_mismatch(TypeMismatchData *Data, + ValueHandle Pointer); + +struct OverflowData { + SourceLocation Loc; + const TypeDescriptor &Type; +}; + +/// \brief Handle a signed integer addition overflow. +extern "C" void __ubsan_handle_add_overflow(OverflowData *Data, + ValueHandle LHS, + ValueHandle RHS); +/// \brief Handle a signed integer subtraction overflow. +extern "C" void __ubsan_handle_sub_overflow(OverflowData *Data, + ValueHandle LHS, + ValueHandle RHS); +/// \brief Handle a signed integer multiplication overflow. +extern "C" void __ubsan_handle_mul_overflow(OverflowData *Data, + ValueHandle LHS, + ValueHandle RHS); +/// \brief Handle a signed integer overflow for a unary negate operator. +extern "C" void __ubsan_handle_negate_overflow(OverflowData *Data, + ValueHandle OldVal); +/// \brief Handle an INT_MIN/-1 overflow or division by zero. +extern "C" void __ubsan_handle_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. +extern "C" void __ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data, + ValueHandle LHS, + ValueHandle RHS); + +struct UnreachableData { + SourceLocation Loc; +}; + +/// \brief Handle a __builtin_unreachable which is reached. +extern "C" void __ubsan_handle_builtin_unreachable(UnreachableData *Data); +/// \brief Handle reaching the end of a value-returning function. +extern "C" void __ubsan_handle_missing_return(UnreachableData *Data); + +} + +#endif // UBSAN_HANDLERS_H diff --git a/compiler-rt/lib/ubsan/ubsan_value.cc b/compiler-rt/lib/ubsan/ubsan_value.cc new file mode 100644 index 000000000000..a8819043c638 --- /dev/null +++ b/compiler-rt/lib/ubsan/ubsan_value.cc @@ -0,0 +1,75 @@ +//===-- 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(Val); +#ifdef HAVE_INT128_T + if (getType().getIntegerBitWidth() == 128) + return *reinterpret_cast(Val); +#endif + UNREACHABLE("unexpected bit width"); +} + +UIntMax Value::getUIntValue() const { + CHECK(getType().isUnsignedIntegerTy()); + if (isInlineInt()) + return Val; + if (getType().getIntegerBitWidth() == 64) + return *reinterpret_cast(Val); +#ifdef HAVE_INT128_T + if (getType().getIntegerBitWidth() == 128) + return *reinterpret_cast(Val); +#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). +long double 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(Val); + case 64: return *reinterpret_cast(Val); + case 80: return *reinterpret_cast(Val); + case 128: return *reinterpret_cast(Val); + } + UNREACHABLE("unexpected floating point bit width"); +} diff --git a/compiler-rt/lib/ubsan/ubsan_value.h b/compiler-rt/lib/ubsan/ubsan_value.h new file mode 100644 index 000000000000..d3e62853aecb --- /dev/null +++ b/compiler-rt/lib/ubsan/ubsan_value.h @@ -0,0 +1,171 @@ +//===-- 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. Other platforms should be easy to add, and +// probably work as-is. +#if !defined(__linux__) +#error "UBSan not supported for this platform!" +#endif + +#include "sanitizer_common/sanitizer_common.h" + +// FIXME: Move this out to a config header. +typedef __int128 s128; +typedef unsigned __int128 u128; +#define HAVE_INT128_T 1 + + +namespace __ubsan { + +/// \brief Largest integer types we support. +#ifdef HAVE_INT128_T +typedef s128 SIntMax; +typedef u128 UIntMax; +#else +typedef s64 SIntMax; +typedef u64 UIntMax; +#endif + + +/// \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(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 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 { + /// The name of the type, in a format suitable for including in diagnostics. + const char *TypeName; + + /// 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; + +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(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. + long double getFloatValue() const; +}; + +} // namespace __ubsan + +#endif // UBSAN_VALUE_H