REPOSITORY
rL LLVM
http://reviews.llvm.org/D8756
Files:
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/CodeGenCXX/cfi-nvcall.cpp
cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp
cfe/trunk/test/CodeGenCXX/cfi-vptr.cpp
cfe/trunk/test/Driver/fsanitize.c
EMAIL PREFERENCES
http://reviews.llvm.org/settings/panel/emailpreferences/
Index: cfe/trunk/lib/Driver/SanitizerArgs.cpp
===================================================================
--- cfe/trunk/lib/Driver/SanitizerArgs.cpp
+++ cfe/trunk/lib/Driver/SanitizerArgs.cpp
@@ -48,7 +48,7 @@
RecoverableByDefault = Undefined | Integer,
Unrecoverable = Address | Unreachable | Return,
LegacyFsanitizeRecoverMask = Undefined | Integer,
- NeedsLTO = CFIDerivedCast | CFIUnrelatedCast | CFIVptr,
+ NeedsLTO = CFI,
};
}
Index: cfe/trunk/lib/CodeGen/CGExprCXX.cpp
===================================================================
--- cfe/trunk/lib/CodeGen/CGExprCXX.cpp
+++ cfe/trunk/lib/CodeGen/CGExprCXX.cpp
@@ -256,6 +256,12 @@
} 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)
Index: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
===================================================================
--- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
+++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
@@ -1443,7 +1443,8 @@
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 =
Index: cfe/trunk/lib/CodeGen/CGVTables.cpp
===================================================================
--- cfe/trunk/lib/CodeGen/CGVTables.cpp
+++ cfe/trunk/lib/CodeGen/CGVTables.cpp
@@ -842,7 +842,10 @@
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);
Index: cfe/trunk/lib/CodeGen/CGClass.cpp
===================================================================
--- cfe/trunk/lib/CodeGen/CGClass.cpp
+++ cfe/trunk/lib/CodeGen/CGClass.cpp
@@ -2088,14 +2088,6 @@
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 @@
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) {
Index: cfe/trunk/test/CodeGenCXX/cfi-vptr.cpp
===================================================================
--- cfe/trunk/test/CodeGenCXX/cfi-vptr.cpp
+++ cfe/trunk/test/CodeGenCXX/cfi-vptr.cpp
@@ -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}
Index: cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp
===================================================================
--- cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp
+++ cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -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-vcall.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-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}
+// CHECK-DAG: !{!"1C", [5 x i8*]* @_ZTV1C, i64 32}
Index: cfe/trunk/test/CodeGenCXX/cfi-nvcall.cpp
===================================================================
--- cfe/trunk/test/CodeGenCXX/cfi-nvcall.cpp
+++ cfe/trunk/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();
+}
Index: cfe/trunk/test/Driver/fsanitize.c
===================================================================
--- cfe/trunk/test/Driver/fsanitize.c
+++ cfe/trunk/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
Index: cfe/trunk/include/clang/Basic/Sanitizers.def
===================================================================
--- cfe/trunk/include/clang/Basic/Sanitizers.def
+++ cfe/trunk/include/clang/Basic/Sanitizers.def
@@ -82,8 +82,10 @@
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
Index: cfe/trunk/docs/UsersManual.rst
===================================================================
--- cfe/trunk/docs/UsersManual.rst
+++ cfe/trunk/docs/UsersManual.rst
@@ -974,7 +974,9 @@
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
Index: cfe/trunk/docs/ControlFlowIntegrity.rst
===================================================================
--- cfe/trunk/docs/ControlFlowIntegrity.rst
+++ cfe/trunk/docs/ControlFlowIntegrity.rst
@@ -27,8 +27,8 @@
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 @@
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 @@
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
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits