CFI: Get check-cfi passing on Windows.

Specifically:

- Start using %expect_crash.

- Provide an implementation of __ubsan::getDynamicTypeInfoFromVtable
  for the Microsoft C++ ABI. This is all that is needed for CFI
  diagnostics; UBSan's -fsanitize=vptr also requires an implementation of
  __ubsan::checkDynamicType.

- Build the sanitizer runtimes against the release version of the C
  runtime, even in debug builds.

- Accommodate demangling differences in tests.

Differential Revision: http://reviews.llvm.org/D11029

git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@241745 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Peter Collingbourne 2015-07-08 22:10:34 +00:00
parent d3b30c45fa
commit 521a01f23d
19 changed files with 564 additions and 449 deletions

View File

@ -213,17 +213,16 @@ append_list_if(COMPILER_RT_HAS_FNO_FUNCTION_SECTIONS_FLAG -fno-function-sections
append_list_if(COMPILER_RT_HAS_FNO_LTO_FLAG -fno-lto SANITIZER_COMMON_CFLAGS)
if(MSVC)
# Replace the /MD[d] flags with /MT.
# Replace the /M[DT][d] flags with /MT, and strip any definitions of _DEBUG,
# which cause definition mismatches at link time.
# FIXME: In fact, sanitizers should support both /MT and /MD, see PR20214.
if(COMPILER_RT_HAS_MT_FLAG)
foreach(flag_var
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
if(${flag_var} MATCHES "/MD")
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
elseif(${flag_var} MATCHES "/MDd")
string(REGEX REPLACE "/MDd" "/MT" ${flag_var} "${${flag_var}}")
endif()
string(REGEX REPLACE "/M[DT]d" "/MT" ${flag_var} "${${flag_var}}")
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
string(REGEX REPLACE "/D_DEBUG" "" ${flag_var} "${${flag_var}}")
endforeach()
endif()
append_list_if(COMPILER_RT_HAS_Oy_FLAG /Oy- SANITIZER_COMMON_CFLAGS)
@ -333,9 +332,6 @@ if(APPLE AND SANITIZER_MIN_OSX_VERSION VERSION_LESS "10.9")
# Mac OS X prior to 10.9 had problems with exporting symbols from
# libc++/libc++abi.
set(SANITIZER_CAN_USE_CXXABI FALSE)
elseif(WIN32)
# We do not currently support the Microsoft C++ ABI.
set(SANITIZER_CAN_USE_CXXABI FALSE)
else()
set(SANITIZER_CAN_USE_CXXABI TRUE)
endif()

View File

@ -15,6 +15,8 @@ set(UBSAN_STANDALONE_SOURCES
set(UBSAN_CXX_SOURCES
ubsan_handlers_cxx.cc
ubsan_type_hash.cc
ubsan_type_hash_itanium.cc
ubsan_type_hash_win.cc
)
include_directories(..)

View File

@ -165,8 +165,12 @@ static void renderText(const char *Message, const Diag::Arg *Args) {
case Diag::AK_String:
Printf("%s", A.String);
break;
case Diag::AK_Mangled: {
Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String));
case Diag::AK_TypeName: {
if (SANITIZER_WINDOWS)
// The Windows implementation demangles names early.
Printf("'%s'", A.String);
else
Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String));
break;
}
case Diag::AK_SInt:

View File

@ -113,11 +113,11 @@ public:
const char *getText() const { return Text; }
};
/// \brief A mangled C++ name. Really just a strong typedef for 'const char*'.
class MangledName {
/// \brief A C++ type name. Really just a strong typedef for 'const char*'.
class TypeName {
const char *Name;
public:
MangledName(const char *Name) : Name(Name) {}
TypeName(const char *Name) : Name(Name) {}
const char *getName() const { return Name; }
};
@ -141,7 +141,7 @@ public:
/// Kinds of arguments, corresponding to members of \c Arg's union.
enum ArgKind {
AK_String, ///< A string argument, displayed as-is.
AK_Mangled,///< A C++ mangled name, demangled before display.
AK_TypeName,///< A C++ type name, possibly demangled before display.
AK_UInt, ///< An unsigned integer argument.
AK_SInt, ///< A signed integer argument.
AK_Float, ///< A floating-point argument.
@ -152,7 +152,7 @@ public:
struct Arg {
Arg() {}
Arg(const char *String) : Kind(AK_String), String(String) {}
Arg(MangledName MN) : Kind(AK_Mangled), String(MN.getName()) {}
Arg(TypeName TN) : Kind(AK_TypeName), String(TN.getName()) {}
Arg(UIntMax UInt) : Kind(AK_UInt), UInt(UInt) {}
Arg(SIntMax SInt) : Kind(AK_SInt), SInt(SInt) {}
Arg(FloatMax Float) : Kind(AK_Float), Float(Float) {}
@ -202,7 +202,7 @@ public:
~Diag();
Diag &operator<<(const char *Str) { return AddArg(Str); }
Diag &operator<<(MangledName MN) { return AddArg(MN); }
Diag &operator<<(TypeName TN) { return AddArg(TN); }
Diag &operator<<(unsigned long long V) { return AddArg(UIntMax(V)); }
Diag &operator<<(const void *V) { return AddArg(V); }
Diag &operator<<(const TypeDescriptor &V);

View File

@ -54,19 +54,19 @@ static void HandleDynamicTypeCacheMiss(
// If possible, say what type it actually points to.
if (!DTI.isValid())
Diag(Pointer, DL_Note, "object has invalid vptr")
<< MangledName(DTI.getMostDerivedTypeName())
<< TypeName(DTI.getMostDerivedTypeName())
<< Range(Pointer, Pointer + sizeof(uptr), "invalid vptr");
else if (!DTI.getOffset())
Diag(Pointer, DL_Note, "object is of type %0")
<< MangledName(DTI.getMostDerivedTypeName())
<< TypeName(DTI.getMostDerivedTypeName())
<< Range(Pointer, Pointer + sizeof(uptr), "vptr for %0");
else
// FIXME: Find the type at the specified offset, and include that
// in the note.
Diag(Pointer - DTI.getOffset(), DL_Note,
"object is base class subobject at offset %0 within object of type %1")
<< DTI.getOffset() << MangledName(DTI.getMostDerivedTypeName())
<< MangledName(DTI.getSubobjectTypeName())
<< DTI.getOffset() << TypeName(DTI.getMostDerivedTypeName())
<< TypeName(DTI.getSubobjectTypeName())
<< Range(Pointer, Pointer + sizeof(uptr),
"vptr for %2 base class of %1");
}
@ -104,7 +104,7 @@ static void HandleCFIBadType(CFIBadTypeData *Data, ValueHandle Vtable,
Diag(Vtable, DL_Note, "invalid vtable");
else
Diag(Vtable, DL_Note, "vtable is of type %0")
<< MangledName(DTI.getMostDerivedTypeName());
<< TypeName(DTI.getMostDerivedTypeName());
}
void __ubsan::__ubsan_handle_cfi_bad_type(CFIBadTypeData *Data,

View File

@ -11,6 +11,9 @@
// relationships. This file is only linked into C++ compilations, and is
// permitted to use language features which require a C++ ABI library.
//
// Most of the implementation lives in an ABI-specific source file
// (ubsan_type_hash_{itanium,win}.cc).
//
//===----------------------------------------------------------------------===//
#include "ubsan_platform.h"
@ -19,243 +22,13 @@
#include "sanitizer_common/sanitizer_common.h"
// The following are intended to be binary compatible with the definitions
// given in the Itanium ABI. We make no attempt to be ODR-compatible with
// those definitions, since existing ABI implementations aren't.
namespace std {
class type_info {
public:
virtual ~type_info();
const char *__type_name;
};
}
namespace __cxxabiv1 {
/// Type info for classes with no bases, and base class for type info for
/// classes with bases.
class __class_type_info : public std::type_info {
~__class_type_info() override;
};
/// Type info for classes with simple single public inheritance.
class __si_class_type_info : public __class_type_info {
public:
~__si_class_type_info() override;
const __class_type_info *__base_type;
};
class __base_class_type_info {
public:
const __class_type_info *__base_type;
long __offset_flags;
enum __offset_flags_masks {
__virtual_mask = 0x1,
__public_mask = 0x2,
__offset_shift = 8
};
};
/// Type info for classes with multiple, virtual, or non-public inheritance.
class __vmi_class_type_info : public __class_type_info {
public:
~__vmi_class_type_info() override;
unsigned int flags;
unsigned int base_count;
__base_class_type_info base_info[1];
};
}
namespace abi = __cxxabiv1;
// We implement a simple two-level cache for type-checking results. For each
// (vptr,type) pair, a hash is computed. This hash is assumed to be globally
// unique; if it collides, we will get false negatives, but:
// * such a collision would have to occur on the *first* bad access,
// * the probability of such a collision is low (and for a 64-bit target, is
// negligible), and
// * the vptr, and thus the hash, can be affected by ASLR, so multiple runs
// give better coverage.
//
// The first caching layer is a small hash table with no chaining; buckets are
// reused as needed. The second caching layer is a large hash table with open
// chaining. We can freely evict from either layer since this is just a cache.
//
// FIXME: Make these hash table accesses thread-safe. The races here are benign:
// assuming the unsequenced loads and stores don't misbehave too badly,
// the worst case is false negatives or poor cache behavior, not false
// positives or crashes.
/// Find a bucket to store the given hash value in.
static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) {
static const unsigned HashTableSize = 65537;
static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize];
unsigned First = (V & 65535) ^ 1;
unsigned Probe = First;
for (int Tries = 5; Tries; --Tries) {
if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V)
return &__ubsan_vptr_hash_set[Probe];
Probe += ((V >> 16) & 65535) + 1;
if (Probe >= HashTableSize)
Probe -= HashTableSize;
}
// FIXME: Pick a random entry from the probe sequence to evict rather than
// just taking the first.
return &__ubsan_vptr_hash_set[First];
}
/// A cache of recently-checked hashes. Mini hash table with "random" evictions.
__ubsan::HashValue
__ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize];
/// \brief Determine whether \p Derived has a \p Base base class subobject at
/// offset \p Offset.
static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
const abi::__class_type_info *Base,
sptr Offset) {
if (Derived->__type_name == Base->__type_name)
return Offset == 0;
if (const abi::__si_class_type_info *SI =
dynamic_cast<const abi::__si_class_type_info*>(Derived))
return isDerivedFromAtOffset(SI->__base_type, Base, Offset);
const abi::__vmi_class_type_info *VTI =
dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
if (!VTI)
// No base class subobjects.
return false;
// Look for a base class which is derived from \p Base at the right offset.
for (unsigned int base = 0; base != VTI->base_count; ++base) {
// FIXME: Curtail the recursion if this base can't possibly contain the
// given offset.
sptr OffsetHere = VTI->base_info[base].__offset_flags >>
abi::__base_class_type_info::__offset_shift;
if (VTI->base_info[base].__offset_flags &
abi::__base_class_type_info::__virtual_mask)
// For now, just punt on virtual bases and say 'yes'.
// FIXME: OffsetHere is the offset in the vtable of the virtual base
// offset. Read the vbase offset out of the vtable and use it.
return true;
if (isDerivedFromAtOffset(VTI->base_info[base].__base_type,
Base, Offset - OffsetHere))
return true;
}
return false;
}
/// \brief Find the derived-most dynamic base class of \p Derived at offset
/// \p Offset.
static const abi::__class_type_info *findBaseAtOffset(
const abi::__class_type_info *Derived, sptr Offset) {
if (!Offset)
return Derived;
if (const abi::__si_class_type_info *SI =
dynamic_cast<const abi::__si_class_type_info*>(Derived))
return findBaseAtOffset(SI->__base_type, Offset);
const abi::__vmi_class_type_info *VTI =
dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
if (!VTI)
// No base class subobjects.
return 0;
for (unsigned int base = 0; base != VTI->base_count; ++base) {
sptr OffsetHere = VTI->base_info[base].__offset_flags >>
abi::__base_class_type_info::__offset_shift;
if (VTI->base_info[base].__offset_flags &
abi::__base_class_type_info::__virtual_mask)
// FIXME: Can't handle virtual bases yet.
continue;
if (const abi::__class_type_info *Base =
findBaseAtOffset(VTI->base_info[base].__base_type,
Offset - OffsetHere))
return Base;
}
return 0;
}
namespace {
struct VtablePrefix {
/// The offset from the vptr to the start of the most-derived object.
/// This should never be greater than zero, and will usually be exactly
/// zero.
sptr Offset;
/// The type_info object describing the most-derived class type.
std::type_info *TypeInfo;
};
VtablePrefix *getVtablePrefix(void *Vtable) {
VtablePrefix *Vptr = reinterpret_cast<VtablePrefix*>(Vtable);
if (!Vptr)
return 0;
VtablePrefix *Prefix = Vptr - 1;
if (Prefix->Offset > 0 || !Prefix->TypeInfo)
// This can't possibly be a valid vtable.
return 0;
return Prefix;
}
}
bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
// A crash anywhere within this function probably means the vptr is corrupted.
// FIXME: Perform these checks more cautiously.
// Check whether this is something we've evicted from the cache.
HashValue *Bucket = getTypeCacheHashTableBucket(Hash);
if (*Bucket == Hash) {
__ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
return true;
}
void *VtablePtr = *reinterpret_cast<void **>(Object);
VtablePrefix *Vtable = getVtablePrefix(VtablePtr);
if (!Vtable)
return false;
// Check that this is actually a type_info object for a class type.
abi::__class_type_info *Derived =
dynamic_cast<abi::__class_type_info*>(Vtable->TypeInfo);
if (!Derived)
return false;
abi::__class_type_info *Base = (abi::__class_type_info*)Type;
if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset))
return false;
// Success. Cache this result.
__ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
*Bucket = Hash;
return true;
}
__ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfoFromObject(void *Object) {
void *VtablePtr = *reinterpret_cast<void **>(Object);
return getDynamicTypeInfoFromVtable(VtablePtr);
}
__ubsan::DynamicTypeInfo
__ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) {
VtablePrefix *Vtable = getVtablePrefix(VtablePtr);
if (!Vtable)
return DynamicTypeInfo(0, 0, 0);
const abi::__class_type_info *ObjectType = findBaseAtOffset(
static_cast<const abi::__class_type_info*>(Vtable->TypeInfo),
-Vtable->Offset);
return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset,
ObjectType ? ObjectType->__type_name : "<unknown>");
}
#endif // CAN_SANITIZE_UB

