Index: lib/cfi/cfi.cc =================================================================== --- lib/cfi/cfi.cc +++ lib/cfi/cfi.cc @@ -307,7 +307,7 @@ #ifdef CFI_ENABLE_DIAG if (DiagData) { __ubsan_handle_cfi_check_fail( - reinterpret_cast<__ubsan::CFICheckFailData *>(DiagData), Addr); + reinterpret_cast<__ubsan::CFICheckFailData *>(DiagData), Addr, false); return; } #endif Index: lib/ubsan/ubsan_handlers.h =================================================================== --- lib/ubsan/ubsan_handlers.h +++ lib/ubsan/ubsan_handlers.h @@ -165,8 +165,8 @@ }; /// \brief Handle control flow integrity failures. -RECOVERABLE(cfi_check_fail, CFICheckFailData *Data, ValueHandle Function) - +RECOVERABLE(cfi_check_fail, CFICheckFailData *Data, ValueHandle Function, + uptr VtableIsValid) } #endif // UBSAN_HANDLERS_H Index: lib/ubsan/ubsan_handlers.cc =================================================================== --- lib/ubsan/ubsan_handlers.cc +++ lib/ubsan/ubsan_handlers.cc @@ -551,31 +551,33 @@ #ifdef UBSAN_CAN_USE_CXXABI SANITIZER_WEAK_ATTRIBUTE void HandleCFIBadType(CFICheckFailData *Data, ValueHandle Vtable, - ReportOptions Opts); + bool ValidVtable, ReportOptions Opts); #else static void HandleCFIBadType(CFICheckFailData *Data, ValueHandle Vtable, - ReportOptions Opts) { + bool ValidVtable, ReportOptions Opts) { Die(); } #endif } // namespace __ubsan void __ubsan::__ubsan_handle_cfi_check_fail(CFICheckFailData *Data, - ValueHandle Value) { + ValueHandle Value, + uptr ValidVtable) { GET_REPORT_OPTIONS(false); if (Data->CheckKind == CFITCK_ICall) handleCFIBadIcall(Data, Value, Opts); else - HandleCFIBadType(Data, Value, Opts); + HandleCFIBadType(Data, Value, ValidVtable, Opts); } void __ubsan::__ubsan_handle_cfi_check_fail_abort(CFICheckFailData *Data, - ValueHandle Value) { + ValueHandle Value, + uptr ValidVtable) { GET_REPORT_OPTIONS(true); if (Data->CheckKind == CFITCK_ICall) handleCFIBadIcall(Data, Value, Opts); else - HandleCFIBadType(Data, Value, Opts); + HandleCFIBadType(Data, Value, ValidVtable, Opts); Die(); } Index: lib/ubsan/ubsan_handlers_cxx.cc =================================================================== --- lib/ubsan/ubsan_handlers_cxx.cc +++ lib/ubsan/ubsan_handlers_cxx.cc @@ -90,7 +90,7 @@ namespace __ubsan { void HandleCFIBadType(CFICheckFailData *Data, ValueHandle Vtable, - ReportOptions Opts) { + bool ValidVtable, ReportOptions Opts) { SourceLocation Loc = Data->Loc.acquire(); ErrorType ET = ErrorType::CFIBadType; @@ -98,7 +98,9 @@ return; ScopedReport R(Opts, Loc, ET); - DynamicTypeInfo DTI = getDynamicTypeInfoFromVtable((void *)Vtable); + DynamicTypeInfo DTI = ValidVtable + ? getDynamicTypeInfoFromVtable((void *)Vtable) + : DynamicTypeInfo(0, 0, 0); const char *CheckKindStr; switch (Data->CheckKind) { 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,12 @@ // 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 + +// RUN: %clangxx_cfi_diag %s -o %t2 +// RUN: %t2 zero 2>&1 | FileCheck --check-prefix=CHECK-ZERO %s +// RUN: %t2 unaddressable 2>&1 | FileCheck --check-prefix=CHECK-UNADDR %s +// RUN: %t2 2>&1 | FileCheck --check-prefix=CHECK-TYPEINFO %s // REQUIRES: cxxabi @@ -15,18 +22,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); }