summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/cfi/cfi.cc2
-rw-r--r--lib/ubsan/ubsan_handlers.cc14
-rw-r--r--lib/ubsan/ubsan_handlers.h4
-rw-r--r--lib/ubsan/ubsan_handlers_cxx.cc17
-rw-r--r--test/cfi/cross-dso/target_out_of_bounds.cpp58
-rw-r--r--test/cfi/target_uninstrumented.cpp44
6 files changed, 112 insertions, 27 deletions
diff --git a/lib/cfi/cfi.cc b/lib/cfi/cfi.cc
index 55b2327e3..766a7b40d 100644
--- a/lib/cfi/cfi.cc
+++ b/lib/cfi/cfi.cc
@@ -307,7 +307,7 @@ ALWAYS_INLINE void CfiSlowPathCommon(u64 CallSiteTypeId, void *Ptr,
#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
diff --git a/lib/ubsan/ubsan_handlers.cc b/lib/ubsan/ubsan_handlers.cc
index 3b37af669..4ede388e0 100644
--- a/lib/ubsan/ubsan_handlers.cc
+++ b/lib/ubsan/ubsan_handlers.cc
@@ -551,31 +551,33 @@ namespace __ubsan {
#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();
}
diff --git a/lib/ubsan/ubsan_handlers.h b/lib/ubsan/ubsan_handlers.h
index 1d4c41529..e0cfd5b70 100644
--- a/lib/ubsan/ubsan_handlers.h
+++ b/lib/ubsan/ubsan_handlers.h
@@ -165,8 +165,8 @@ struct CFICheckFailData {
};
/// \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
diff --git a/lib/ubsan/ubsan_handlers_cxx.cc b/lib/ubsan/ubsan_handlers_cxx.cc
index 739b4ca4a..593b15e12 100644
--- a/lib/ubsan/ubsan_handlers_cxx.cc
+++ b/lib/ubsan/ubsan_handlers_cxx.cc
@@ -90,7 +90,7 @@ void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort(
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 @@ void HandleCFIBadType(CFICheckFailData *Data, ValueHandle Vtable,
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 @@ void HandleCFIBadType(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, "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
diff --git a/test/cfi/cross-dso/target_out_of_bounds.cpp b/test/cfi/cross-dso/target_out_of_bounds.cpp
index fbd664bce..6353f030a 100644
--- a/test/cfi/cross-dso/target_out_of_bounds.cpp
+++ b/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 <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mman.h>
struct A {
virtual void f();
@@ -15,18 +23,42 @@ struct A {
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<char *>(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: <memory cannot be printed>
+ // CHECK-UNADDR: runtime error: control flow integrity check for type 'A' failed during cast
+ // CHECK-UNADDR: note: invalid vtable
+ // CHECK-UNADDR: <memory cannot be printed>
+ } 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<A *>(p);
-
- // CHECK: untime error: control flow integrity check for type 'A' failed during virtual call
- pa->f();
+ pa = reinterpret_cast<A *>(p);
}
diff --git a/test/cfi/target_uninstrumented.cpp b/test/cfi/target_uninstrumented.cpp
new file mode 100644
index 000000000..2ec2b5bbc
--- /dev/null
+++ b/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 <stdio.h>
+#include <string.h>
+
+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