forked from OSchip/llvm-project
Add a runtime diagnostics library for Clang's -fcatch-undefined-behavior.
llvm-svn: 165533
This commit is contained in:
parent
a88397d9ba
commit
68b3014cd3
|
@ -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.
|
||||
|
|
|
@ -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)
|
|
@ -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()
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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")
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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");
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue