diff --git a/compiler-rt/lib/ubsan/ubsan_diag.cpp b/compiler-rt/lib/ubsan/ubsan_diag.cpp index 3673e66539d0e103e9ed6bb59f331da97dbc1c57..51bb3dca0a0fe5f07f03df3f9b580217c486e17f 100644 --- a/compiler-rt/lib/ubsan/ubsan_diag.cpp +++ b/compiler-rt/lib/ubsan/ubsan_diag.cpp @@ -115,6 +115,16 @@ SymbolizedStack *__ubsan::getSymbolizedLocation(uptr PC) { return Symbolizer::GetOrInit()->SymbolizePC(PC); } +bool __ubsan::getSymbolizedData(uptr Addr, DataInfo *Info) { + InitAsStandaloneIfNecessary(); + return Symbolizer::GetOrInit()->SymbolizeData(Addr, Info); +} + +const char *__ubsan::demangle(const char *Name) { + InitAsStandaloneIfNecessary(); + return Symbolizer::GetOrInit()->Demangle(Name); +} + Diag &Diag::operator<<(const TypeDescriptor &V) { return AddArg(V.getTypeName()); } diff --git a/compiler-rt/lib/ubsan/ubsan_diag.h b/compiler-rt/lib/ubsan/ubsan_diag.h index b444e971b2283889e060473c1cde9f1f30adaa03..6b964643df1f9445e8b95cc8cb76895f544b931b 100644 --- a/compiler-rt/lib/ubsan/ubsan_diag.h +++ b/compiler-rt/lib/ubsan/ubsan_diag.h @@ -39,6 +39,8 @@ public: }; SymbolizedStack *getSymbolizedLocation(uptr PC); +bool getSymbolizedData(uptr Addr, DataInfo *Info); +const char *demangle(const char *Name); inline SymbolizedStack *getCallerLocation(uptr CallerPC) { CHECK(CallerPC); diff --git a/compiler-rt/lib/ubsan/ubsan_handlers_cxx.cpp b/compiler-rt/lib/ubsan/ubsan_handlers_cxx.cpp index 2a6d558de034297d84436192b50e9931b833dcd2..8f52952c36d30fce949af93559320965440bfe00 100644 --- a/compiler-rt/lib/ubsan/ubsan_handlers_cxx.cpp +++ b/compiler-rt/lib/ubsan/ubsan_handlers_cxx.cpp @@ -94,6 +94,31 @@ void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort( Die(); } +static bool StartsWith(const char *Str, const char *Pattern, uptr PatternLen) { + return internal_strncmp(Str, Pattern, PatternLen) == 0; +} +static bool EndsWith(const char *Str, uptr StrLen, const char *Pattern, uptr PatternLen) { + return (StrLen >= PatternLen) && + (internal_strcmp(Str + StrLen - PatternLen, Pattern) == 0); +} + +static const char *UndecorateCfiTypeIdSymbol(char *Str) { + const char StartPattern[] = "__typeid_"; + const char EndPattern[] = "_global_addr"; + auto StartPatternLen = sizeof(StartPattern) - 1; + auto EndPatternLen = sizeof(EndPattern) - 1; + + uptr Len = internal_strlen(Str); + if (Len > StartPatternLen + EndPatternLen && + StartsWith(Str, StartPattern, StartPatternLen) && + EndsWith(Str, Len, EndPattern, EndPatternLen)) { + Str[Len - EndPatternLen] = 0; + Str += StartPatternLen; + return common_flags()->demangle ? demangle(Str) : Str; + } + return nullptr; +} + namespace __ubsan { void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable, bool ValidVtable, ReportOptions Opts) { @@ -136,11 +161,26 @@ void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable, << Data->Type << CheckKindStr << (void *)Vtable; // If possible, say what type it actually points to. - if (!DTI.isValid()) - Diag(Vtable, DL_Note, ET, "invalid vtable"); - else + DataInfo VtableDataInfo; + if (DTI.isValid()) { Diag(Vtable, DL_Note, ET, "vtable is of type %0") << TypeName(DTI.getMostDerivedTypeName()); + } else if (getSymbolizedData(Vtable, &VtableDataInfo) && + VtableDataInfo.name) { + if (ValidVtable) { + const char *PrintedName = VtableDataInfo.name; + if (auto *Undecorated = UndecorateCfiTypeIdSymbol(VtableDataInfo.name)) + PrintedName = Undecorated; + Diag(Vtable, DL_Note, ET, "vtable CFI typeid is (or aliases) %0`%1") + << VtableDataInfo.module << PrintedName; + } else { + Diag(Vtable, DL_Note, ET, "invalid vtable (address points to %0`%1)") + << VtableDataInfo.module << VtableDataInfo.name; + } + } else { + Diag(Vtable, DL_Note, ET, "invalid vtable"); + } + VtableDataInfo.Clear(); // If the failure involved different DSOs for the check location and vtable, // report the DSO names. diff --git a/compiler-rt/test/cfi/no-cfi-vtable-diagnostic.cpp b/compiler-rt/test/cfi/no-cfi-vtable-diagnostic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b2b8b35c206b3438f283344f7e73c1d1ad8c0832 --- /dev/null +++ b/compiler-rt/test/cfi/no-cfi-vtable-diagnostic.cpp @@ -0,0 +1,31 @@ +// Check that diagnostics print the vtable symbol name, when it doesn't have CFI typeid + +// RUN: %clangxx -fno-sanitize=cfi -fno-lto -g -DSTATIC_LIB -c -o %t_static.o %s +// RUN: %clangxx_cfi_diag -g -o %t_exe_suffix %s %t_static.o +// RUN: %t_exe_suffix 2>&1 | FileCheck %s + +// REQUIRES: cxxabi + +struct S1 { + S1(); + virtual void f1(); +}; + +#ifdef STATIC_LIB + +S1::S1() = default; + +void S1::f1() {} + +#else + +int main() { + S1 *S = new S1(); + // CHECK: runtime error: control flow integrity check for type 'S1' failed during virtual call + // CHECK: note: invalid vtable (address points to {{.*}}vtable for S1) + S->f1(); // trigger cfi-vcall failure + + return 0; +} + +#endif // SHARED_LIB diff --git a/compiler-rt/test/cfi/no-rtti-diagnostic.cpp b/compiler-rt/test/cfi/no-rtti-diagnostic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5e04fc9c5149ef39c70fc53fb218e209f3767a88 --- /dev/null +++ b/compiler-rt/test/cfi/no-rtti-diagnostic.cpp @@ -0,0 +1,30 @@ +// Check that diagnostics print CFI typeid names when RTTI is disabled + +// RUN: %clangxx_cfi_diag -g -fno-rtti -o %t1 %s +// RUN: %t1 2>&1 | FileCheck %s + +// REQUIRES: cxxabi + +struct S1 { + virtual void f1() {} +}; + +struct S2 { + virtual void f2() {} +}; + +// One of the CFI typeids aliases all-vtables symbol, so trigger two checks +void f(S1 *S1p, S2 *S2p) { + // CHECK-DAG: runtime error: control flow integrity check for type 'S1' failed during cast to unrelated type + // CHECK-DAG: runtime error: control flow integrity check for type 'S2' failed during cast to unrelated type + // CHECK-DAG: vtable CFI typeid is (or aliases) {{.*}}typeinfo name for S + S2 *S2cast = reinterpret_cast(S1p); // trigger cfi-unrelated-cast failure + S1 *S1cast = reinterpret_cast(S2p); // trigger cfi-unrelated-cast failure +} + +int main() { + S1 S1v; + S2 S2v; + f(&S1v, &S2v); + return 0; +}