From d954601f636464a0beae2d8ae3883631bda9467f Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Fri, 19 Jun 2015 02:30:43 +0000 Subject: [PATCH] CFI: Implement bitset emission for the Microsoft ABI. Clang's control flow integrity implementation works by conceptually attaching "tags" (in the form of bitset entries) to each virtual table, identifying the names of the classes that the virtual table is compatible with. Under the Itanium ABI, it is simple to assign tags to virtual tables; they are simply the address points, which are available via VTableLayout. Because any overridden methods receive an entry in the derived class's virtual table, a check for an overridden method call can always be done by checking the tag of whichever derived class overrode the method call. The Microsoft ABI is a little different, as it does not directly use address points, and overrides in a derived class do not cause new virtual table entries to be added to the derived class; instead, the slot in the base class is reused, and the compiler needs to adjust the this pointer at the call site to (generally) the base class that initially defined the method. After the this pointer has been adjusted, we cannot check for the derived class's tag, as the virtual table may not be compatible with the derived class. So we need to determine which base class we have been adjusted to. Specifically, at each call site, we use ASTRecordLayout to identify the most derived class whose virtual table is laid out at the "this" pointer offset we are using to make the call, and check the virtual table for that tag. Because address point information is unavailable, we "reconstruct" it as follows: any virtual tables we create for a non-derived class receive a tag for that class, and virtual tables for a base class inside a derived class receive a tag for the base class, together with tags for any derived classes which are laid out at the same position as the derived class (and therefore have compatible virtual tables). Differential Revision: http://reviews.llvm.org/D10520 llvm-svn: 240117 --- clang/lib/AST/ItaniumMangle.cpp | 3 +- clang/lib/AST/MicrosoftMangle.cpp | 12 +++- clang/lib/CodeGen/MicrosoftCXXABI.cpp | 104 ++++++++++++++++++++++++++++++++ clang/test/CodeGenCXX/cfi-vcall.cpp | 110 +++++++++++++++++++++++++++------- 4 files changed, 203 insertions(+), 26 deletions(-) diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 98e1006..e5a31f8 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -4066,8 +4066,7 @@ void ItaniumMangleContextImpl::mangleTypeName(QualType Ty, raw_ostream &Out) { void ItaniumMangleContextImpl::mangleCXXVTableBitSet(const CXXRecordDecl *RD, raw_ostream &Out) { - Linkage L = RD->getLinkageInternal(); - if (L == InternalLinkage || L == UniqueExternalLinkage) { + if (!RD->isExternallyVisible()) { // This part of the identifier needs to be unique across all translation // units in the linked program. The scheme fails if multiple translation // units are compiled using the same relative source file path, or if diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index db5b48e..29a95a5 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -2761,7 +2761,17 @@ void MicrosoftMangleContextImpl::mangleStringLiteral(const StringLiteral *SL, void MicrosoftMangleContextImpl::mangleCXXVTableBitSet(const CXXRecordDecl *RD, raw_ostream &Out) { - llvm::report_fatal_error("Cannot mangle bitsets yet"); + if (!RD->isExternallyVisible()) { + // This part of the identifier needs to be unique across all translation + // units in the linked program. The scheme fails if multiple translation + // units are compiled using the same relative source file path, or if + // multiple translation units are built from the same source file. + SourceManager &SM = getASTContext().getSourceManager(); + Out << "[" << SM.getFileEntryForID(SM.getMainFileID())->getName() << "]"; + } + + MicrosoftCXXNameMangler mangler(*this, Out); + mangler.mangleName(RD); } MicrosoftMangleContext * diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index bc3b68f..679516b 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -205,6 +205,9 @@ public: CXXDtorType Type, bool ForVirtualBase, bool Delegating, llvm::Value *This) override; + void emitVTableBitSetEntries(VPtrInfo *Info, const CXXRecordDecl *RD, + llvm::GlobalVariable *VTable); + void emitVTableDefinitions(CodeGenVTables &CGVT, const CXXRecordDecl *RD) override; @@ -1401,6 +1404,58 @@ void MicrosoftCXXABI::EmitDestructorCall(CodeGenFunction &CGF, getFromDtorType(Type)); } +void MicrosoftCXXABI::emitVTableBitSetEntries(VPtrInfo *Info, + const CXXRecordDecl *RD, + llvm::GlobalVariable *VTable) { + if (!getContext().getLangOpts().Sanitize.has(SanitizerKind::CFIVCall) && + !getContext().getLangOpts().Sanitize.has(SanitizerKind::CFINVCall) && + !getContext().getLangOpts().Sanitize.has(SanitizerKind::CFIDerivedCast) && + !getContext().getLangOpts().Sanitize.has(SanitizerKind::CFIUnrelatedCast)) + return; + + llvm::NamedMDNode *BitsetsMD = + CGM.getModule().getOrInsertNamedMetadata("llvm.bitsets"); + CharUnits PointerWidth = getContext().toCharUnitsFromBits( + getContext().getTargetInfo().getPointerWidth(0)); + + // FIXME: Add blacklisting scheme. + + if (Info->PathToBaseWithVPtr.empty()) { + BitsetsMD->addOperand( + CGM.CreateVTableBitSetEntry(VTable, PointerWidth, RD)); + return; + } + + // Add a bitset entry for the least derived base belonging to this vftable. + BitsetsMD->addOperand(CGM.CreateVTableBitSetEntry( + VTable, PointerWidth, Info->PathToBaseWithVPtr.back())); + + // Add a bitset entry for each derived class that is laid out at the same + // offset as the least derived base. + for (unsigned I = Info->PathToBaseWithVPtr.size() - 1; I != 0; --I) { + const CXXRecordDecl *DerivedRD = Info->PathToBaseWithVPtr[I - 1]; + const CXXRecordDecl *BaseRD = Info->PathToBaseWithVPtr[I]; + + const ASTRecordLayout &Layout = + getContext().getASTRecordLayout(DerivedRD); + CharUnits Offset; + auto VBI = Layout.getVBaseOffsetsMap().find(BaseRD); + if (VBI == Layout.getVBaseOffsetsMap().end()) + Offset = Layout.getBaseClassOffset(BaseRD); + else + Offset = VBI->second.VBaseOffset; + if (!Offset.isZero()) + return; + BitsetsMD->addOperand( + CGM.CreateVTableBitSetEntry(VTable, PointerWidth, DerivedRD)); + } + + // Finally do the same for the most derived class. + if (Info->FullOffsetInMDC.isZero()) + BitsetsMD->addOperand( + CGM.CreateVTableBitSetEntry(VTable, PointerWidth, RD)); +} + void MicrosoftCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT, const CXXRecordDecl *RD) { MicrosoftVTableContext &VFTContext = CGM.getMicrosoftVTableContext(); @@ -1423,6 +1478,8 @@ void MicrosoftCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT, VTLayout.getNumVTableThunks(), RTTI); VTable->setInitializer(Init); + + emitVTableBitSetEntries(Info, RD, VTable); } } @@ -1585,6 +1642,49 @@ llvm::GlobalVariable *MicrosoftCXXABI::getAddrOfVTable(const CXXRecordDecl *RD, return VTable; } +// Compute the identity of the most derived class whose virtual table is located +// at the given offset into RD. +static const CXXRecordDecl *getClassAtVTableLocation(ASTContext &Ctx, + const CXXRecordDecl *RD, + CharUnits Offset) { + if (Offset.isZero()) + return RD; + + const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD); + const CXXRecordDecl *MaxBase = nullptr; + CharUnits MaxBaseOffset; + for (auto &&B : RD->bases()) { + const CXXRecordDecl *Base = B.getType()->getAsCXXRecordDecl(); + CharUnits BaseOffset = Layout.getBaseClassOffset(Base); + if (BaseOffset <= Offset && BaseOffset > MaxBaseOffset) { + MaxBase = Base; + MaxBaseOffset = BaseOffset; + } + } + for (auto &&B : RD->vbases()) { + const CXXRecordDecl *Base = B.getType()->getAsCXXRecordDecl(); + CharUnits BaseOffset = Layout.getVBaseClassOffset(Base); + if (BaseOffset <= Offset && BaseOffset > MaxBaseOffset) { + MaxBase = Base; + MaxBaseOffset = BaseOffset; + } + } + assert(MaxBase); + return getClassAtVTableLocation(Ctx, MaxBase, Offset - MaxBaseOffset); +} + +// Compute the identity of the most derived class whose virtual table is located +// at the MethodVFTableLocation ML. +static const CXXRecordDecl * +getClassAtVTableLocation(ASTContext &Ctx, GlobalDecl GD, + MicrosoftVTableContext::MethodVFTableLocation &ML) { + const CXXRecordDecl *RD = ML.VBase; + if (!RD) + RD = cast(GD.getDecl())->getParent(); + + return getClassAtVTableLocation(Ctx, RD, ML.VFPtrOffset); +} + llvm::Value *MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, GlobalDecl GD, llvm::Value *This, @@ -1600,6 +1700,10 @@ llvm::Value *MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, MicrosoftVTableContext::MethodVFTableLocation ML = CGM.getMicrosoftVTableContext().getMethodVFTableLocation(GD); + if (CGF.SanOpts.has(SanitizerKind::CFIVCall)) + CGF.EmitVTablePtrCheck(getClassAtVTableLocation(getContext(), GD, ML), + VTable, CodeGenFunction::CFITCK_VCall, Loc); + llvm::Value *VFuncPtr = Builder.CreateConstInBoundsGEP1_64(VTable, ML.Index, "vfn"); return Builder.CreateLoad(VFuncPtr); diff --git a/clang/test/CodeGenCXX/cfi-vcall.cpp b/clang/test/CodeGenCXX/cfi-vcall.cpp index a450961..333c7bd 100644 --- a/clang/test/CodeGenCXX/cfi-vcall.cpp +++ b/clang/test/CodeGenCXX/cfi-vcall.cpp @@ -1,6 +1,15 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NDIAG %s -// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=DIAG --check-prefix=DIAG-ABORT %s -// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-recover=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=DIAG --check-prefix=DIAG-RECOVER %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=NDIAG %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=DIAG --check-prefix=DIAG-ABORT %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-recover=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=DIAG --check-prefix=DIAG-RECOVER %s +// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=MS --check-prefix=NDIAG %s + +// MS: @[[VTA:[0-9]*]] {{.*}} comdat($"\01??_7A@@6B@") +// MS: @[[VTB:[0-9]*]] {{.*}} comdat($"\01??_7B@@6B0@@") +// MS: @[[VTAinB:[0-9]*]] {{.*}} comdat($"\01??_7B@@6BA@@@") +// MS: @[[VTAinC:[0-9]*]] {{.*}} comdat($"\01??_7C@@6B@") +// MS: @[[VTBinD:[0-9]*]] {{.*}} comdat($"\01??_7D@?A@@6BB@@@") +// MS: @[[VTAinBinD:[0-9]*]] {{.*}} comdat($"\01??_7D@?A@@6BA@@@") +// MS: @[[VTFA:[0-9]*]] {{.*}} comdat($"\01??_7FA@?1??foo@@YAXXZ@6B@") struct A { A(); @@ -9,6 +18,8 @@ struct A { struct B : virtual A { B(); + virtual void g(); + virtual void h(); }; struct C : virtual A { @@ -20,6 +31,7 @@ namespace { struct D : B, C { D(); virtual void f(); + virtual void h(); }; } @@ -32,17 +44,26 @@ D::D() {} void A::f() { } +void B::g() { +} + void D::f() { } +void D::h() { +} + // DIAG: @[[SRC:.*]] = private unnamed_addr constant [{{.*}} x i8] c"{{.*}}cfi-vcall.cpp\00", align 1 // DIAG: @[[TYPE:.*]] = private unnamed_addr constant { i16, i16, [4 x i8] } { i16 -1, i16 0, [4 x i8] c"'A'\00" } -// DIAG: @[[BADTYPESTATIC:.*]] = private unnamed_addr global { { [{{.*}} x i8]*, i32, i32 }, { i16, i16, [4 x i8] }*, i8 } { { [{{.*}} x i8]*, i32, i32 } { [{{.*}} x i8]* @[[SRC]], i32 58, i32 3 }, { i16, i16, [4 x i8] }* @[[TYPE]], i8 0 } +// DIAG: @[[BADTYPESTATIC:.*]] = private unnamed_addr global { { [{{.*}} x i8]*, i32, i32 }, { i16, i16, [4 x i8] }*, i8 } { { [{{.*}} x i8]*, i32, i32 } { [{{.*}} x i8]* @[[SRC]], i32 [[@LINE+21]], i32 3 }, { i16, i16, [4 x i8] }* @[[TYPE]], i8 0 } -// CHECK: define void @_Z2afP1A +// ITANIUM: define void @_Z2afP1A +// MS: define void @"\01?af@@YAXPEAUA@@@Z" void af(A *a) { - // CHECK: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"1A") + // ITANIUM: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"1A") + // MS: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"A@@") // CHECK-NEXT: br i1 [[P]], label %[[CONTBB:[^ ,]*]], label %[[TRAPBB:[^ ,]*]] + // CHECK-NEXT: {{^$}} // CHECK: [[TRAPBB]] // NDIAG-NEXT: call void @llvm.trap() @@ -58,20 +79,40 @@ void af(A *a) { a->f(); } -// CHECK: define internal void @_Z3df1PN12_GLOBAL__N_11DE +// ITANIUM: define internal void @_Z3df1PN12_GLOBAL__N_11DE +// MS: define internal void @"\01?df1@@YAXPEAUD@?A@@@Z" void df1(D *d) { - // CHECK: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE") + // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE") + // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"A@@") d->f(); } -// CHECK: define internal void @_Z3df2PN12_GLOBAL__N_11DE +// ITANIUM: define internal void @_Z3dg1PN12_GLOBAL__N_11DE +// MS: define internal void @"\01?dg1@@YAXPEAUD@?A@@@Z" +void dg1(D *d) { + // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") + // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"B@@") + d->g(); +} + +// ITANIUM: define internal void @_Z3dh1PN12_GLOBAL__N_11DE +// MS: define internal void @"\01?dh1@@YAXPEAUD@?A@@@Z" +void dh1(D *d) { + // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE") + // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vcall.cpp]D@?A@@") + d->h(); +} + +// ITANIUM: define internal void @_Z3df2PN12_GLOBAL__N_11DE +// MS: define internal void @"\01?df2@@YAXPEAUD@?A@@@Z" __attribute__((no_sanitize("cfi"))) void df2(D *d) { // CHECK-NOT: call i1 @llvm.bitset.test d->f(); } -// CHECK: define internal void @_Z3df3PN12_GLOBAL__N_11DE +// ITANIUM: define internal void @_Z3df3PN12_GLOBAL__N_11DE +// MS: define internal void @"\01?df3@@YAXPEAUD@?A@@@Z" __attribute__((no_sanitize("address"))) __attribute__((no_sanitize("cfi-vcall"))) void df3(D *d) { // CHECK-NOT: call i1 @llvm.bitset.test @@ -82,20 +123,43 @@ D d; void foo() { df1(&d); + dg1(&d); + dh1(&d); df2(&d); df3(&d); + + struct FA : A { + void f() {} + } fa; + af(&fa); } -// 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} +// Check for the expected number of elements (9 or 15 respectively). +// MS: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){8}]]} +// ITANIUM: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){14}]]} + +// ITANIUM-DAG: !{!"1A", [3 x i8*]* @_ZTV1A, i64 16} +// ITANIUM-DAG: !{!"1A", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} +// ITANIUM-DAG: !{!"1B", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} +// ITANIUM-DAG: !{!"1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64} +// ITANIUM-DAG: !{!"1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32} +// ITANIUM-DAG: !{!"1A", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-DAG: !{!"1B", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-DAG: !{!"1C", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88} +// ITANIUM-DAG: !{!"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-DAG: !{!"1A", [7 x i8*]* @_ZTV1B, i64 32} +// ITANIUM-DAG: !{!"1B", [7 x i8*]* @_ZTV1B, i64 32} +// ITANIUM-DAG: !{!"1A", [5 x i8*]* @_ZTV1C, i64 32} +// ITANIUM-DAG: !{!"1C", [5 x i8*]* @_ZTV1C, i64 32} +// ITANIUM-DAG: !{!"1A", [3 x i8*]* @_ZTVZ3foovE2FA, i64 16} +// ITANIUM-DAG: !{!"[{{.*}}cfi-vcall.cpp]Z3foovE2FA", [3 x i8*]* @_ZTVZ3foovE2FA, i64 16} + +// MS-DAG: !{!"A@@", [2 x i8*]* @[[VTA]], i64 8} +// MS-DAG: !{!"B@@", [3 x i8*]* @[[VTB]], i64 8} +// MS-DAG: !{!"A@@", [2 x i8*]* @[[VTAinB]], i64 8} +// MS-DAG: !{!"A@@", [2 x i8*]* @[[VTAinC]], i64 8} +// MS-DAG: !{!"B@@", [3 x i8*]* @[[VTBinD]], i64 8} +// MS-DAG: !{!"[{{.*}}cfi-vcall.cpp]D@?A@@", [3 x i8*]* @[[VTBinD]], i64 8} +// MS-DAG: !{!"A@@", [2 x i8*]* @[[VTAinBinD]], i64 8} +// MS-DAG: !{!"A@@", [2 x i8*]* @[[VTFA]], i64 8} +// MS-DAG: !{!"[{{.*}}cfi-vcall.cpp]FA@?1??foo@@YAXXZ@", [2 x i8*]* @[[VTFA]], i64 8} -- 2.7.4