View File

@ -0,0 +1,251 @@
//===-- ubsan_type_hash_itanium.cc ----------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implementation of type hashing/lookup for Itanium C++ ABI.
//
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_platform.h"
#include "ubsan_platform.h"
#if CAN_SANITIZE_UB && !SANITIZER_WINDOWS
#include "ubsan_type_hash.h"
#include "sanitizer_common/sanitizer_common.h"
// The following are intended to be binary compatible with the definitions
// given in the Itanium ABI. We make no attempt to be ODR-compatible with
// those definitions, since existing ABI implementations aren't.
namespace std {
class type_info {
public:
virtual ~type_info();
const char *__type_name;
};
}
namespace __cxxabiv1 {
/// Type info for classes with no bases, and base class for type info for
/// classes with bases.
class __class_type_info : public std::type_info {
~__class_type_info() override;
};
/// Type info for classes with simple single public inheritance.
class __si_class_type_info : public __class_type_info {
public:
~__si_class_type_info() override;
const __class_type_info *__base_type;
};
class __base_class_type_info {
public:
const __class_type_info *__base_type;
long __offset_flags;
enum __offset_flags_masks {
__virtual_mask = 0x1,
__public_mask = 0x2,
__offset_shift = 8
};
};
/// Type info for classes with multiple, virtual, or non-public inheritance.
class __vmi_class_type_info : public __class_type_info {
public:
~__vmi_class_type_info() override;
unsigned int flags;
unsigned int base_count;
__base_class_type_info base_info[1];
};
}
namespace abi = __cxxabiv1;
// We implement a simple two-level cache for type-checking results. For each
// (vptr,type) pair, a hash is computed. This hash is assumed to be globally
// unique; if it collides, we will get false negatives, but:
// * such a collision would have to occur on the *first* bad access,
// * the probability of such a collision is low (and for a 64-bit target, is
// negligible), and
// * the vptr, and thus the hash, can be affected by ASLR, so multiple runs
// give better coverage.
//
// The first caching layer is a small hash table with no chaining; buckets are
// reused as needed. The second caching layer is a large hash table with open
// chaining. We can freely evict from either layer since this is just a cache.
//
// FIXME: Make these hash table accesses thread-safe. The races here are benign:
// assuming the unsequenced loads and stores don't misbehave too badly,
// the worst case is false negatives or poor cache behavior, not false
// positives or crashes.
/// Find a bucket to store the given hash value in.
static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) {
static const unsigned HashTableSize = 65537;
static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize];
unsigned First = (V & 65535) ^ 1;
unsigned Probe = First;
for (int Tries = 5; Tries; --Tries) {
if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V)
return &__ubsan_vptr_hash_set[Probe];
Probe += ((V >> 16) & 65535) + 1;
if (Probe >= HashTableSize)
Probe -= HashTableSize;
}
// FIXME: Pick a random entry from the probe sequence to evict rather than
// just taking the first.
return &__ubsan_vptr_hash_set[First];
}
/// \brief Determine whether \p Derived has a \p Base base class subobject at
/// offset \p Offset.
static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
const abi::__class_type_info *Base,
sptr Offset) {
if (Derived->__type_name == Base->__type_name)
return Offset == 0;
if (const abi::__si_class_type_info *SI =
dynamic_cast<const abi::__si_class_type_info*>(Derived))
return isDerivedFromAtOffset(SI->__base_type, Base, Offset);
const abi::__vmi_class_type_info *VTI =
dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
if (!VTI)
// No base class subobjects.
return false;
// Look for a base class which is derived from \p Base at the right offset.
for (unsigned int base = 0; base != VTI->base_count; ++base) {
// FIXME: Curtail the recursion if this base can't possibly contain the
// given offset.
sptr OffsetHere = VTI->base_info[base].__offset_flags >>
abi::__base_class_type_info::__offset_shift;
if (VTI->base_info[base].__offset_flags &
abi::__base_class_type_info::__virtual_mask)
// For now, just punt on virtual bases and say 'yes'.
// FIXME: OffsetHere is the offset in the vtable of the virtual base
// offset. Read the vbase offset out of the vtable and use it.
return true;
if (isDerivedFromAtOffset(VTI->base_info[base].__base_type,
Base, Offset - OffsetHere))
return true;
}
return false;
}
/// \brief Find the derived-most dynamic base class of \p Derived at offset
/// \p Offset.
static const abi::__class_type_info *findBaseAtOffset(
const abi::__class_type_info *Derived, sptr Offset) {
if (!Offset)
return Derived;
if (const abi::__si_class_type_info *SI =
dynamic_cast<const abi::__si_class_type_info*>(Derived))
return findBaseAtOffset(SI->__base_type, Offset);
const abi::__vmi_class_type_info *VTI =
dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
if (!VTI)
// No base class subobjects.
return 0;
for (unsigned int base = 0; base != VTI->base_count; ++base) {
sptr OffsetHere = VTI->base_info[base].__offset_flags >>
abi::__base_class_type_info::__offset_shift;
if (VTI->base_info[base].__offset_flags &
abi::__base_class_type_info::__virtual_mask)
// FIXME: Can't handle virtual bases yet.
continue;
if (const abi::__class_type_info *Base =
findBaseAtOffset(VTI->base_info[base].__base_type,
Offset - OffsetHere))
return Base;
}
return 0;
}
namespace {
struct VtablePrefix {
/// The offset from the vptr to the start of the most-derived object.
/// This should never be greater than zero, and will usually be exactly
/// zero.
sptr Offset;
/// The type_info object describing the most-derived class type.
std::type_info *TypeInfo;
};
VtablePrefix *getVtablePrefix(void *Vtable) {
VtablePrefix *Vptr = reinterpret_cast<VtablePrefix*>(Vtable);
if (!Vptr)
return 0;
VtablePrefix *Prefix = Vptr - 1;
if (Prefix->Offset > 0 || !Prefix->TypeInfo)
// This can't possibly be a valid vtable.
return 0;
return Prefix;
}
}
bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
// A crash anywhere within this function probably means the vptr is corrupted.
// FIXME: Perform these checks more cautiously.
// Check whether this is something we've evicted from the cache.
HashValue *Bucket = getTypeCacheHashTableBucket(Hash);
if (*Bucket == Hash) {
__ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
return true;
}
void *VtablePtr = *reinterpret_cast<void **>(Object);
VtablePrefix *Vtable = getVtablePrefix(VtablePtr);
if (!Vtable)
return false;
// Check that this is actually a type_info object for a class type.
abi::__class_type_info *Derived =
dynamic_cast<abi::__class_type_info*>(Vtable->TypeInfo);
if (!Derived)
return false;
abi::__class_type_info *Base = (abi::__class_type_info*)Type;
if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset))
return false;
// Success. Cache this result.
__ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
*Bucket = Hash;
return true;
}
__ubsan::DynamicTypeInfo
__ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) {
VtablePrefix *Vtable = getVtablePrefix(VtablePtr);
if (!Vtable)
return DynamicTypeInfo(0, 0, 0);
const abi::__class_type_info *ObjectType = findBaseAtOffset(
static_cast<const abi::__class_type_info*>(Vtable->TypeInfo),
-Vtable->Offset);
return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset,
ObjectType ? ObjectType->__type_name : "<unknown>");
}
#endif // CAN_SANITIZE_UB && !SANITIZER_WINDOWS

