[PATCH] D47567: Implement CFI for indirect calls via a member function pointer.

2018-06-25 Thread Peter Collingbourne via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rC335569: Implement CFI for indirect calls via a member 
function pointer. (authored by pcc, committed by ).

Changed prior to commit:
  https://reviews.llvm.org/D47567?vs=151272&id=152819#toc

Repository:
  rC Clang

https://reviews.llvm.org/D47567

Files:
  docs/ControlFlowIntegrity.rst
  docs/LTOVisibility.rst
  include/clang/Basic/Sanitizers.def
  lib/CodeGen/CGClass.cpp
  lib/CodeGen/CGVTables.cpp
  lib/CodeGen/CodeGenFunction.h
  lib/CodeGen/CodeGenModule.cpp
  lib/CodeGen/CodeGenModule.h
  lib/CodeGen/ItaniumCXXABI.cpp
  lib/Driver/SanitizerArgs.cpp
  lib/Driver/ToolChains/MSVC.cpp
  test/CodeGenCXX/cfi-mfcall-incomplete.cpp
  test/CodeGenCXX/cfi-mfcall.cpp
  test/CodeGenCXX/type-metadata-memfun.cpp
  test/CodeGenCXX/type-metadata.cpp
  test/Driver/fsanitize.c

Index: lib/CodeGen/CGClass.cpp
===
--- lib/CodeGen/CGClass.cpp
+++ lib/CodeGen/CGClass.cpp
@@ -2688,7 +2688,9 @@
 SSK = llvm::SanStat_CFI_UnrelatedCast;
 break;
   case CFITCK_ICall:
-llvm_unreachable("not expecting CFITCK_ICall");
+  case CFITCK_NVMFCall:
+  case CFITCK_VMFCall:
+llvm_unreachable("unexpected sanitizer kind");
   }
 
   std::string TypeName = RD->getQualifiedNameAsString();
Index: lib/CodeGen/CodeGenModule.cpp
===
--- lib/CodeGen/CodeGenModule.cpp
+++ lib/CodeGen/CodeGenModule.cpp
@@ -1132,6 +1132,34 @@
   return true;
 }
 
+static bool requiresMemberFunctionPointerTypeMetadata(CodeGenModule &CGM,
+  const CXXMethodDecl *MD) {
+  // Check that the type metadata can ever actually be used by a call.
+  if (!CGM.getCodeGenOpts().LTOUnit ||
+  !CGM.HasHiddenLTOVisibility(MD->getParent()))
+return false;
+
+  // Only functions whose address can be taken with a member function pointer
+  // need this sort of type metadata.
+  return !MD->isStatic() && !MD->isVirtual() && !isa(MD) &&
+ !isa(MD);
+}
+
+std::vector
+CodeGenModule::getMostBaseClasses(const CXXRecordDecl *RD) {
+  llvm::SetVector MostBases;
+
+  std::function CollectMostBases;
+  CollectMostBases = [&](const CXXRecordDecl *RD) {
+if (RD->getNumBases() == 0)
+  MostBases.insert(RD);
+for (const CXXBaseSpecifier &B : RD->bases())
+  CollectMostBases(B.getType()->getAsCXXRecordDecl());
+  };
+  CollectMostBases(RD);
+  return MostBases.takeVector();
+}
+
 void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
llvm::Function *F) {
   llvm::AttrBuilder B;
@@ -1257,7 +1285,20 @@
   // In the cross-dso CFI mode, we want !type attributes on definitions only.
   if (CodeGenOpts.SanitizeCfiCrossDso)
 if (auto *FD = dyn_cast(D))
-  CreateFunctionTypeMetadata(FD, F);
+  CreateFunctionTypeMetadataForIcall(FD, F);
+
+  // Emit type metadata on member functions for member function pointer checks.
+  // These are only ever necessary on definitions; we're guaranteed that the
+  // definition will be present in the LTO unit as a result of LTO visibility.
+  auto *MD = dyn_cast(D);
+  if (MD && requiresMemberFunctionPointerTypeMetadata(*this, MD)) {
+for (const CXXRecordDecl *Base : getMostBaseClasses(MD->getParent())) {
+  llvm::Metadata *Id =
+  CreateMetadataIdentifierForType(Context.getMemberPointerType(
+  MD->getType(), Context.getRecordType(Base).getTypePtr()));
+  F->addTypeMetadata(0, Id);
+}
+  }
 }
 
 void CodeGenModule::SetCommonAttributes(GlobalDecl GD, llvm::GlobalValue *GV) {
@@ -1378,13 +1419,14 @@
 GV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
 }
 
