[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