Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit 8253227

Browse files
committed
CodeGen: Start emitting checked loads when both trapping CFI and -fwhole-program-vtables are enabled.
Differential Revision: http://reviews.llvm.org/D21122 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@273757 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 3c1c202 commit 8253227

File tree

6 files changed

+111
-30
lines changed

6 files changed

+111
-30
lines changed

Diff for: lib/CodeGen/CGClass.cpp

+36
Original file line numberDiff line numberDiff line change
@@ -2645,6 +2645,42 @@ void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD,
26452645
{CastedVTable, ValidVtable});
26462646
}
26472647

2648+
bool CodeGenFunction::ShouldEmitVTableTypeCheckedLoad(const CXXRecordDecl *RD) {
2649+
if (!CGM.getCodeGenOpts().WholeProgramVTables ||
2650+
!SanOpts.has(SanitizerKind::CFIVCall) ||
2651+
!CGM.getCodeGenOpts().SanitizeTrap.has(SanitizerKind::CFIVCall) ||
2652+
!CGM.HasHiddenLTOVisibility(RD))
2653+
return false;
2654+
2655+
std::string TypeName = RD->getQualifiedNameAsString();
2656+
return !getContext().getSanitizerBlacklist().isBlacklistedType(TypeName);
2657+
}
2658+
2659+
llvm::Value *CodeGenFunction::EmitVTableTypeCheckedLoad(
2660+
const CXXRecordDecl *RD, llvm::Value *VTable, uint64_t VTableByteOffset) {
2661+
SanitizerScope SanScope(this);
2662+
2663+
EmitSanitizerStatReport(llvm::SanStat_CFI_VCall);
2664+
2665+
llvm::Metadata *MD =
2666+
CGM.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0));
2667+
llvm::Value *TypeId = llvm::MetadataAsValue::get(CGM.getLLVMContext(), MD);
2668+
2669+
llvm::Value *CastedVTable = Builder.CreateBitCast(VTable, Int8PtrTy);
2670+
llvm::Value *CheckedLoad = Builder.CreateCall(
2671+
CGM.getIntrinsic(llvm::Intrinsic::type_checked_load),
2672+
{CastedVTable, llvm::ConstantInt::get(Int32Ty, VTableByteOffset),
2673+
TypeId});
2674+
llvm::Value *CheckResult = Builder.CreateExtractValue(CheckedLoad, 1);
2675+
2676+
EmitCheck(std::make_pair(CheckResult, SanitizerKind::CFIVCall),
2677+
"cfi_check_fail", nullptr, nullptr);
2678+
2679+
return Builder.CreateBitCast(
2680+
Builder.CreateExtractValue(CheckedLoad, 0),
2681+
cast<llvm::PointerType>(VTable->getType())->getElementType());
2682+
}
2683+
26482684
// FIXME: Ideally Expr::IgnoreParenNoopCasts should do this, but it doesn't do
26492685
// quite what we want.
26502686
static const Expr *skipNoOpCastsAndParens(const Expr *E) {

Diff for: lib/CodeGen/CodeGenFunction.h

+9
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,15 @@ class CodeGenFunction : public CodeGenTypeCache {
14231423
void EmitTypeMetadataCodeForVCall(const CXXRecordDecl *RD,
14241424
llvm::Value *VTable, SourceLocation Loc);
14251425

1426+
/// Returns whether we should perform a type checked load when loading a
1427+
/// virtual function for virtual calls to members of RD. This is generally
1428+
/// true when both vcall CFI and whole-program-vtables are enabled.
1429+
bool ShouldEmitVTableTypeCheckedLoad(const CXXRecordDecl *RD);
1430+
1431+
/// Emit a type checked load from the given vtable.
1432+
llvm::Value *EmitVTableTypeCheckedLoad(const CXXRecordDecl *RD, llvm::Value *VTable,
1433+
uint64_t VTableByteOffset);
1434+
14261435
/// CanDevirtualizeMemberFunctionCalls - Checks whether virtual calls on given
14271436
/// expr can be devirtualized.
14281437
bool CanDevirtualizeMemberFunctionCall(const Expr *Base,

Diff for: lib/CodeGen/ItaniumCXXABI.cpp

+11-5
Original file line numberDiff line numberDiff line change
@@ -1595,12 +1595,18 @@ llvm::Value *ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
15951595
auto *MethodDecl = cast<CXXMethodDecl>(GD.getDecl());
15961596
llvm::Value *VTable = CGF.GetVTablePtr(This, Ty, MethodDecl->getParent());
15971597

1598-
CGF.EmitTypeMetadataCodeForVCall(MethodDecl->getParent(), VTable, Loc);
1599-
16001598
uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD);
1601-
llvm::Value *VFuncPtr =
1602-
CGF.Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfn");
1603-
return CGF.Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign());
1599+
if (CGF.ShouldEmitVTableTypeCheckedLoad(MethodDecl->getParent())) {
1600+
return CGF.EmitVTableTypeCheckedLoad(
1601+
MethodDecl->getParent(), VTable,
1602+
VTableIndex * CGM.getContext().getTargetInfo().getPointerWidth(0) / 8);
1603+
} else {
1604+
CGF.EmitTypeMetadataCodeForVCall(MethodDecl->getParent(), VTable, Loc);
1605+
1606+
llvm::Value *VFuncPtr =
1607+
CGF.Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfn");
1608+
return CGF.Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign());
1609+
}
16041610
}
16051611