-void CodeGenModule::CreateFunctionTypeMetadata(const FunctionDecl *FD,
-   llvm::Function *F) {
+void CodeGenModule::CreateFunctionTypeMetadataForIcall(const FunctionDecl *FD,
+   llvm::Function *F) {
   // Only if we are checking indirect calls.
   if (!LangOpts.Sanitize.has(SanitizerKind::CFIICall))
 return;
 
-  // Non-static class methods are handled via vtable pointer checks elsewhere.
+  // Non-static class methods are handled via vtable or member function pointer
+  // checks elsewhere.
   if (isa(FD) && !cast(FD)->isStatic())
 return;
 
@@ -1476,7 +1518,7 @@
   // Don't emit entries for function declarations in the cross-DSO mode. This
   // is handled with better precision by the receiving DSO.
   if (!CodeGenOpts.SanitizeCfiCrossDso)
-CreateFunctionTypeMetadata(FD, F);
+CreateFunctionTypeMetadataForIcall(FD, F);
 
   if (getLangOpts().OpenMP && FD->hasAttr())
 getOpenMPRuntime().emitDeclareSimdFunction(FD, F);
@@ -4925,15 +4967,18 @@
   }
 }
 
-llvm::Metadata *CodeGenModule::CreateMetadataIdentifierForType(QualType T) {
-  llvm::Metadata *&InternalId =

[PATCH] D47567: Implement CFI for indirect calls via a member function pointer.

2018-06-25 Thread Peter Collingbourne via Phabricator via cfe-commits
pcc added inline comments.



Comment at: clang/lib/CodeGen/CodeGenModule.cpp:1413
+  FD->getType(), Context.getRecordType(Base).getTypePtr()));
+  F->addTypeMetadata(0, Id);
+}

vlad.tsyrklevich wrote:
> It'd be nice to have a test that reaches this.
I'll add one.



Comment at: clang/lib/CodeGen/CodeGenModule.h:1256
 
+  std::vector
+  getMostBaseClasses(const CXXRecordDecl *RD);

vlad.tsyrklevich wrote:
> Could be helpful to have a comment here to ensure there is no confusion 
> interpreting this as 'the most-base classes' and not 'most of the base 
> classes'.
Will do.



Comment at: clang/lib/Driver/SanitizerArgs.cpp:233
+ options::OPT_fno_sanitize_cfi_cross_dso, false);
+  if (CfiCrossDso)
+Supported &= ~CFIMFCall;

vlad.tsyrklevich wrote:
> This will cause supplying both options to fail with `clang: error: 
> unsupported option '-fsanitize=cfi-mfcall' for target ...`. Having it error 
> out the same way as type generalization below where it states that 
> cfi-cross-dso is unsupported with cfi-mfcall seems like a more helpful error.
The issue with that is that it would cause the flag combination `-fsanitize=cfi 
-fsanitize-cfi-cross-dso` to fail with an unsupported error. So I think it 
would need to work in a similar way as with the supported sanitizers. I guess I 
could add something after line 293 below instead.



Comment at: clang/test/CodeGenCXX/type-metadata.cpp:281
 // ITANIUM: [[FA_ID]] = distinct !{}
 
 // MS: [[A8]] = !{i64 8, !"?AUA@@"}

vlad.tsyrklevich wrote:
> Any reason not to include AF64/CF64/FAF16 here?
No, I just forgot to add them. I'll do that.



Comment at: compiler-rt/lib/ubsan/ubsan_handlers.cc:645
+  const char *CheckKindStr = Data->CheckKind == CFITCK_NVMFCall
+ ? "non-virtual member function call"
+ : "indirect function call";

vlad.tsyrklevich wrote:
> s/member/pointer to member/ ?
Makes sense.



Comment at: compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc:126
+  case CFITCK_VMFCall:
+CheckKindStr = "virtual member function call";
+break;

vlad.tsyrklevich wrote:
> s/member/pointer to member/ ?
Ditto


https://reviews.llvm.org/D47567



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D47567: Implement CFI for indirect calls via a member function pointer.

2018-06-25 Thread Vlad Tsyrklevich via Phabricator via cfe-commits
vlad.tsyrklevich accepted this revision.
vlad.tsyrklevich added a comment.
This revision is now accepted and ready to land.

I think it would be clearer to replace uses of 'member function pointer' with 
'pointer to member function'; however, a google search shows that the usage of 
both terms is basically the same so not this might be just be my own bias 
coming through.




Comment at: clang/lib/CodeGen/CodeGenModule.cpp:1413
+  FD->getType(), Context.getRecordType(Base).getTypePtr()));
+  F->addTypeMetadata(0, Id);
+}

It'd be nice to have a test that reaches this.



Comment at: clang/lib/CodeGen/CodeGenModule.h:1256
 
+  std::vector
+  getMostBaseClasses(const CXXRecordDecl *RD);

Could be helpful to have a comment here to ensure there is no confusion 
interpreting this as 'the most-base classes' and not 'most of the base classes'.



Comment at: clang/lib/Driver/SanitizerArgs.cpp:233
+ options::OPT_fno_sanitize_cfi_cross_dso, false);
+  if (CfiCrossDso)
+Supported &= ~CFIMFCall;

