summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Collingbourne <peter@pcc.me.uk>2015-04-02 00:23:30 +0000
committerPeter Collingbourne <peter@pcc.me.uk>2015-04-02 00:23:30 +0000
commit209a92660664324ed64c677aa227b6fcc59ce13c (patch)
tree0af43e0a071c047c1082549ff621f03507de95a8
parent971cf68f266f58f63b0b260b7f3a363d2f111cd6 (diff)
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
-rw-r--r--docs/ControlFlowIntegrity.rst25
-rw-r--r--docs/UsersManual.rst4
-rw-r--r--include/clang/Basic/Sanitizers.def6
-rw-r--r--lib/CodeGen/CGClass.cpp17
-rw-r--r--lib/CodeGen/CGExprCXX.cpp6
-rw-r--r--lib/CodeGen/CGVTables.cpp5
-rw-r--r--lib/CodeGen/ItaniumCXXABI.cpp3
-rw-r--r--lib/Driver/SanitizerArgs.cpp2
-rw-r--r--test/CodeGenCXX/cfi-nvcall.cpp35
-rw-r--r--test/CodeGenCXX/cfi-vcall.cpp (renamed from test/CodeGenCXX/cfi-vptr.cpp)6
-rw-r--r--test/Driver/fsanitize.c8
11 files changed, 93 insertions, 24 deletions
diff --git a/docs/ControlFlowIntegrity.rst b/docs/ControlFlowIntegrity.rst
index 51c9917767..915385b7b1 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 29c4d4ef90..bf8ce78036 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 fa58a34a03..65ababd5ac 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 84d6437abb..bd15c12109 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 6852d3afc7..f7bf40660c 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 372db7a7f6..57370a6faa 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 62f1293ff6..7bb0a9bafc 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 88725cdedd..cd3785cd90 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 0000000000..b0db478c9d
--- /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
index 545f22c3c7..bfbbceaa1a 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 f43029bba7..994b2b236c 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