16061612
llvm::Value *ItaniumCXXABI::EmitVirtualDestructorCall(

Diff for: lib/CodeGen/MicrosoftCXXABI.cpp

+13-6
Original file line numberDiff line numberDiff line change
@@ -1815,13 +1815,20 @@ llvm::Value *MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
18151815

18161816
MicrosoftVTableContext::MethodVFTableLocation ML =
18171817
CGM.getMicrosoftVTableContext().getMethodVFTableLocation(GD);
1818-
if (CGM.getCodeGenOpts().PrepareForLTO)
1819-
CGF.EmitTypeMetadataCodeForVCall(
1820-
getClassAtVTableLocation(getContext(), GD, ML), VTable, Loc);
18211818

1822-
llvm::Value *VFuncPtr =
1823-
Builder.CreateConstInBoundsGEP1_64(VTable, ML.Index, "vfn");
1824-
return Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign());
1819+
if (CGF.ShouldEmitVTableTypeCheckedLoad(MethodDecl->getParent())) {
1820+
return CGF.EmitVTableTypeCheckedLoad(
1821+
getClassAtVTableLocation(getContext(), GD, ML), VTable,
1822+
ML.Index * CGM.getContext().getTargetInfo().getPointerWidth(0) / 8);
1823+
} else {
1824+
if (CGM.getCodeGenOpts().PrepareForLTO)
1825+
CGF.EmitTypeMetadataCodeForVCall(
1826+
getClassAtVTableLocation(getContext(), GD, ML), VTable, Loc);
1827+
1828+
llvm::Value *VFuncPtr =
1829+
Builder.CreateConstInBoundsGEP1_64(VTable, ML.Index, "vfn");
1830+
return Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign());
1831+
}
18251832
}
18261833

