[compiler-rt] r335569 - Implement CFI for indirect calls via a member function pointer.
Peter Collingbourne via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 25 19:15:48 PDT 2018
Author: pcc
Date: Mon Jun 25 19:15:47 2018
New Revision: 335569
URL: http://llvm.org/viewvc/llvm-project?rev=335569&view=rev
Log:
Implement CFI for indirect calls via a member function pointer.
Similarly to CFI on virtual and indirect calls, this implementation
tries to use program type information to make the checks as precise
as possible. The basic way that it works is as follows, where `C`
is the name of the class being defined or the target of a call and
the function type is assumed to be `void()`.
For virtual calls:
- Attach type metadata to the addresses of function pointers in vtables
(not the functions themselves) of type `void (B::*)()` for each `B`
that is a recursive dynamic base class of `C`, including `C` itself.
This type metadata has an annotation that the type is for virtual
calls (to distinguish it from the non-virtual case).
- At the call site, check that the computed address of the function
pointer in the vtable has type `void (C::*)()`.
For non-virtual calls:
- Attach type metadata to each non-virtual member function whose address
can be taken with a member function pointer. The type of a function
in class `C` of type `void()` is each of the types `void (B::*)()`
where `B` is a most-base class of `C`. A most-base class of `C`
is defined as a recursive base class of `C`, including `C` itself,
that does not have any bases.
- At the call site, check that the function pointer has one of the types
`void (B::*)()` where `B` is a most-base class of `C`.
Differential Revision: https://reviews.llvm.org/D47567
Added:
compiler-rt/trunk/test/cfi/mfcall.cpp
Modified:
compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc
compiler-rt/trunk/lib/ubsan/ubsan_handlers.h
compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.cc
Modified: compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc?rev=335569&r1=335568&r2=335569&view=diff
==============================================================================
--- compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc (original)
+++ compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc Mon Jun 25 19:15:47 2018
@@ -637,7 +637,7 @@ void __ubsan::__ubsan_handle_pointer_ove
static void handleCFIBadIcall(CFICheckFailData *Data, ValueHandle Function,
ReportOptions Opts) {
- if (Data->CheckKind != CFITCK_ICall)
+ if (Data->CheckKind != CFITCK_ICall && Data->CheckKind != CFITCK_NVMFCall)
Die();
SourceLocation Loc = Data->Loc.acquire();
@@ -648,10 +648,12 @@ static void handleCFIBadIcall(CFICheckFa
ScopedReport R(Opts, Loc, ET);
+ const char *CheckKindStr = Data->CheckKind == CFITCK_NVMFCall
+ ? "non-virtual pointer to member function call"
+ : "indirect function call";
Diag(Loc, DL_Error, ET,
- "control flow integrity check for type %0 failed during "
- "indirect function call")
- << Data->Type;
+ "control flow integrity check for type %0 failed during %1")
+ << Data->Type << CheckKindStr;
SymbolizedStackHolder FLoc(getSymbolizedLocation(Function));
const char *FName = FLoc.get()->info.function;
@@ -693,7 +695,7 @@ void __ubsan::__ubsan_handle_cfi_check_f
ValueHandle Value,
uptr ValidVtable) {
GET_REPORT_OPTIONS(false);
- if (Data->CheckKind == CFITCK_ICall)
+ if (Data->CheckKind == CFITCK_ICall || Data->CheckKind == CFITCK_NVMFCall)
handleCFIBadIcall(Data, Value, Opts);
else
__ubsan_handle_cfi_bad_type(Data, Value, ValidVtable, Opts);
@@ -703,7 +705,7 @@ void __ubsan::__ubsan_handle_cfi_check_f
ValueHandle Value,
uptr ValidVtable) {
GET_REPORT_OPTIONS(true);
- if (Data->CheckKind == CFITCK_ICall)
+ if (Data->CheckKind == CFITCK_ICall || Data->CheckKind == CFITCK_NVMFCall)
handleCFIBadIcall(Data, Value, Opts);
else
__ubsan_handle_cfi_bad_type(Data, Value, ValidVtable, Opts);
Modified: compiler-rt/trunk/lib/ubsan/ubsan_handlers.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/ubsan_handlers.h?rev=335569&r1=335568&r2=335569&view=diff
==============================================================================
--- compiler-rt/trunk/lib/ubsan/ubsan_handlers.h (original)
+++ compiler-rt/trunk/lib/ubsan/ubsan_handlers.h Mon Jun 25 19:15:47 2018
@@ -181,6 +181,8 @@ enum CFITypeCheckKind : unsigned char {
CFITCK_DerivedCast,
CFITCK_UnrelatedCast,
CFITCK_ICall,
+ CFITCK_NVMFCall,
+ CFITCK_VMFCall,
};
struct CFICheckFailData {
Modified: compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.cc?rev=335569&r1=335568&r2=335569&view=diff
==============================================================================
--- compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.cc (original)
+++ compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.cc Mon Jun 25 19:15:47 2018
@@ -123,7 +123,11 @@ void __ubsan_handle_cfi_bad_type(CFIChec
case CFITCK_UnrelatedCast:
CheckKindStr = "cast to unrelated type";
break;
+ case CFITCK_VMFCall:
+ CheckKindStr = "virtual pointer to member function call";
+ break;
case CFITCK_ICall:
+ case CFITCK_NVMFCall:
Die();
}
Added: compiler-rt/trunk/test/cfi/mfcall.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/cfi/mfcall.cpp?rev=335569&view=auto
==============================================================================
--- compiler-rt/trunk/test/cfi/mfcall.cpp (added)
+++ compiler-rt/trunk/test/cfi/mfcall.cpp Mon Jun 25 19:15:47 2018
@@ -0,0 +1,94 @@
+// RUN: %clangxx_cfi -o %t %s
+// RUN: %expect_crash %run %t a
+// RUN: %expect_crash %run %t b
+// RUN: %expect_crash %run %t c
+// RUN: %expect_crash %run %t d
+// RUN: %expect_crash %run %t e
+// RUN: %run %t f
+// RUN: %run %t g
+
+// RUN: %clangxx_cfi_diag -o %t2 %s
+// RUN: %run %t2 a 2>&1 | FileCheck --check-prefix=A %s
+// RUN: %run %t2 b 2>&1 | FileCheck --check-prefix=B %s
+// RUN: %run %t2 c 2>&1 | FileCheck --check-prefix=C %s
+// RUN: %run %t2 d 2>&1 | FileCheck --check-prefix=D %s
+// RUN: %run %t2 e 2>&1 | FileCheck --check-prefix=E %s
+
+#include <assert.h>
+#include <string.h>
+
+struct SBase1 {
+ void b1() {}
+};
+
+struct SBase2 {
+ void b2() {}
+};
+
+struct S : SBase1, SBase2 {
+ void f1() {}
+ int f2() { return 1; }
+ virtual void g1() {}
+ virtual int g2() { return 1; }
+ virtual int g3() { return 1; }
+};
+
+struct T {
+ void f1() {}
+ int f2() { return 2; }
+ virtual void g1() {}
+ virtual int g2() { return 2; }
+ virtual void g3() {}
+};
+
+typedef void (S::*S_void)();
+
+typedef int (S::*S_int)();
+typedef int (T::*T_int)();
+
+template <typename To, typename From>
+To bitcast(From f) {
+ assert(sizeof(To) == sizeof(From));
+ To t;
+ memcpy(&t, &f, sizeof(f));
+ return t;
+}
+
+int main(int argc, char **argv) {
+ S s;
+ T t;
+
+ switch (argv[1][0]) {
+ case 'a':
+ // A: runtime error: control flow integrity check for type 'int (S::*)()' failed during non-virtual pointer to member function call
+ // A: note: S::f1() defined here
+ (s.*bitcast<S_int>(&S::f1))();
+ break;
+ case 'b':
+ // B: runtime error: control flow integrity check for type 'int (T::*)()' failed during non-virtual pointer to member function call
+ // B: note: S::f2() defined here
+ (t.*bitcast<T_int>(&S::f2))();
+ break;
+ case 'c':
+ // C: runtime error: control flow integrity check for type 'int (S::*)()' failed during virtual pointer to member function call
+ // C: note: vtable is of type 'S'
+ (s.*bitcast<S_int>(&S::g1))();
+ break;
+ case 'd':
+ // D: runtime error: control flow integrity check for type 'int (S::*)()' failed during virtual pointer to member function call
+ // D: note: vtable is of type 'T'
+ (reinterpret_cast<S &>(t).*&S::g2)();
+ break;
+ case 'e':
+ // E: runtime error: control flow integrity check for type 'void (S::*)()' failed during virtual pointer to member function call
+ // E: note: vtable is of type 'S'
+ (s.*bitcast<S_void>(&T::g3))();
+ break;
+ case 'f':
+ (s.*&SBase1::b1)();
+ break;
+ case 'g':
+ (s.*&SBase2::b2)();
+ break;
+ }
+}
More information about the llvm-commits
mailing list