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) { @@ -123,11 +125,16 @@ << Data->Type << CheckKindStr << (void *)Vtable; // If possible, say what type it actually points to. - if (!DTI.isValid()) - Diag(Vtable, DL_Note, "invalid vtable"); - else + if (!DTI.isValid()) { + const char *module = Symbolizer::GetOrInit()->GetModuleNameForPc(Vtable); + if (module) + Diag(Vtable, DL_Note, "invalid vtable in module %0") << module; + else + Diag(Vtable, DL_Note, "invalid vtable"); + } else { Diag(Vtable, DL_Note, "vtable is of type %0") << TypeName(DTI.getMostDerivedTypeName()); + } } } // namespace __ubsan 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: %clangxx_cfi_dso_diag -std=c++11 %s -o %t +// 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 -std=c++11 %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 @@ -7,6 +14,7 @@ #include #include #include +#include struct A { virtual void f(); @@ -15,18 +23,42 @@ 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) { + void *vtable = mmap(nullptr, 4096, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + // Create an object with a vtable in an unaddressable memory region. + *(uintptr_t *)p = (uintptr_t)vtable + 64; + // CHECK-UNADDR: runtime error: control flow integrity check for type 'A' failed during cast + // CHECK-UNADDR: note: invalid vtable + // CHECK-UNADDR: + // CHECK-UNADDR: runtime error: control flow integrity check for type 'A' failed during cast + // CHECK-UNADDR: 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 *vtable = calloc(1, 128); + *(uintptr_t *)p = (uintptr_t)vtable + 64; + // 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 *vtable = calloc(1, 128); + memset(vtable, 0xFE, 128); + *(uintptr_t *)p = (uintptr_t)vtable + 64; + // 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); } Index: test/cfi/target_uninstrumented.cpp =================================================================== --- /dev/null +++ test/cfi/target_uninstrumented.cpp @@ -0,0 +1,44 @@ +// RUN: %clangxx -g -DSHARED_LIB %s -fPIC -shared -o %T/target_uninstrumented-so.so +// RUN: %clangxx_cfi_diag -g %s -o %t %T/target_uninstrumented-so.so +// RUN: %t 2>&1 | FileCheck %s + +// REQUIRES: cxxabi + +#include +#include + +struct A { + virtual void f(); +}; + +void *create_B(); + +#ifdef SHARED_LIB + +struct B { + virtual void f(); +}; +void B::f() {} + +void *create_B() { + return (void *)(new B()); +} + +#else + +void A::f() {} + +int main(int argc, char *argv[]) { + void *p = create_B(); + // CHECK: runtime error: control flow integrity check for type 'A' failed during cast to unrelated type + // CHECK: invalid vtable in module {{.*}}target_uninstrumented-so.so + A *a = (A *)p; + memset(p, 0, sizeof(A)); + // CHECK: runtime error: control flow integrity check for type 'A' failed during cast to unrelated type + // CHECK-NOT: invalid vtable in module + // CHECK: invalid vtable + a = (A *)p; + // CHECK: done + fprintf(stderr, "done %p\n", a); +} +#endif