Add a runtime diagnostics library for Clang's -fcatch-undefined-behavior.

llvm-svn: 165533
This commit is contained in:
Richard Smith 2012-10-09 19:34:32 +00:00
parent a88397d9ba
commit 68b3014cd3
23 changed files with 1023 additions and 0 deletions

View File

@ -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.

View File

@ -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)

View File

@ -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()

View File

@ -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 <stdint.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: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
}

View File

@ -0,0 +1,10 @@
// RUN: %clang -fcatch-undefined-behavior %s -o %t && %t 2>&1 | FileCheck %s
#include <stdint.h>
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;
}

View File

@ -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;
}

View File

@ -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 <stdint.h>
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;
}

View File

@ -0,0 +1,14 @@
// RUN: %clang -fcatch-undefined-behavior %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: fatal error: signed integer overflow: 65535 * 32769 cannot be represented in type 'int'
(void)(uint16_t(0xffff) * uint16_t(0x8001));
}

View File

@ -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);
}

View File

@ -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 <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: 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
}

View File

@ -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 <stdint.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: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
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -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")

View File

@ -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 <stdio.h>
#include <unistd.h>
#include <limits.h>
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("<unknown>");
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, "<unknown>:");
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);
}

View File

@ -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

View File

@ -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<typename T> 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();
}

View File

@ -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

View File

@ -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<s64*>(Val);
#ifdef HAVE_INT128_T
if (getType().getIntegerBitWidth() == 128)
return *reinterpret_cast<s128*>(Val);
#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);
#ifdef HAVE_INT128_T
if (getType().getIntegerBitWidth() == 128)
return *reinterpret_cast<u128*>(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<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");
}

View File

@ -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<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.
long double getFloatValue() const;
};
} // namespace __ubsan
#endif // UBSAN_VALUE_H