r233874 - Implement CFI type checks for non-virtual calls.

Peter Collingbourne peter at pcc.me.uk
Wed Apr 1 17:23:30 PDT 2015


Author: pcc
Date: Wed Apr  1 19:23:30 2015
New Revision: 233874

URL: http://llvm.org/viewvc/llvm-project?rev=233874&view=rev
Log:
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

Added:
    cfe/trunk/test/CodeGenCXX/cfi-nvcall.cpp
    cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp
      - copied, changed from r233866, cfe/trunk/test/CodeGenCXX/cfi-vptr.cpp
Removed:
    cfe/trunk/test/CodeGenCXX/cfi-vptr.cpp
Modified:
    cfe/trunk/docs/ControlFlowIntegrity.rst
    cfe/trunk/docs/UsersManual.rst
    cfe/trunk/include/clang/Basic/Sanitizers.def
    cfe/trunk/lib/CodeGen/CGClass.cpp
    cfe/trunk/lib/CodeGen/CGExprCXX.cpp
    cfe/trunk/lib/CodeGen/CGVTables.cpp
    cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
    cfe/trunk/lib/Driver/SanitizerArgs.cpp
    cfe/trunk/test/Driver/fsanitize.c

Modified: cfe/trunk/docs/ControlFlowIntegrity.rst
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/ControlFlowIntegrity.rst?rev=233874&r1=233873&r2=233874&view=diff
==============================================================================
--- cfe/trunk/docs/ControlFlowIntegrity.rst (original)
+++ cfe/trunk/docs/ControlFlowIntegrity.rst Wed Apr  1 19:23:30 2015
@@ -27,8 +27,8 @@ the program must be structured such that
 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 progra
 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

Modified: cfe/trunk/docs/UsersManual.rst
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/UsersManual.rst?rev=233874&r1=233873&r2=233874&view=diff
==============================================================================
--- cfe/trunk/docs/UsersManual.rst (original)
+++ cfe/trunk/docs/UsersManual.rst Wed Apr  1 19:23:30 2015
@@ -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

Modified: cfe/trunk/include/clang/Basic/Sanitizers.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Sanitizers.def?rev=233874&r1=233873&r2=233874&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Sanitizers.def (original)
+++ cfe/trunk/include/clang/Basic/Sanitizers.def Wed Apr  1 19:23:30 2015
@@ -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

Modified: cfe/trunk/lib/CodeGen/CGClass.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=233874&r1=233873&r2=233874&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGClass.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGClass.cpp Wed Apr  1 19:23:30 2015
@@ -2088,14 +2088,6 @@ llvm::Value *CodeGenFunction::GetVTableP
   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 CX
       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) {

Modified: cfe/trunk/lib/CodeGen/CGExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExprCXX.cpp?rev=233874&r1=233873&r2=233874&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGExprCXX.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExprCXX.cpp Wed Apr  1 19:23:30 2015
@@ -256,6 +256,12 @@ RValue CodeGenFunction::EmitCXXMemberOrO
   } 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)

Modified: cfe/trunk/lib/CodeGen/CGVTables.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGVTables.cpp?rev=233874&r1=233873&r2=233874&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGVTables.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGVTables.cpp Wed Apr  1 19:23:30 2015
@@ -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);

Modified: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=233874&r1=233873&r2=233874&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp (original)
+++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp Wed Apr  1 19:23:30 2015
@@ -1443,7 +1443,8 @@ llvm::Value *ItaniumCXXABI::getVirtualFu
   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 =

Modified: cfe/trunk/lib/Driver/SanitizerArgs.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/SanitizerArgs.cpp?rev=233874&r1=233873&r2=233874&view=diff
==============================================================================
--- cfe/trunk/lib/Driver/SanitizerArgs.cpp (original)
+++ cfe/trunk/lib/Driver/SanitizerArgs.cpp Wed Apr  1 19:23:30 2015
@@ -48,7 +48,7 @@ enum SanitizeKind : uint64_t {
   RecoverableByDefault = Undefined | Integer,
   Unrecoverable = Address | Unreachable | Return,
   LegacyFsanitizeRecoverMask = Undefined | Integer,
-  NeedsLTO = CFIDerivedCast | CFIUnrelatedCast | CFIVptr,
+  NeedsLTO = CFI,
 };
 }
 

Added: cfe/trunk/test/CodeGenCXX/cfi-nvcall.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cfi-nvcall.cpp?rev=233874&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/cfi-nvcall.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/cfi-nvcall.cpp Wed Apr  1 19:23:30 2015
@@ -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();
+}

Copied: cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp (from r233866, cfe/trunk/test/CodeGenCXX/cfi-vptr.cpp)
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp?p2=cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp&p1=cfe/trunk/test/CodeGenCXX/cfi-vptr.cpp&r1=233866&r2=233874&rev=233874&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/cfi-vptr.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp Wed Apr  1 19:23:30 2015
@@ -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}

Removed: cfe/trunk/test/CodeGenCXX/cfi-vptr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cfi-vptr.cpp?rev=233873&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/cfi-vptr.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/cfi-vptr.cpp (removed)
@@ -1,74 +0,0 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vptr -emit-llvm -o - %s | FileCheck %s
-
-struct A {
-  A();
-  virtual void f();
-};
-
-struct B : virtual A {
-  B();
-};
-
-struct C : virtual A {
-  C();
-};
-
-namespace {
-
-struct D : B, C {
-  D();
-  virtual void f();
-};
-
-}
-
-A::A() {}
-B::B() {}
-C::C() {}
-D::D() {}
-
-void A::f() {
-}
-
-void D::f() {
-}
-
-// CHECK: define void @_Z2afP1A
-void af(A *a) {
-  // CHECK: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1A")
-  // CHECK-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]]
-
-  // CHECK: [[TRAPBB]]
-  // CHECK-NEXT: call void @llvm.trap()
-  // CHECK-NEXT: unreachable
-
-  // CHECK: [[CONTBB]]
-  // CHECK: call void %
-  a->f();
-}
-
-// 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")
-  d->f();
-}
-
-D d;
-
-void foo() {
-  df(&d);
-}
-
-// CHECK-DAG: !{!"1A", [3 x i8*]* @_ZTV1A, i64 16}
-// CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32}
-// CHECK-DAG: !{!"1B", [5 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32}
-// CHECK-DAG: !{!"1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64}
-// CHECK-DAG: !{!"1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32}
-// 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: !{!"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}
-// CHECK-DAG: !{!"1C", [5 x i8*]* @_ZTV1C, i64 32}

Modified: cfe/trunk/test/Driver/fsanitize.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Driver/fsanitize.c?rev=233874&r1=233873&r2=233874&view=diff
==============================================================================
--- cfe/trunk/test/Driver/fsanitize.c (original)
+++ cfe/trunk/test/Driver/fsanitize.c Wed Apr  1 19:23:30 2015
@@ -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





More information about the cfe-commits mailing list