mirror of
https://github.com/darlinghq/darling-compiler-rt.git
synced 2024-11-30 07:10:42 +00:00
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:
parent
d3b30c45fa
commit
521a01f23d
@ -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()
|
||||
|
@ -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(..)
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
251
lib/ubsan/ubsan_type_hash_itanium.cc
Normal file
251
lib/ubsan/ubsan_type_hash_itanium.cc
Normal 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
|
72
lib/ubsan/ubsan_type_hash_win.cc
Normal file
72
lib/ubsan/ubsan_type_hash_win.cc
Normal 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
|
@ -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"
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user