From 209a92660664324ed64c677aa227b6fcc59ce13c Mon Sep 17 00:00:00 2001
From: Peter Collingbourne <peter@pcc.me.uk>
Date: Thu, 2 Apr 2015 00:23:30 +0000
Subject: [PATCH] Implement CFI type checks for non-virtual calls.

This uses the same class metadata currently used for virtual call and
cast checks.

The new flag is -fsanitize=cfi-nvcall. For consistency, the -fsanitize=cfi-vptr
flag has been renamed -fsanitize=cfi-vcall.

Differential Revision: http://reviews.llvm.org/D8756

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@233874 91177308-0d34-0410-b5e6-96231b3b80d8
---
 docs/ControlFlowIntegrity.rst                 | 25 ++++++++++---
 docs/UsersManual.rst                          |  4 ++-
 include/clang/Basic/Sanitizers.def            |  6 ++--
 lib/CodeGen/CGClass.cpp                       | 17 ++++-----
 lib/CodeGen/CGExprCXX.cpp                     |  6 ++++
 lib/CodeGen/CGVTables.cpp                     |  5 ++-
 lib/CodeGen/ItaniumCXXABI.cpp                 |  3 +-
 lib/Driver/SanitizerArgs.cpp                  |  2 +-
 test/CodeGenCXX/cfi-nvcall.cpp                | 35 +++++++++++++++++++
 .../{cfi-vptr.cpp => cfi-vcall.cpp}           |  6 ++--
 test/Driver/fsanitize.c                       |  8 +++--
 11 files changed, 93 insertions(+), 24 deletions(-)
 create mode 100644 test/CodeGenCXX/cfi-nvcall.cpp
 rename test/CodeGenCXX/{cfi-vptr.cpp => cfi-vcall.cpp} (88%)

diff --git a/docs/ControlFlowIntegrity.rst b/docs/ControlFlowIntegrity.rst
index 51c99177674..915385b7b19 100644
--- a/docs/ControlFlowIntegrity.rst
+++ b/docs/ControlFlowIntegrity.rst
@@ -27,8 +27,8 @@ the program must be structured such that certain object files are compiled
 with CFI enabled, and are statically linked into the program. This may
 preclude the use of shared libraries in some cases.
 
-Clang currently implements forward-edge CFI for virtual calls. More schemes
-are under development.
+Clang currently implements forward-edge CFI for member function calls and
+bad cast checking. More schemes are under development.
 
 .. _gold plugin: http://llvm.org/docs/GoldPlugin.html
 
@@ -38,11 +38,11 @@ Forward-Edge CFI for Virtual Calls
 This scheme checks that virtual calls take place using a vptr of the correct
 dynamic type; that is, the dynamic type of the called object must be a
 derived class of the static type of the object used to make the call.
-This CFI scheme can be enabled on its own using ``-fsanitize=cfi-vptr``.
+This CFI scheme can be enabled on its own using ``-fsanitize=cfi-vcall``.
 
 For this scheme to work, all translation units containing the definition
 of a virtual member function (whether inline or not) must be compiled
-with ``-fsanitize=cfi-vptr`` enabled and be statically linked into the
+with ``-fsanitize=cfi-vcall`` enabled and be statically linked into the
 program. Classes in the C++ standard library (under namespace ``std``) are
 exempted from checking, and therefore programs may be linked against a
 pre-built standard library, but this may change in the future.
@@ -95,6 +95,23 @@ and be statically linked into the program. Classes in the C++ standard library
 may be linked against a pre-built standard library, but this may change in
 the future.
 
+Non-Virtual Member Function Call Checking
+-----------------------------------------
+
+This scheme checks that non-virtual calls take place using an object of
+the correct dynamic type; that is, the dynamic type of the called object
+must be a derived class of the static type of the object used to make the
+call. The checks are currently only introduced where the object is of a
+polymorphic class type.  This CFI scheme can be enabled on its own using
+``-fsanitize=cfi-nvcall``.
+
+For this scheme to work, all translation units containing the definition
+of a virtual member function (whether inline or not) must be compiled
+with ``-fsanitize=cfi-nvcall`` enabled and be statically linked into the
+program. Classes in the C++ standard library (under namespace ``std``) are
+exempted from checking, and therefore programs may be linked against a
+pre-built standard library, but this may change in the future.
+
 .. _cfi-strictness:
 
 Strictness