This will cause supplying both options to fail with `clang: error: unsupported 
option '-fsanitize=cfi-mfcall' for target ...`. Having it error out the same 
way as type generalization below where it states that cfi-cross-dso is 
unsupported with cfi-mfcall seems like a more helpful error.



Comment at: clang/test/CodeGenCXX/type-metadata.cpp:281
 // ITANIUM: [[FA_ID]] = distinct !{}
 
 // MS: [[A8]] = !{i64 8, !"?AUA@@"}

Any reason not to include AF64/CF64/FAF16 here?



Comment at: compiler-rt/lib/ubsan/ubsan_handlers.cc:645
+  const char *CheckKindStr = Data->CheckKind == CFITCK_NVMFCall
+ ? "non-virtual member function call"
+ : "indirect function call";

s/member/pointer to member/ ?



Comment at: compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc:126
+  case CFITCK_VMFCall:
+CheckKindStr = "virtual member function call";
+break;

s/member/pointer to member/ ?


https://reviews.llvm.org/D47567



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D47567: Implement CFI for indirect calls via a member function pointer.

2018-06-13 Thread Peter Collingbourne via Phabricator via cfe-commits
pcc updated this revision to Diff 151272.
pcc added a comment.

- Add some more documentation to ControlFlowIntegrity.rst


https://reviews.llvm.org/D47567

Files:
  clang/docs/ControlFlowIntegrity.rst
  clang/docs/LTOVisibility.rst
  clang/include/clang/Basic/Sanitizers.def
  clang/lib/CodeGen/CGClass.cpp
  clang/lib/CodeGen/CGVTables.cpp
  clang/lib/CodeGen/CodeGenFunction.h
  clang/lib/CodeGen/CodeGenModule.cpp
  clang/lib/CodeGen/CodeGenModule.h
  clang/lib/CodeGen/ItaniumCXXABI.cpp
  clang/lib/Driver/SanitizerArgs.cpp
  clang/lib/Driver/ToolChains/MSVC.cpp
  clang/test/CodeGenCXX/cfi-mfcall-incomplete.cpp
  clang/test/CodeGenCXX/cfi-mfcall.cpp
  clang/test/CodeGenCXX/type-metadata.cpp
  clang/test/Driver/fsanitize.c
  compiler-rt/lib/ubsan/ubsan_handlers.cc
  compiler-rt/lib/ubsan/ubsan_handlers.h
  compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc
  compiler-rt/test/cfi/mfcall.cpp

Index: compiler-rt/test/cfi/mfcall.cpp
===
--- /dev/null
+++ compiler-rt/test/cfi/mfcall.cpp
@@ -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 
+#include 
+
+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 
+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 member function call
+  // A: note: S::f1() defined here
+  (s.*bitcast(&S::f1))();
+  break;
+case 'b':
+  // B: runtime error: control flow integrity check for type 'int (T::*)()' failed during non-virtual member function call
+  // B: note: S::f2() defined here
+  (t.*bitcast(&S::f2))();
+  break;
+case 'c':
+  // C: runtime error: control flow integrity check for type 'int (S::*)()' failed during virtual member function call
+  // C: note: vtable is of type 'S'
+  (s.*bitcast(&S::g1))();
+  break;
+case 'd':
+  // D: runtime error: control flow integrity check for type 'int (S::*)()' failed during virtual member function call
+  // D: note: vtable is of type 'T'
+  (reinterpret_cast(t).*&S::g2)();
+  break;
+case 'e':
+  // E: runtime error: control flow integrity check for type 'void (S::*)()' failed during virtual member function call
+  // E: note: vtable is of type 'S'
+  (s.*bitcast(&T::g3))();
+  break;
+case 'f':
+  (s.*&SBase1::b1)();
+  break;
+case 'g':
+  (s.*&SBase2::b2)();
+  break;
+  }
+}
Index: compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc
===
--- compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc
+++ compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc
@@ -122,7 +122,11 @@
   case CFITCK_UnrelatedCast:
 CheckKindStr = "cast to unrelated type";
 break;
+  case CFITCK_VMFCall:
+CheckKindStr = "virtual member function call";
+break;
   case CFITCK_ICall:
+  case CFITCK_NVMFCall:
 Die();
   }
 
Index: compiler-rt/lib/ubsan/ubsan_handlers.h
===
--- compiler-rt/lib/ubsan/ubsan_handlers.h
+++ compiler-rt/lib/ubsan/ubsan_handlers.h
@@ -181,6 +181,8 @@
   CFITCK_DerivedCast,
   CFITCK_UnrelatedCast,
   CFITCK_ICall,
+  CFITCK_NVMFCall,
+  CFITCK_VMFCall,
 };
 
 struct CFICheckFailData {
Index: compiler-rt/lib/ubsan/ubsan_handlers.cc
===
--- compiler-rt/lib/ubsan/ubsan_handlers.cc
+++ compiler-rt/lib/ubsan/ubsan_handlers.cc
@@ -630,7 +630,7 @@
 
 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();
@@ -641,9 +641,12 @@
 
   Scoped