View File

@ -0,0 +1,72 @@
//===-- ubsan_type_hash_win.cc --------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implementation of type hashing/lookup for Microsoft C++ ABI.
//
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_platform.h"
#include "ubsan_platform.h"
#if CAN_SANITIZE_UB && SANITIZER_WINDOWS
#include "ubsan_type_hash.h"
#include "sanitizer_common/sanitizer_common.h"
#include <typeinfo>
#include <windows.h>
struct CompleteObjectLocator {
int is_image_relative;
int offset_to_top;
int vfptr_offset;
};
bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
// FIXME: Implement.
return false;
}
__ubsan::DynamicTypeInfo
__ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) {
// The virtual table may not have a complete object locator if the object
// was compiled without RTTI (i.e. we might be reading from some other global
// laid out before the virtual table), so we need to carefully validate each
// pointer dereference and perform sanity checks.
CompleteObjectLocator **obj_locator_ptr =
((CompleteObjectLocator**)VtablePtr)-1;
if (!IsAccessibleMemoryRange((uptr)obj_locator_ptr, sizeof(void*)))
return DynamicTypeInfo(0, 0, 0);
CompleteObjectLocator *obj_locator = *obj_locator_ptr;
if (!IsAccessibleMemoryRange((uptr)obj_locator,
sizeof(CompleteObjectLocator)+sizeof(void*)))
return DynamicTypeInfo(0, 0, 0);
std::type_info *tinfo;
if (obj_locator->is_image_relative == 1) {
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(obj_locator, &mbi, sizeof(mbi));
tinfo = (std::type_info*)(*(int*)(obj_locator+1) +
(char*)mbi.AllocationBase);
} else if (obj_locator->is_image_relative == 0)
tinfo = *(std::type_info**)(obj_locator+1);
else
// Probably not a complete object locator.
return DynamicTypeInfo(0, 0, 0);
if (!IsAccessibleMemoryRange((uptr)tinfo, sizeof(std::type_info)))
return DynamicTypeInfo(0, 0, 0);
// Okay, this is probably a std::type_info. Request its name.
// FIXME: Implement a base class search like we do for Itanium.
return DynamicTypeInfo(tinfo->name(), obj_locator->offset_to_top,
"<unknown>");
}
#endif // CAN_SANITIZE_UB && SANITIZER_WINDOWS

