Index: lib/ubsan/ubsan_type_hash_itanium.cc =================================================================== --- lib/ubsan/ubsan_type_hash_itanium.cc +++ lib/ubsan/ubsan_type_hash_itanium.cc @@ -155,9 +155,20 @@ if (!Offset) return Derived; + if (!IsAccessibleMemoryRange((uptr)Derived, sizeof(*Derived))) + return 0; + + uptr *DerivedVtable = *(uptr **)Derived; + if (!IsAccessibleMemoryRange((uptr)(DerivedVtable - 2), 2 * sizeof(uptr))) + return 0; + if (const abi::__si_class_type_info *SI = - dynamic_cast(Derived)) + dynamic_cast(Derived)) { + if (!IsAccessibleMemoryRange((uptr)&SI->__base_type, + sizeof(SI->__base_type))) + return 0; return findBaseAtOffset(SI->__base_type, Offset); + } const abi::__vmi_class_type_info *VTI = dynamic_cast(Derived); @@ -165,6 +176,13 @@ // No base class subobjects. return 0; + if (!IsAccessibleMemoryRange((uptr)VTI, sizeof(*VTI))) + return 0; + + if (!IsAccessibleMemoryRange((uptr)VTI->base_info, + sizeof(VTI->base_info[0]) * VTI->base_count)) + 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; @@ -191,11 +209,13 @@ /// The type_info object describing the most-derived class type. std::type_info *TypeInfo; }; -VtablePrefix *getVtablePrefix(void *Vtable) { +VtablePrefix *getVtablePrefix(void *Vtable, bool Safe) { VtablePrefix *Vptr = reinterpret_cast(Vtable); if (!Vptr) return 0; VtablePrefix *Prefix = Vptr - 1; + if (Safe && !IsAccessibleMemoryRange((uptr)Prefix, sizeof(VtablePrefix))) + return 0; if (!Prefix->TypeInfo) // This can't possibly be a valid vtable. return 0; @@ -216,7 +236,7 @@ } void *VtablePtr = *reinterpret_cast(Object); - VtablePrefix *Vtable = getVtablePrefix(VtablePtr); + VtablePrefix *Vtable = getVtablePrefix(VtablePtr, false /* Safe */); if (!Vtable) return false; @@ -238,13 +258,18 @@ __ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) { - VtablePrefix *Vtable = getVtablePrefix(VtablePtr); + VtablePrefix *Vtable = getVtablePrefix(VtablePtr, true /* Safe */); if (!Vtable) return DynamicTypeInfo(0, 0, 0); const abi::__class_type_info *ObjectType = findBaseAtOffset( static_cast(Vtable->TypeInfo), -Vtable->Offset); - return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset, + const char *most_derived_type_name = + IsAccessibleMemoryRange((uptr)&Vtable->TypeInfo->__type_name, + sizeof(char *)) + ? Vtable->TypeInfo->__type_name + : nullptr; + return DynamicTypeInfo(most_derived_type_name, -Vtable->Offset, ObjectType ? ObjectType->__type_name : ""); } Index: test/cfi/cross-dso/target_out_of_bounds.cpp =================================================================== --- test/cfi/cross-dso/target_out_of_bounds.cpp +++ test/cfi/cross-dso/target_out_of_bounds.cpp @@ -1,5 +1,7 @@ // RUN: %clangxx_cfi_dso_diag %s -o %t -// RUN: %expect_crash %t 2>&1 | FileCheck %s +// RUN: %t zero 2>&1 | FileCheck --check-prefix=CHECK-ZERO %s +// RUN: %t unaddressable 2>&1 | FileCheck --check-prefix=CHECK-UNADDR %s +// RUN: %t 2>&1 | FileCheck --check-prefix=CHECK-TYPEINFO %s // REQUIRES: cxxabi @@ -15,18 +17,45 @@ void A::f() {} int main(int argc, char *argv[]) { - // Create an object with a vtable outside of any known DSO, but still in an - // addressable area. Current implementation of handlers in UBSan is not robust - // enough to handle unaddressable vtables. TODO: fix this. - void *empty = calloc(1, 128); - uintptr_t v = (uintptr_t)empty + 64; char *volatile p = reinterpret_cast(new A()); - for (uintptr_t *q = (uintptr_t *)p; q < (uintptr_t *)(p + sizeof(A)); ++q) - *q = v; + if (argc > 1 && strcmp(argv[1], "unaddressable") == 0) { + // Create an object with a vtable in an unaddressable memory region. + memset(p, 0xFE, sizeof(A)); + // CHECK-UNADDR: runtime error: control flow integrity check for type 'A' failed during cast + // CHECK-UNADDR: fefefefe{{.*}}: note: invalid vtable + // CHECK-UNADDR: + // CHECK-UNADDR: runtime error: control flow integrity check for type 'A' failed during cast + // CHECK-UNADDR: fefefefe{{.*}}: note: invalid vtable + // CHECK-UNADDR: + } else if (argc > 1 && strcmp(argv[1], "zero") == 0) { + // Create an object with a vtable outside of any known DSO, but still in an + // addressable area. + void *empty = calloc(1, 128); + uintptr_t v = (uintptr_t)empty + 64; + for (uintptr_t *q = (uintptr_t *)p; q < (uintptr_t *)(p + sizeof(A)); ++q) + *q = v; + // CHECK-ZERO: runtime error: control flow integrity check for type 'A' failed during cast + // CHECK-ZERO: note: invalid vtable + // CHECK-ZERO: 00 00 00 00 00 00 00 00 + // CHECK-ZERO: runtime error: control flow integrity check for type 'A' failed during cast + // CHECK-ZERO: note: invalid vtable + // CHECK-ZERO: 00 00 00 00 00 00 00 00 + } else { + // Create an object with a seemingly fine vtable, but with an unaddressable + // typeinfo pointer. + void *empty = calloc(1, 128); + memset(empty, 0xFE, 128); + uintptr_t v = (uintptr_t)empty + 64; + for (uintptr_t *q = (uintptr_t *)p; q < (uintptr_t *)(p + sizeof(A)); ++q) + *q = v; + // CHECK-TYPEINFO: runtime error: control flow integrity check for type 'A' failed during cast + // CHECK-TYPEINFO: note: invalid vtable + // CHECK-TYPEINFO: fe fe fe fe fe fe fe fe + // CHECK-TYPEINFO: runtime error: control flow integrity check for type 'A' failed during cast + // CHECK-TYPEINFO: note: invalid vtable + // CHECK-TYPEINFO: fe fe fe fe fe fe fe fe + } - // CHECK: runtime error: control flow integrity check for type 'A' failed during cast A *volatile pa = reinterpret_cast(p); - - // CHECK: untime error: control flow integrity check for type 'A' failed during virtual call - pa->f(); + pa = reinterpret_cast(p); }