18271834
llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall(

Diff for: test/CodeGenCXX/cfi-stats.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall,cfi-nvcall,cfi-derived-cast,cfi-unrelated-cast,cfi-icall -fsanitize-stats -emit-llvm -o - %s | FileCheck %s
2+
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall,cfi-nvcall,cfi-derived-cast,cfi-unrelated-cast,cfi-icall -fsanitize-trap=cfi-vcall -fwhole-program-vtables -fsanitize-stats -emit-llvm -o - %s | FileCheck %s
23

34
// CHECK: [[STATS:@[^ ]*]] = internal global { i8*, i32, [5 x [2 x i8*]] } { i8* null, i32 5, [5 x [2 x i8*]]
45
// CHECK: {{\[\[}}2 x i8*] zeroinitializer,

Diff for: test/CodeGenCXX/type-metadata.cpp

+41-19
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
// Tests for the cfi-vcall feature:
2-
// RUN: %clang_cc1 -flto -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=ITANIUM --check-prefix=ITANIUM-NDIAG --check-prefix=NDIAG %s
3-
// RUN: %clang_cc1 -flto -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=ITANIUM --check-prefix=ITANIUM-DIAG --check-prefix=DIAG --check-prefix=DIAG-ABORT %s
4-
// RUN: %clang_cc1 -flto -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall -fsanitize-recover=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=ITANIUM --check-prefix=ITANIUM-DIAG --check-prefix=DIAG --check-prefix=DIAG-RECOVER %s
5-
// RUN: %clang_cc1 -flto -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=MS --check-prefix=NDIAG %s
2+
// RUN: %clang_cc1 -flto -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-NVT --check-prefix=ITANIUM --check-prefix=TT-ITANIUM --check-prefix=NDIAG %s
3+
// RUN: %clang_cc1 -flto -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-NVT --check-prefix=ITANIUM --check-prefix=TT-ITANIUM --check-prefix=ITANIUM-DIAG --check-prefix=DIAG --check-prefix=DIAG-ABORT %s
4+
// RUN: %clang_cc1 -flto -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall -fsanitize-recover=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-NVT --check-prefix=ITANIUM --check-prefix=TT-ITANIUM --check-prefix=ITANIUM-DIAG --check-prefix=DIAG --check-prefix=DIAG-RECOVER %s
5+
// RUN: %clang_cc1 -flto -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-NVT --check-prefix=MS --check-prefix=TT-MS --check-prefix=NDIAG %s
66

77
// Tests for the whole-program-vtables feature:
8-
// RUN: %clang_cc1 -flto -triple x86_64-unknown-linux -fvisibility hidden -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM %s
9-
// RUN: %clang_cc1 -flto -triple x86_64-pc-windows-msvc -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=MS %s
8+
// RUN: %clang_cc1 -flto -triple x86_64-unknown-linux -fvisibility hidden -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM --check-prefix=TT-ITANIUM %s
9+
// RUN: %clang_cc1 -flto -triple x86_64-pc-windows-msvc -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=MS --check-prefix=TT-MS %s
10+
11+
// Tests for cfi + whole-program-vtables:
12+
// RUN: %clang_cc1 -flto -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-VT --check-prefix=ITANIUM --check-prefix=TC-ITANIUM %s
13+
// RUN: %clang_cc1 -flto -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-VT --check-prefix=MS --check-prefix=TC-MS %s
1014

1115
// ITANIUM: @_ZTV1A = {{[^!]*}}, !type [[A16:![0-9]+]]
1216
// ITANIUM-DIAG-SAME: !type [[ALL16:![0-9]+]]
@@ -102,8 +106,11 @@ void D::h() {
102106
// ITANIUM: define hidden void @_Z2afP1A
103107
// MS: define void @"\01?af@@YAXPEAUA@@@Z"
104108
void af(A *a) {
105-
// ITANIUM: [[P:%[^ ]*]] = call i1 @llvm.type.test(i8* [[VT:%[^ ]*]], metadata !"_ZTS1A")
106-
// MS: [[P:%[^ ]*]] = call i1 @llvm.type.test(i8* [[VT:%[^ ]*]], metadata !"?AUA@@")
109+
// TT-ITANIUM: [[P:%[^ ]*]] = call i1 @llvm.type.test(i8* [[VT:%[^ ]*]], metadata !"_ZTS1A")
110+
// TT-MS: [[P:%[^ ]*]] = call i1 @llvm.type.test(i8* [[VT:%[^ ]*]], metadata !"?AUA@@")
111+
// TC-ITANIUM: [[PAIR:%[^ ]*]] = call { i8*, i1 } @llvm.type.checked.load(i8* %2, i32 0, metadata !"_ZTS1A")
112+
// TC-MS: [[PAIR:%[^ ]*]] = call { i8*, i1 } @llvm.type.checked.load(i8* %2, i32 0, metadata !"?AUA@@")
113+
// CFI-VT: [[P:%[^ ]*]] = extractvalue { i8*, i1 } [[PAIR]], 1
107114
// DIAG-NEXT: [[VTVALID0:%[^ ]*]] = call i1 @llvm.type.test(i8* [[VT]], metadata !"all-vtables")
108115
// VTABLE-OPT: call void @llvm.assume(i1 [[P]])
109116
// CFI-NEXT: br i1 [[P]], label %[[CONTBB:[^ ,]*]], label %[[TRAPBB:[^ ,]*]]
@@ -120,48 +127,61 @@ void af(A *a) {
120127
// DIAG-RECOVER-NEXT: br label %[[CONTBB]]
121128

122129
// CFI: [[CONTBB]]
123-
// CFI: call void %
130+
// CFI-NVT: [[PTR:%[^ ]*]] = load
131+
// CFI-VT: [[PTRI8:%[^ ]*]] = extractvalue { i8*, i1 } [[PAIR]], 0
132+
// CFI-VT: [[PTR:%[^ ]*]] = bitcast i8* [[PTRI8]] to
133+
// CFI: call void [[PTR]]
124134
#line 123
125135
a->f();
126136
}
127137

128138
// ITANIUM: define internal void @_Z3df1PN12_GLOBAL__N_11DE
129139
// MS: define internal void @"\01?df1@@YAXPEAUD@?A@@@Z"
130140
void df1(D *d) {
131-
// ITANIUM: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]])
132-
// MS: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"?AUA@@")
141+
// TT-ITANIUM: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]])
142+
// TT-MS: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"?AUA@@")
143+
// TC-ITANIUM: {{%[^ ]*}} = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 0, metadata ![[DTYPE:[0-9]+]])
144+
// TC-MS: {{%[^ ]*}} = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 0, metadata !"?AUA@@")
133145
d->f();
134146
}
135147

136148
// ITANIUM: define internal void @_Z3dg1PN12_GLOBAL__N_11DE
137149
// MS: define internal void @"\01?dg1@@YAXPEAUD@?A@@@Z"
138150
void dg1(D *d) {
139-
// ITANIUM: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B")
140-
// MS: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"?AUB@@")
151+
// TT-ITANIUM: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B")
152+
// TT-MS: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"?AUB@@")
153+
// TC-ITANIUM: {{%[^ ]*}} = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 8, metadata !"_ZTS1B")
154+
// TC-MS: {{%[^ ]*}} = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 0, metadata !"?AUB@@")
141155
d->g();
142156
}
143157

144158
// ITANIUM: define internal void @_Z3dh1PN12_GLOBAL__N_11DE
145159
// MS: define internal void @"\01?dh1@@YAXPEAUD@?A@@@Z"
146160
void dh1(D *d) {
147-
// ITANIUM: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata ![[DTYPE]])
148-
// MS: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]])
161+
// TT-ITANIUM: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata ![[DTYPE]])
162+
// TT-MS: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]])
163+
// TC-ITANIUM: {{%[^ ]*}} = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 16, metadata ![[DTYPE]])
164+
// TC-MS: {{%[^ ]*}} = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 8, metadata ![[DTYPE:[0-9]+]])
149165
d->h();
150166
}
151167

152168
// ITANIUM: define internal void @_Z3df2PN12_GLOBAL__N_11DE
153169
// MS: define internal void @"\01?df2@@YAXPEAUD@?A@@@Z"
154170
__attribute__((no_sanitize("cfi")))
155171
void df2(D *d) {
156-
// CFI-NOT: call i1 @llvm.type.test
172+
// CFI-NVT-NOT: call i1 @llvm.type.test
173+
// CFI-VT: [[P:%[^ ]*]] = call i1 @llvm.type.test
174+
// CFI-VT: call void @llvm.assume(i1 [[P]])
157175
d->f();
158176
}
159177

160178
// ITANIUM: define internal void @_Z3df3PN12_GLOBAL__N_11DE
161179
// MS: define internal void @"\01?df3@@YAXPEAUD@?A@@@Z"
162180
__attribute__((no_sanitize("address"))) __attribute__((no_sanitize("cfi-vcall")))
163181
void df3(D *d) {
164-
// CFI-NOT: call i1 @llvm.type.test
182+
// CFI-NVT-NOT: call i1 @llvm.type.test
183+
// CFI-VT: [[P:%[^ ]*]] = call i1 @llvm.type.test
184+
// CFI-VT: call void @llvm.assume(i1 [[P]])
165185
d->f();
166186
}
167187

@@ -196,8 +216,10 @@ struct D : C {
196216
// ITANIUM: define hidden void @_ZN5test21fEPNS_1DE
197217
// MS: define void @"\01?f@test2@@YAXPEAUD@1@@Z"
198218
void f(D *d) {
199-
// ITANIUM: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"_ZTSN5test21DE")
200-
// MS: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"?AUA@test2@@")
219+
// TT-ITANIUM: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"_ZTSN5test21DE")
220+
// TT-MS: {{%[^ ]*}} = call i1 @llvm.type.test(i8* {{%[^ ]*}}, metadata !"?AUA@test2@@")
221+
// TC-ITANIUM: {{%[^ ]*}} = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 8, metadata !"_ZTSN5test21DE")
222+
// TC-MS: {{%[^ ]*}} = call { i8*, i1 } @llvm.type.checked.load(i8* {{%[^ ]*}}, i32 0, metadata !"?AUA@test2@@")
201223
d->m_fn1();
202224
}
203225

0 commit comments

Comments
 (0)