diff --git a/docs/UsersManual.rst b/docs/UsersManual.rst
index 29c4d4ef901..bf8ce78036f 100644
--- a/docs/UsersManual.rst
+++ b/docs/UsersManual.rst
@@ -974,7 +974,9 @@ are listed below.
       dynamic type. Implies ``-flto``.
    -  ``-fsanitize=cfi-unrelated-cast``: Cast from ``void*`` or another
       unrelated type to the wrong dynamic type. Implies ``-flto``.
-   -  ``-fsanitize=cfi-vptr``: Use of an object whose vptr is of the
+   -  ``-fsanitize=cfi-nvcall``: Non-virtual call via an object whose vptr is of
+      the wrong dynamic type. Implies ``-flto``.
+   -  ``-fsanitize=cfi-vcall``: Virtual call via an object whose vptr is of the
       wrong dynamic type. Implies ``-flto``.
    -  ``-fsanitize=enum``: Load of a value of an enumerated type which
       is not in the range of representable values for that enumerated
diff --git a/include/clang/Basic/Sanitizers.def b/include/clang/Basic/Sanitizers.def
index fa58a34a034..65ababd5ac8 100644
--- a/include/clang/Basic/Sanitizers.def
+++ b/include/clang/Basic/Sanitizers.def
@@ -82,8 +82,10 @@ SANITIZER("dataflow", DataFlow)
 SANITIZER("cfi-cast-strict", CFICastStrict)
 SANITIZER("cfi-derived-cast", CFIDerivedCast)
 SANITIZER("cfi-unrelated-cast", CFIUnrelatedCast)
-SANITIZER("cfi-vptr", CFIVptr)
-SANITIZER_GROUP("cfi", CFI, CFIDerivedCast | CFIUnrelatedCast | CFIVptr)
+SANITIZER("cfi-nvcall", CFINVCall)
+SANITIZER("cfi-vcall", CFIVCall)
+SANITIZER_GROUP("cfi", CFI,
+                CFIDerivedCast | CFIUnrelatedCast | CFINVCall | CFIVCall)
 
 // -fsanitize=undefined-trap includes sanitizers from -fsanitize=undefined
 // that can be used without runtime support, generally by providing extra
diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp
index 84d6437abbe..bd15c121095 100644
--- a/lib/CodeGen/CGClass.cpp
+++ b/lib/CodeGen/CGClass.cpp
@@ -2088,14 +2088,6 @@ llvm::Value *CodeGenFunction::GetVTablePtr(llvm::Value *This,
   return VTable;
 }
 
-void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXMethodDecl *MD,
-                                                llvm::Value *VTable) {
-  if (!SanOpts.has(SanitizerKind::CFIVptr))
-    return;
-
-  EmitVTablePtrCheck(MD->getParent(), VTable);
-}
-
 // If a class has a single non-virtual base and does not introduce or override
 // virtual member functions or fields, it will have the same layout as its base.
 // This function returns the least derived such class.
@@ -2131,6 +2123,15 @@ LeastDerivedClassWithSameLayout(const CXXRecordDecl *RD) {
       RD->bases_begin()->getType()->getAsCXXRecordDecl());
 }
 