View File

@ -21,6 +21,11 @@ if(NOT COMPILER_RT_STANDALONE_BUILD)
LTO
)
endif()
if(WIN32 AND EXISTS ${CMAKE_SOURCE_DIR}/tools/lld)
list(APPEND CFI_TEST_DEPS
lld
)
endif()
endif()
add_lit_testsuite(check-cfi "Running the cfi regression tests"

View File

@ -1,32 +1,32 @@
// RUN: %clangxx_cfi -c -DTU1 -o %t1.o %s
// RUN: %clangxx_cfi -c -DTU2 -o %t2.o %S/../cfi/anon-namespace.cpp
// RUN: %clangxx_cfi -o %t %t1.o %t2.o
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -o %t1 %t1.o %t2.o
// RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -c -DTU1 -DB32 -o %t1.o %s
// RUN: %clangxx_cfi -c -DTU2 -DB32 -o %t2.o %S/../cfi/anon-namespace.cpp
// RUN: %clangxx_cfi -o %t %t1.o %t2.o
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -o %t2 %t1.o %t2.o
// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -c -DTU1 -DB64 -o %t1.o %s
// RUN: %clangxx_cfi -c -DTU2 -DB64 -o %t2.o %S/../cfi/anon-namespace.cpp
// RUN: %clangxx_cfi -o %t %t1.o %t2.o
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -o %t3 %t1.o %t2.o
// RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -c -DTU1 -DBM -o %t1.o %s
// RUN: %clangxx_cfi -c -DTU2 -DBM -o %t2.o %S/../cfi/anon-namespace.cpp
// RUN: %clangxx_cfi -o %t %t1.o %t2.o
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -o %t4 %t1.o %t2.o
// RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx -c -DTU1 -o %t1.o %s
// RUN: %clangxx -c -DTU2 -o %t2.o %S/../cfi/anon-namespace.cpp
// RUN: %clangxx -o %t %t1.o %t2.o
// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx -o %t5 %t1.o %t2.o
// RUN: %t5 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx_cfi_diag -c -DTU1 -o %t1.o %s
// RUN: %clangxx_cfi_diag -c -DTU2 -o %t2.o %S/../cfi/anon-namespace.cpp
// RUN: %clangxx_cfi_diag -o %t %t1.o %t2.o
// RUN: %t 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
// RUN: %clangxx_cfi_diag -o %t6 %t1.o %t2.o
// RUN: %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
// Tests that the CFI mechanism treats classes in the anonymous namespace in
// different translation units as having distinct identities. This is done by
@ -91,13 +91,13 @@ int main() {
fprintf(stderr, "1\n");
// CFI-DIAG: runtime error: control flow integrity check for type '(anonymous namespace)::B' failed during base-to-derived cast
// CFI-DIAG-NEXT: note: vtable is of type '(anonymous namespace)::B'
// CFI-DIAG-NEXT: note: vtable is of type '{{.*}}anonymous namespace{{.*}}::B'
// CFI-DIAG: runtime error: control flow integrity check for type '(anonymous namespace)::B' failed during virtual call
// CFI-DIAG-NEXT: note: vtable is of type '(anonymous namespace)::B'
// CFI-DIAG-NEXT: note: vtable is of type '{{.*}}anonymous namespace{{.*}}::B'
((B *)a)->f(); // UB here
// CFI-NOT: 2
// NCFI: 2
// CFI-NOT: {{^2$}}
// NCFI: {{^2$}}
fprintf(stderr, "2\n");
}

View File

@ -1,68 +1,68 @@
// RUN: %clangxx_cfi -o %t %s
// RUN: not --crash %t a 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: not --crash %t b 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: not --crash %t c 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %t d 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t e 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t f 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: not --crash %t g 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %t h 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %clangxx_cfi -o %t1 %s
// RUN: %expect_crash %t1 a 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %t1 b 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %t1 c 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %t1 d 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t1 e 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t1 f 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %expect_crash %t1 g 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %t1 h 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %clangxx_cfi -DB32 -o %t %s
// RUN: not --crash %t a 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: not --crash %t b 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: not --crash %t c 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %t d 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t e 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t f 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: not --crash %t g 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %t h 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %clangxx_cfi -DB32 -o %t2 %s
// RUN: %expect_crash %t2 a 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %t2 b 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %t2 c 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %t2 d 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t2 e 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t2 f 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %expect_crash %t2 g 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %t2 h 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %clangxx_cfi -DB64 -o %t %s
// RUN: not --crash %t a 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: not --crash %t b 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: not --crash %t c 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %t d 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t e 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t f 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: not --crash %t g 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %t h 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %clangxx_cfi -DB64 -o %t3 %s
// RUN: %expect_crash %t3 a 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %t3 b 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %t3 c 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %t3 d 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t3 e 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t3 f 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %expect_crash %t3 g 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %t3 h 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %clangxx_cfi -DBM -o %t %s
// RUN: not --crash %t a 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: not --crash %t b 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: not --crash %t c 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %t d 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t e 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t f 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: not --crash %t g 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %t h 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %clangxx_cfi -DBM -o %t4 %s
// RUN: %expect_crash %t4 a 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %t4 b 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %t4 c 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %t4 d 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t4 e 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t4 f 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %expect_crash %t4 g 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %t4 h 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %clangxx_cfi -fsanitize=cfi-cast-strict -o %t %s
// RUN: not --crash %t a 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: not --crash %t b 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: not --crash %t c 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: not --crash %t d 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: not --crash %t e 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: not --crash %t f 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: not --crash %t g 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: not --crash %t h 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %clangxx_cfi -fsanitize=cfi-cast-strict -o %t5 %s
// RUN: %expect_crash %t5 a 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %t5 b 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %t5 c 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %t5 d 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %t5 e 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %t5 f 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %t5 g 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %t5 h 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %clangxx -o %t %s
// RUN: %t a 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t b 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t c 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t d 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t e 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t f 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t g 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t h 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %clangxx -o %t6 %s
// RUN: %t6 a 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t6 b 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t6 c 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t6 d 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t6 e 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t6 f 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t6 g 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %t6 h 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %clangxx_cfi_diag -o %t %s
// RUN: %t a 2>&1 | FileCheck --check-prefix=CFI-DIAG-D %s
// RUN: %t b 2>&1 | FileCheck --check-prefix=CFI-DIAG-D %s
// RUN: %t c 2>&1 | FileCheck --check-prefix=CFI-DIAG-D %s
// RUN: %t g 2>&1 | FileCheck --check-prefix=CFI-DIAG-U %s
// RUN: %clangxx_cfi_diag -o %t7 %s
// RUN: %t7 a 2>&1 | FileCheck --check-prefix=CFI-DIAG-D %s
// RUN: %t7 b 2>&1 | FileCheck --check-prefix=CFI-DIAG-D %s
// RUN: %t7 c 2>&1 | FileCheck --check-prefix=CFI-DIAG-D %s
// RUN: %t7 g 2>&1 | FileCheck --check-prefix=CFI-DIAG-U %s
// Tests that the CFI enforcement detects bad casts.
@ -112,10 +112,10 @@ int main(int argc, char **argv) {
A a;
// CFI-DIAG-D: runtime error: control flow integrity check for type 'B' failed during base-to-derived cast
// CFI-DIAG-D-NEXT: note: vtable is of type 'A'
// CFI-DIAG-D-NEXT: note: vtable is of type '{{(struct )?}}A'
// CFI-DIAG-U: runtime error: control flow integrity check for type 'B' failed during cast to unrelated type
// CFI-DIAG-U-NEXT: note: vtable is of type 'A'
// CFI-DIAG-U-NEXT: note: vtable is of type '{{(struct )?}}A'
switch (argv[1][0]) {
case 'a':
@ -144,7 +144,7 @@ int main(int argc, char **argv) {
break;
}
// FAIL-NOT: 2
// PASS: 2
// FAIL-NOT: {{^2$}}
// PASS: {{^2$}}
fprintf(stderr, "2\n");
}

View File

@ -1,26 +1,26 @@
// RUN: %clangxx_cfi -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: not --crash %t x 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -o %t1 %s
// RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %expect_crash %t1 x 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB32 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: not --crash %t x 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB32 -o %t2 %s
// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %expect_crash %t2 x 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB64 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: not --crash %t x 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB64 -o %t3 %s
// RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %expect_crash %t3 x 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DBM -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: not --crash %t x 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DBM -o %t4 %s
// RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %expect_crash %t4 x 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx -o %t %s
// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %t x 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx -o %t5 %s
// RUN: %t5 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %t5 x 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx_cfi_diag -o %t %s
// RUN: %t 2>&1 | FileCheck --check-prefix=CFI-DIAG2 %s
// RUN: %t x 2>&1 | FileCheck --check-prefix=CFI-DIAG1 %s
// RUN: %clangxx_cfi_diag -o %t6 %s
// RUN: %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG2 %s
// RUN: %t6 x 2>&1 | FileCheck --check-prefix=CFI-DIAG1 %s
// Tests that the CFI mechanism is sensitive to multiple inheritance and only
// permits calls via virtual tables for the correct base class.
@ -77,16 +77,16 @@ int main(int argc, char **argv) {
if (argc > 1) {
A *a = c;
// CFI-DIAG1: runtime error: control flow integrity check for type 'B' failed during cast to unrelated type
// CFI-DIAG1-NEXT: note: vtable is of type 'C'
// CFI-DIAG1-NEXT: note: vtable is of type '{{(struct )?}}C'
((B *)a)->g(); // UB here
} else {
// CFI-DIAG2: runtime error: control flow integrity check for type 'A' failed during cast to unrelated type
// CFI-DIAG2-NEXT: note: vtable is of type 'C'
// CFI-DIAG2-NEXT: note: vtable is of type '{{(struct )?}}C'
B *b = c;
((A *)b)->f(); // UB here
}
// CFI-NOT: 2
// NCFI: 2
// CFI-NOT: {{^2$}}
// NCFI: {{^2$}}
fprintf(stderr, "2\n");
}

View File

@ -1,20 +1,20 @@
// RUN: %clangxx_cfi -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -o %t1 %s
// RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB32 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB32 -o %t2 %s
// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB64 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB64 -o %t3 %s
// RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DBM -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DBM -o %t4 %s
// RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx -o %t %s
// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx -o %t5 %s
// RUN: %t5 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx_cfi_diag -o %t %s
// RUN: %t 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
// RUN: %clangxx_cfi_diag -o %t6 %s
// RUN: %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
// Tests that the CFI mechanism crashes the program when making a non-virtual
// call to an object of the wrong class, by casting a pointer to such an object
@ -63,10 +63,10 @@ int main() {
fprintf(stderr, "1\n");
// CFI-DIAG: runtime error: control flow integrity check for type 'B' failed during non-virtual call
// CFI-DIAG-NEXT: note: vtable is of type 'A'
// CFI-DIAG-NEXT: note: vtable is of type '{{(struct )?}}A'
((B *)a)->f(); // UB here
// CFI-NOT: 2
// NCFI: 2
// CFI-NOT: {{^2$}}
// NCFI: {{^2$}}
fprintf(stderr, "2\n");
}

View File

@ -1,20 +1,20 @@
// RUN: %clangxx_cfi -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -o %t1 %s
// RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB32 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB32 -o %t2 %s
// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB64 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB64 -o %t3 %s
// RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DBM -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DBM -o %t4 %s
// RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx -o %t %s
// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx -o %t5 %s
// RUN: %t5 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx_cfi_diag -o %t %s
// RUN: %t 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
// RUN: %clangxx_cfi_diag -o %t6 %s
// RUN: %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
// Tests that the CFI mechanism crashes the program when a virtual table is
// replaced with a compatible table of function pointers that does not belong to
@ -68,7 +68,7 @@ int main() {
// CFI-DIAG-NEXT: note: invalid vtable
a->f();
// CFI-NOT: 2
// NCFI: 2
// CFI-NOT: {{^2$}}
// NCFI: {{^2$}}
fprintf(stderr, "2\n");
}

View File

@ -1,19 +1,19 @@
// XFAIL: *
// RUN: %clangxx_cfi -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -o %t1 %s
// RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB32 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB32 -o %t2 %s
// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB64 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB64 -o %t3 %s
// RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DBM -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DBM -o %t4 %s
// RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx -o %t %s
// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx -o %t5 %s
// RUN: %t5 2>&1 | FileCheck --check-prefix=NCFI %s
// Tests that the CFI enforcement distinguishes betwen non-overriding siblings.
// XFAILed as not implemented yet.

View File

@ -1,56 +1,56 @@
// RUN: %clangxx_cfi -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -o %t1 %s
// RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB32 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB32 -o %t2 %s
// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB64 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB64 -o %t3 %s
// RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DBM -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DBM -o %t4 %s
// RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O1 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O1 -o %t5 %s
// RUN: %expect_crash %t5 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O1 -DB32 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O1 -DB32 -o %t6 %s
// RUN: %expect_crash %t6 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O1 -DB64 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O1 -DB64 -o %t7 %s
// RUN: %expect_crash %t7 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O1 -DBM -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O1 -DBM -o %t8 %s
// RUN: %expect_crash %t8 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O2 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O2 -o %t9 %s
// RUN: %expect_crash %t9 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O2 -DB32 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O2 -DB32 -o %t10 %s
// RUN: %expect_crash %t10 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O2 -DB64 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O2 -DB64 -o %t11 %s
// RUN: %expect_crash %t11 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O2 -DBM -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O2 -DBM -o %t12 %s
// RUN: %expect_crash %t12 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O3 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O3 -o %t13 %s
// RUN: %expect_crash %t13 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O3 -DB32 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O3 -DB32 -o %t14 %s
// RUN: %expect_crash %t14 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O3 -DB64 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O3 -DB64 -o %t15 %s
// RUN: %expect_crash %t15 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O3 -DBM -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O3 -DBM -o %t16 %s
// RUN: %expect_crash %t16 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi_diag -o %t %s
// RUN: %t 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
// RUN: %clangxx_cfi_diag -o %t17 %s
// RUN: %t17 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
// RUN: %clangxx -o %t %s
// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx -o %t18 %s
// RUN: %t18 2>&1 | FileCheck --check-prefix=NCFI %s
// Tests that the CFI mechanism crashes the program when making a virtual call
// to an object of the wrong class but with a compatible vtable, by casting a
@ -97,12 +97,12 @@ int main() {
fprintf(stderr, "1\n");
// CFI-DIAG: runtime error: control flow integrity check for type 'B' failed during cast to unrelated type
// CFI-DIAG-NEXT: note: vtable is of type 'A'
// CFI-DIAG-NEXT: note: vtable is of type '{{(struct )?}}A'
// CFI-DIAG: runtime error: control flow integrity check for type 'B' failed during virtual call
// CFI-DIAG-NEXT: note: vtable is of type 'A'
// CFI-DIAG-NEXT: note: vtable is of type '{{(struct )?}}A'
((B *)a)->f(); // UB here
// CFI-NOT: 2
// NCFI: 2
// CFI-NOT: {{^2$}}
// NCFI: {{^2$}}
fprintf(stderr, "2\n");
}

View File

@ -1,20 +1,20 @@
// RUN: %clangxx_cfi -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -o %t1 %s
// RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB32 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB32 -o %t2 %s
// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB64 -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB64 -o %t3 %s
// RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DBM -o %t %s
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DBM -o %t4 %s
// RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx -o %t %s
// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx -o %t5 %s
// RUN: %t5 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx_cfi_diag -o %t %s
// RUN: %t 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
// RUN: %clangxx_cfi_diag -o %t6 %s
// RUN: %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
// Tests that the CFI enforcement also applies to virtual destructor calls made
// via 'delete'.
@ -60,10 +60,10 @@ int main() {
fprintf(stderr, "1\n");
// CFI-DIAG: runtime error: control flow integrity check for type 'B' failed during virtual call
// CFI-DIAG-NEXT: note: vtable is of type 'A'
// CFI-DIAG-NEXT: note: vtable is of type '{{(struct )?}}A'
delete (B *)a; // UB here
// CFI-NOT: 2
// NCFI: 2
// CFI-NOT: {{^2$}}
// NCFI: {{^2$}}
fprintf(stderr, "2\n");
}

View File

@ -121,13 +121,20 @@ def is_linux_lto_supported():
return True
if sys.platform == 'darwin' and is_darwin_lto_supported():
def is_windows_lto_supported():
return os.path.exists(os.path.join(config.llvm_tools_dir, 'lld-link2.exe'))
if config.host_os == 'Darwin' and is_darwin_lto_supported():
config.lto_supported = True
config.lto_launch = ["env", "DYLD_LIBRARY_PATH=" + config.llvm_shlib_dir]
config.lto_flags = []
elif sys.platform.startswith('linux') and is_linux_lto_supported():
elif config.host_os == 'Linux' and is_linux_lto_supported():
config.lto_supported = True
config.lto_launch = []
config.lto_flags = ["-fuse-ld=gold"]
elif config.host_os == 'Windows' and is_windows_lto_supported():
config.lto_supported = True
config.lto_launch = []
config.lto_flags = ["-fuse-ld=lld-link2"]
else:
config.lto_supported = False

View File

@ -54,6 +54,11 @@ config.suffixes = ['.c', '.cc', '.cpp']
if config.host_os not in ['Linux', 'Darwin', 'FreeBSD', 'Windows']:
config.unsupported = True
if config.host_os == 'Windows':
# We do not currently support enough of the Microsoft ABI for UBSan to work on
# Windows.
config.available_features.remove('cxxabi')
# Allow tests to use REQUIRES=stable-runtime. For use when you cannot use XFAIL
# because the test hangs or fails on one configuration and not the other.
if config.target_arch.startswith('arm') == False: