Skip to content

Commit 209a926

Browse files
committed
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
1 parent 971cf68 commit 209a926

11 files changed

+93
-24
lines changed

docs/ControlFlowIntegrity.rst

+21-4
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ the program must be structured such that certain object files are compiled
2727
with CFI enabled, and are statically linked into the program. This may
2828
preclude the use of shared libraries in some cases.
2929

30-
Clang currently implements forward-edge CFI for virtual calls. More schemes
31-
are under development.
30+
Clang currently implements forward-edge CFI for member function calls and
31+
bad cast checking. More schemes are under development.
3232

3333
.. _gold plugin: http://llvm.org/docs/GoldPlugin.html
3434

@@ -38,11 +38,11 @@ Forward-Edge CFI for Virtual Calls
3838
This scheme checks that virtual calls take place using a vptr of the correct
3939
dynamic type; that is, the dynamic type of the called object must be a
4040
derived class of the static type of the object used to make the call.
41-
This CFI scheme can be enabled on its own using ``-fsanitize=cfi-vptr``.
41+
This CFI scheme can be enabled on its own using ``-fsanitize=cfi-vcall``.
4242

4343
For this scheme to work, all translation units containing the definition
4444
of a virtual member function (whether inline or not) must be compiled
45-
with ``-fsanitize=cfi-vptr`` enabled and be statically linked into the
45+
with ``-fsanitize=cfi-vcall`` enabled and be statically linked into the
4646
program. Classes in the C++ standard library (under namespace ``std``) are
4747
exempted from checking, and therefore programs may be linked against a
4848
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
9595
may be linked against a pre-built standard library, but this may change in
9696
the future.
9797

98+
Non-Virtual Member Function Call Checking
99+
-----------------------------------------
100+
101+
This scheme checks that non-virtual calls take place using an object of
102+
the correct dynamic type; that is, the dynamic type of the called object
103+
must be a derived class of the static type of the object used to make the
104+
call. The checks are currently only introduced where the object is of a
105+
polymorphic class type. This CFI scheme can be enabled on its own using
106+
``-fsanitize=cfi-nvcall``.
107+
108+
For this scheme to work, all translation units containing the definition
109+
of a virtual member function (whether inline or not) must be compiled
110+
with ``-fsanitize=cfi-nvcall`` enabled and be statically linked into the
111+
program. Classes in the C++ standard library (under namespace ``std``) are
112+
exempted from checking, and therefore programs may be linked against a
113+
pre-built standard library, but this may change in the future.
114+
98115
.. _cfi-strictness:
99116

100117
Strictness

docs/UsersManual.rst

+3-1
Original file line numberDiff line numberDiff line change
@@ -974,7 +974,9 @@ are listed below.
974974
dynamic type. Implies ``-flto``.
975975
- ``-fsanitize=cfi-unrelated-cast``: Cast from ``void*`` or another
976976
unrelated type to the wrong dynamic type. Implies ``-flto``.
977-
- ``-fsanitize=cfi-vptr``: Use of an object whose vptr is of the
977+
- ``-fsanitize=cfi-nvcall``: Non-virtual call via an object whose vptr is of
978+
the wrong dynamic type. Implies ``-flto``.
979+
- ``-fsanitize=cfi-vcall``: Virtual call via an object whose vptr is of the
978980
wrong dynamic type. Implies ``-flto``.
979981
- ``-fsanitize=enum``: Load of a value of an enumerated type which
980982
is not in the range of representable values for that enumerated

include/clang/Basic/Sanitizers.def

+4-2
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,10 @@ SANITIZER("dataflow", DataFlow)
8282
SANITIZER("cfi-cast-strict", CFICastStrict)
8383
SANITIZER("cfi-derived-cast", CFIDerivedCast)
8484
SANITIZER("cfi-unrelated-cast", CFIUnrelatedCast)
85-
SANITIZER("cfi-vptr", CFIVptr)
86-
SANITIZER_GROUP("cfi", CFI, CFIDerivedCast | CFIUnrelatedCast | CFIVptr)
85+
SANITIZER("cfi-nvcall", CFINVCall)
86+
SANITIZER("cfi-vcall", CFIVCall)
87+
SANITIZER_GROUP("cfi", CFI,
88+
CFIDerivedCast | CFIUnrelatedCast | CFINVCall | CFIVCall)
8789

8890
// -fsanitize=undefined-trap includes sanitizers from -fsanitize=undefined
8991
// that can be used without runtime support, generally by providing extra

lib/CodeGen/CGClass.cpp

+9-8
Original file line numberDiff line numberDiff line change
@@ -2088,14 +2088,6 @@ llvm::Value *CodeGenFunction::GetVTablePtr(llvm::Value *This,
20882088
return VTable;
20892089
}
20902090

2091-
void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXMethodDecl *MD,
2092-
llvm::Value *VTable) {
2093-
if (!SanOpts.has(SanitizerKind::CFIVptr))
2094-
return;
2095-
2096-
EmitVTablePtrCheck(MD->getParent(), VTable);
2097-
}
2098-
20992091
// If a class has a single non-virtual base and does not introduce or override
21002092
// virtual member functions or fields, it will have the same layout as its base.
21012093
// This function returns the least derived such class.
@@ -2131,6 +2123,15 @@ LeastDerivedClassWithSameLayout(const CXXRecordDecl *RD) {
21312123
RD->bases_begin()->getType()->getAsCXXRecordDecl());
21322124
}
21332125

2126+
void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXMethodDecl *MD,
2127+
llvm::Value *VTable) {
2128+
const CXXRecordDecl *ClassDecl = MD->getParent();
2129+
if (!SanOpts.has(SanitizerKind::CFICastStrict))
2130+
ClassDecl = LeastDerivedClassWithSameLayout(ClassDecl);
2131+
2132+
EmitVTablePtrCheck(ClassDecl, VTable);
2133+
}
2134+
21342135
void CodeGenFunction::EmitVTablePtrCheckForCast(QualType T,
21352136
llvm::Value *Derived,
21362137
bool MayBeNull) {

lib/CodeGen/CGExprCXX.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,12 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr(
256256
} else if (UseVirtualCall) {
257257
Callee = CGM.getCXXABI().getVirtualFunctionPointer(*this, MD, This, Ty);
258258
} else {
259+
if (SanOpts.has(SanitizerKind::CFINVCall) &&
260+
MD->getParent()->isDynamicClass()) {
261+
llvm::Value *VTable = GetVTablePtr(This, Int8PtrTy);
262+
EmitVTablePtrCheckForCall(MD, VTable);
263+
}
264+
259265
if (getLangOpts().AppleKext && MD->isVirtual() && HasQualifier)
260266
Callee = BuildAppleKextVirtualCall(MD, Qualifier, Ty);
261267
else if (!DevirtualizedMethod)

lib/CodeGen/CGVTables.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,10 @@ void CodeGenModule::EmitDeferredVTables() {
842842

843843
void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable,
844844
const VTableLayout &VTLayout) {
845-
if (!LangOpts.Sanitize.has(SanitizerKind::CFIVptr))
845+
if (!LangOpts.Sanitize.has(SanitizerKind::CFIVCall) &&
846+
!LangOpts.Sanitize.has(SanitizerKind::CFINVCall) &&
847+
!LangOpts.Sanitize.has(SanitizerKind::CFIDerivedCast) &&
848+
!LangOpts.Sanitize.has(SanitizerKind::CFIUnrelatedCast))
846849
return;
847850

848851
llvm::Metadata *VTableMD = llvm::ConstantAsMetadata::get(VTable);

lib/CodeGen/ItaniumCXXABI.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1443,7 +1443,8 @@ llvm::Value *ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
14431443
Ty = Ty->getPointerTo()->getPointerTo();
14441444
llvm::Value *VTable = CGF.GetVTablePtr(This, Ty);
14451445

1446-
CGF.EmitVTablePtrCheckForCall(cast<CXXMethodDecl>(GD.getDecl()), VTable);
1446+
if (CGF.SanOpts.has(SanitizerKind::CFIVCall))
1447+
CGF.EmitVTablePtrCheckForCall(cast<CXXMethodDecl>(GD.getDecl()), VTable);
14471448

14481449
uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD);
14491450
llvm::Value *VFuncPtr =

lib/Driver/SanitizerArgs.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ enum SanitizeKind : uint64_t {
4848
RecoverableByDefault = Undefined | Integer,
4949
Unrecoverable = Address | Unreachable | Return,
5050
LegacyFsanitizeRecoverMask = Undefined | Integer,
51-
NeedsLTO = CFIDerivedCast | CFIUnrelatedCast | CFIVptr,
51+
NeedsLTO = CFI,
5252
};
5353
}
5454

test/CodeGenCXX/cfi-nvcall.cpp

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-nvcall -emit-llvm -o - %s | FileCheck %s
2+
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-nvcall,cfi-cast-strict -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-STRICT %s
3+
4+
struct A {
5+
virtual void f();
6+
};
7+
8+
struct B : A {
9+
int i;
10+
void g();
11+
};
12+
13+
struct C : A {
14+
void g();
15+
};
16+
17+
// CHECK-LABEL: @bg
18+
// CHECK-STRICT-LABEL: @bg
19+
extern "C" void bg(B *b) {
20+
// CHECK: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B")
21+
// CHECK-STRICT: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B")
22+
b->g();
23+
}
24+
25+
// CHECK-LABEL: @cg
26+
// CHECK-STRICT-LABEL: @cg
27+
extern "C" void cg(C *c) {
28+
// http://clang.llvm.org/docs/ControlFlowIntegrity.html#strictness
29+
// In this case C's layout is the same as its base class, so we allow
30+
// c to be of type A in non-strict mode.
31+
32+
// CHECK: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1A")
33+
// CHECK-STRICT: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1C")
34+
c->g();
35+
}

test/CodeGenCXX/cfi-vptr.cpp test/CodeGenCXX/cfi-vcall.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vptr -emit-llvm -o - %s | FileCheck %s
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck %s
22

33
struct A {
44
A();
@@ -49,7 +49,7 @@ void af(A *a) {
4949

5050
// CHECK: define internal void @_Z2dfPN12_GLOBAL__N_11DE
5151
void df(D *d) {
52-
// CHECK: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vptr.cpp]N12_GLOBAL__N_11DE")
52+
// CHECK: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE")
5353
d->f();
5454
}
5555

@@ -67,7 +67,7 @@ void foo() {
6767
// CHECK-DAG: !{!"1A", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
6868
// CHECK-DAG: !{!"1B", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
6969
// CHECK-DAG: !{!"1C", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 72}
70-
// CHECK-DAG: !{!"[{{.*}}cfi-vptr.cpp]N12_GLOBAL__N_11DE", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
70+
// CHECK-DAG: !{!"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
7171
// CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTV1B, i64 32}
7272
// CHECK-DAG: !{!"1B", [5 x i8*]* @_ZTV1B, i64 32}
7373
// CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTV1C, i64 32}

test/Driver/fsanitize.c

+5-3
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,13 @@
206206
// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI
207207
// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-derived-cast -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-DCAST
208208
// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-unrelated-cast -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-UCAST
209-
// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-vptr -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-VPTR
210-
// CHECK-CFI: -emit-llvm-bc{{.*}}-fsanitize=cfi-derived-cast,cfi-unrelated-cast,cfi-vptr
209+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-nvcall -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-NVCALL
210+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-vcall -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-VCALL
211+
// CHECK-CFI: -emit-llvm-bc{{.*}}-fsanitize=cfi-derived-cast,cfi-unrelated-cast,cfi-nvcall,cfi-vcall
211212
// CHECK-CFI-DCAST: -emit-llvm-bc{{.*}}-fsanitize=cfi-derived-cast
212213
// CHECK-CFI-UCAST: -emit-llvm-bc{{.*}}-fsanitize=cfi-unrelated-cast
213-
// CHECK-CFI-VPTR: -emit-llvm-bc{{.*}}-fsanitize=cfi-vptr
214+
// CHECK-CFI-NVCALL: -emit-llvm-bc{{.*}}-fsanitize=cfi-nvcall
215+
// CHECK-CFI-VCALL: -emit-llvm-bc{{.*}}-fsanitize=cfi-vcall
214216

215217
// RUN: %clang_cl -fsanitize=address -c -MDd -### -- %s 2>&1 | FileCheck %s -check-prefix=CHECK-ASAN-DEBUGRTL
216218
// RUN: %clang_cl -fsanitize=address -c -MTd -### -- %s 2>&1 | FileCheck %s -check-prefix=CHECK-ASAN-DEBUGRTL

0 commit comments

Comments
 (0)