+void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXMethodDecl *MD,
+                                                llvm::Value *VTable) {
+  const CXXRecordDecl *ClassDecl = MD->getParent();
+  if (!SanOpts.has(SanitizerKind::CFICastStrict))
+    ClassDecl = LeastDerivedClassWithSameLayout(ClassDecl);
+
+  EmitVTablePtrCheck(ClassDecl, VTable);
+}
+
 void CodeGenFunction::EmitVTablePtrCheckForCast(QualType T,
                                                 llvm::Value *Derived,
                                                 bool MayBeNull) {
diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp
index 6852d3afc7f..f7bf40660c8 100644
--- a/lib/CodeGen/CGExprCXX.cpp
+++ b/lib/CodeGen/CGExprCXX.cpp
@@ -256,6 +256,12 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr(
   } else if (UseVirtualCall) {
     Callee = CGM.getCXXABI().getVirtualFunctionPointer(*this, MD, This, Ty);
   } else {
+    if (SanOpts.has(SanitizerKind::CFINVCall) &&
+        MD->getParent()->isDynamicClass()) {
+      llvm::Value *VTable = GetVTablePtr(This, Int8PtrTy);
+      EmitVTablePtrCheckForCall(MD, VTable);
+    }
+
     if (getLangOpts().AppleKext && MD->isVirtual() && HasQualifier)
       Callee = BuildAppleKextVirtualCall(MD, Qualifier, Ty);
     else if (!DevirtualizedMethod)
diff --git a/lib/CodeGen/CGVTables.cpp b/lib/CodeGen/CGVTables.cpp
index 372db7a7f6e..57370a6faa2 100644
--- a/lib/CodeGen/CGVTables.cpp
+++ b/lib/CodeGen/CGVTables.cpp
@@ -842,7 +842,10 @@ void CodeGenModule::EmitDeferredVTables() {
 
 void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable,
                                             const VTableLayout &VTLayout) {
-  if (!LangOpts.Sanitize.has(SanitizerKind::CFIVptr))
+  if (!LangOpts.Sanitize.has(SanitizerKind::CFIVCall) &&
+      !LangOpts.Sanitize.has(SanitizerKind::CFINVCall) &&
+      !LangOpts.Sanitize.has(SanitizerKind::CFIDerivedCast) &&
+      !LangOpts.Sanitize.has(SanitizerKind::CFIUnrelatedCast))
     return;
 
   llvm::Metadata *VTableMD = llvm::ConstantAsMetadata::get(VTable);
diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp
index 62f1293ff66..7bb0a9bafcb 100644
--- a/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/lib/CodeGen/ItaniumCXXABI.cpp
@@ -1443,7 +1443,8 @@ llvm::Value *ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
   Ty = Ty->getPointerTo()->getPointerTo();
   llvm::Value *VTable = CGF.GetVTablePtr(This, Ty);
 
-  CGF.EmitVTablePtrCheckForCall(cast<CXXMethodDecl>(GD.getDecl()), VTable);
+  if (CGF.SanOpts.has(SanitizerKind::CFIVCall))
+    CGF.EmitVTablePtrCheckForCall(cast<CXXMethodDecl>(GD.getDecl()), VTable);
 
   uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD);
   llvm::Value *VFuncPtr =
diff --git a/lib/Driver/SanitizerArgs.cpp b/lib/Driver/SanitizerArgs.cpp
index 88725cdedd2..cd3785cd90b 100644
--- a/lib/Driver/SanitizerArgs.cpp
+++ b/lib/Driver/SanitizerArgs.cpp
@@ -48,7 +48,7 @@ enum SanitizeKind : uint64_t {
   RecoverableByDefault = Undefined | Integer,
   Unrecoverable = Address | Unreachable | Return,
   LegacyFsanitizeRecoverMask = Undefined | Integer,
-  NeedsLTO = CFIDerivedCast | CFIUnrelatedCast | CFIVptr,
+  NeedsLTO = CFI,
 };
 }
 
diff --git a/test/CodeGenCXX/cfi-nvcall.cpp b/test/CodeGenCXX/cfi-nvcall.cpp
new file mode 100644
index 00000000000..b0db478c9d0
--- /dev/null
+++ b/test/CodeGenCXX/cfi-nvcall.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-nvcall -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-nvcall,cfi-cast-strict -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-STRICT %s
+
+struct A {
+  virtual void f();
+};
+
+struct B : A {
+  int i;
+  void g();
+};
+
+struct C : A {
+  void g();
+};
+
+// CHECK-LABEL: @bg
+// CHECK-STRICT-LABEL: @bg
+extern "C" void bg(B *b) {
+  // CHECK: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B")
+  // CHECK-STRICT: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B")
+  b->g();
+}
+
+// CHECK-LABEL: @cg
+// CHECK-STRICT-LABEL: @cg
+extern "C" void cg(C *c) {
+  // http://clang.llvm.org/docs/ControlFlowIntegrity.html#strictness
+  // In this case C's layout is the same as its base class, so we allow
+  // c to be of type A in non-strict mode.
+
+  // CHECK: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1A")
+  // CHECK-STRICT: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1C")
+  c->g();
+}
diff --git a/test/CodeGenCXX/cfi-vptr.cpp b/test/CodeGenCXX/cfi-vcall.cpp
similarity index 88%
rename from test/CodeGenCXX/cfi-vptr.cpp
rename to test/CodeGenCXX/cfi-vcall.cpp
index 545f22c3c72..bfbbceaa1a3 100644
--- a/test/CodeGenCXX/cfi-vptr.cpp
+++ b/test/CodeGenCXX/cfi-vcall.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vptr -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck %s
 
 struct A {
   A();
@@ -49,7 +49,7 @@ void af(A *a) {
 
 // CHECK: define internal void @_Z2dfPN12_GLOBAL__N_11DE
 void df(D *d) {
-  // CHECK: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vptr.cpp]N12_GLOBAL__N_11DE")
+  // CHECK: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE")
   d->f();
 }
 
@@ -67,7 +67,7 @@ void foo() {
 // CHECK-DAG: !{!"1A", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
 // CHECK-DAG: !{!"1B", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
 // CHECK-DAG: !{!"1C", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 72}
-// CHECK-DAG: !{!"[{{.*}}cfi-vptr.cpp]N12_GLOBAL__N_11DE", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
+// CHECK-DAG: !{!"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
 // CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTV1B, i64 32}
 // CHECK-DAG: !{!"1B", [5 x i8*]* @_ZTV1B, i64 32}
 // CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTV1C, i64 32}
diff --git a/test/Driver/fsanitize.c b/test/Driver/fsanitize.c
index f43029bba74..994b2b236c4 100644
--- a/test/Driver/fsanitize.c
+++ b/test/Driver/fsanitize.c
@@ -206,11 +206,13 @@
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-derived-cast -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-DCAST
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-unrelated-cast -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-UCAST
-// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-vptr -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-VPTR
-// CHECK-CFI: -emit-llvm-bc{{.*}}-fsanitize=cfi-derived-cast,cfi-unrelated-cast,cfi-vptr
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-nvcall -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-NVCALL
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-vcall -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-VCALL
+// CHECK-CFI: -emit-llvm-bc{{.*}}-fsanitize=cfi-derived-cast,cfi-unrelated-cast,cfi-nvcall,cfi-vcall
 // CHECK-CFI-DCAST: -emit-llvm-bc{{.*}}-fsanitize=cfi-derived-cast
 // CHECK-CFI-UCAST: -emit-llvm-bc{{.*}}-fsanitize=cfi-unrelated-cast
-// CHECK-CFI-VPTR: -emit-llvm-bc{{.*}}-fsanitize=cfi-vptr
+// CHECK-CFI-NVCALL: -emit-llvm-bc{{.*}}-fsanitize=cfi-nvcall
+// CHECK-CFI-VCALL: -emit-llvm-bc{{.*}}-fsanitize=cfi-vcall
 
 // RUN: %clang_cl -fsanitize=address -c -MDd -### -- %s 2>&1 | FileCheck %s -check-prefix=CHECK-ASAN-DEBUGRTL
 // RUN: %clang_cl -fsanitize=address -c -MTd -### -- %s 2>&1 | FileCheck %s -check-prefix=CHECK-ASAN-DEBUGRTL
-- 
GitLab