Skip to content

Commit

Permalink
Implement diagnostic mode for -fsanitize=cfi*, -fsanitize=cfi-diag.
Browse files Browse the repository at this point in the history
This causes programs compiled with this flag to print a diagnostic when
a control flow integrity check fails instead of aborting. Diagnostics are
printed using UBSan's runtime library.

The main motivation of this feature over -fsanitize=vptr is fidelity with
the -fsanitize=cfi implementation: the diagnostics are printed under exactly
the same conditions as those which would cause -fsanitize=cfi to abort the
program. This means that the same restrictions apply regarding compiling
all translation units with -fsanitize=cfi, cross-DSO virtual calls are
forbidden, etc.

Differential Revision: http://reviews.llvm.org/D10268

llvm-svn: 240109
  • Loading branch information
pcc committed Jun 19, 2015
1 parent 2a56783 commit 6708c4a
Show file tree
Hide file tree
Showing 12 changed files with 118 additions and 55 deletions.
10 changes: 6 additions & 4 deletions clang/docs/UsersManual.rst
Expand Up @@ -1108,10 +1108,12 @@ are listed below.
the binary size increase caused by the sanitizer runtime is a concern.

This flag is only compatible with ``local-bounds``,
``unsigned-integer-overflow`` and sanitizers in the ``undefined``
group other than ``vptr``. If this flag is supplied together with
``-fsanitize=undefined``, the ``vptr`` sanitizer will be implicitly
disabled.
``unsigned-integer-overflow``, sanitizers in the ``cfi`` group and
sanitizers in the ``undefined`` group other than ``vptr``. If this flag
is supplied together with ``-fsanitize=undefined``, the ``vptr`` sanitizer
will be implicitly disabled.

This flag is enabled by default for sanitizers in the ``cfi`` group.

**-f[no-]sanitize-coverage=[type,features,...]**

Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CGCXXABI.h
Expand Up @@ -366,7 +366,8 @@ class CGCXXABI {
virtual llvm::Value *getVirtualFunctionPointer(CodeGenFunction &CGF,
GlobalDecl GD,
llvm::Value *This,
llvm::Type *Ty) = 0;
llvm::Type *Ty,
SourceLocation Loc) = 0;

/// Emit the ABI-specific virtual destructor call.
virtual llvm::Value *
Expand Down
56 changes: 39 additions & 17 deletions clang/lib/CodeGen/CGClass.cpp
Expand Up @@ -2134,17 +2134,21 @@ LeastDerivedClassWithSameLayout(const CXXRecordDecl *RD) {
}

void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXMethodDecl *MD,
llvm::Value *VTable) {
llvm::Value *VTable,
CFITypeCheckKind TCK,
SourceLocation Loc) {
const CXXRecordDecl *ClassDecl = MD->getParent();
if (!SanOpts.has(SanitizerKind::CFICastStrict))
ClassDecl = LeastDerivedClassWithSameLayout(ClassDecl);

EmitVTablePtrCheck(ClassDecl, VTable);
EmitVTablePtrCheck(ClassDecl, VTable, TCK, Loc);
}

void CodeGenFunction::EmitVTablePtrCheckForCast(QualType T,
llvm::Value *Derived,
bool MayBeNull) {
bool MayBeNull,
CFITypeCheckKind TCK,
SourceLocation Loc) {
if (!getLangOpts().CPlusPlus)
return;

Expand Down Expand Up @@ -2184,7 +2188,7 @@ void CodeGenFunction::EmitVTablePtrCheckForCast(QualType T,
}

llvm::Value *VTable = GetVTablePtr(Derived, Int8PtrTy);
EmitVTablePtrCheck(ClassDecl, VTable);
EmitVTablePtrCheck(ClassDecl, VTable, TCK, Loc);

if (MayBeNull) {
Builder.CreateBr(ContBlock);
Expand All @@ -2193,32 +2197,50 @@ void CodeGenFunction::EmitVTablePtrCheckForCast(QualType T,
}

void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD,
llvm::Value *VTable) {
llvm::Value *VTable,
CFITypeCheckKind TCK,
SourceLocation Loc) {
// FIXME: Add blacklisting scheme.
if (RD->isInStdNamespace())
return;

SanitizerScope SanScope(this);

std::string OutName;
llvm::raw_string_ostream Out(OutName);
CGM.getCXXABI().getMangleContext().mangleCXXVTableBitSet(RD, Out);

llvm::Value *BitSetName = llvm::MetadataAsValue::get(
getLLVMContext(), llvm::MDString::get(getLLVMContext(), Out.str()));

llvm::Value *BitSetTest = Builder.CreateCall(
CGM.getIntrinsic(llvm::Intrinsic::bitset_test),
{Builder.CreateBitCast(VTable, CGM.Int8PtrTy), BitSetName});
llvm::Value *CastedVTable = Builder.CreateBitCast(VTable, Int8PtrTy);
llvm::Value *BitSetTest =
Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::bitset_test),
{CastedVTable, BitSetName});

llvm::BasicBlock *ContBlock = createBasicBlock("vtable.check.cont");
llvm::BasicBlock *TrapBlock = createBasicBlock("vtable.check.trap");

Builder.CreateCondBr(BitSetTest, ContBlock, TrapBlock);

EmitBlock(TrapBlock);
Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::trap), {});
Builder.CreateUnreachable();
SanitizerMask M;
switch (TCK) {
case CFITCK_VCall:
M = SanitizerKind::CFIVCall;
break;
case CFITCK_NVCall:
M = SanitizerKind::CFINVCall;
break;
case CFITCK_DerivedCast:
M = SanitizerKind::CFIDerivedCast;
break;
case CFITCK_UnrelatedCast:
M = SanitizerKind::CFIUnrelatedCast;
break;
}

EmitBlock(ContBlock);
llvm::Constant *StaticData[] = {
EmitCheckSourceLocation(Loc),
EmitCheckTypeDescriptor(QualType(RD->getTypeForDecl(), 0)),
llvm::ConstantInt::get(Int8Ty, TCK),
};
EmitCheck(std::make_pair(BitSetTest, M), "cfi_bad_type", StaticData,
CastedVTable);
}

// FIXME: Ideally Expr::IgnoreParenNoopCasts should do this, but it doesn't do
Expand Down
6 changes: 4 additions & 2 deletions clang/lib/CodeGen/CGExpr.cpp
Expand Up @@ -3035,7 +3035,8 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
Derived, E->getType());

if (SanOpts.has(SanitizerKind::CFIDerivedCast))
EmitVTablePtrCheckForCast(E->getType(), Derived, /*MayBeNull=*/false);
EmitVTablePtrCheckForCast(E->getType(), Derived, /*MayBeNull=*/false,
CFITCK_DerivedCast, E->getLocStart());

return MakeAddrLValue(Derived, E->getType());
}
Expand All @@ -3048,7 +3049,8 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
ConvertType(CE->getTypeAsWritten()));

if (SanOpts.has(SanitizerKind::CFIUnrelatedCast))
EmitVTablePtrCheckForCast(E->getType(), V, /*MayBeNull=*/false);
EmitVTablePtrCheckForCast(E->getType(), V, /*MayBeNull=*/false,
CFITCK_UnrelatedCast, E->getLocStart());

return MakeAddrLValue(V, E->getType());
}
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/CodeGen/CGExprCXX.cpp
Expand Up @@ -254,12 +254,13 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr(
if (const CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(MD)) {
Callee = CGM.GetAddrOfFunction(GlobalDecl(Ctor, Ctor_Complete), Ty);
} else if (UseVirtualCall) {
Callee = CGM.getCXXABI().getVirtualFunctionPointer(*this, MD, This, Ty);
Callee = CGM.getCXXABI().getVirtualFunctionPointer(*this, MD, This, Ty,
CE->getLocStart());
} else {
if (SanOpts.has(SanitizerKind::CFINVCall) &&
MD->getParent()->isDynamicClass()) {
llvm::Value *VTable = GetVTablePtr(This, Int8PtrTy);
EmitVTablePtrCheckForCall(MD, VTable);
EmitVTablePtrCheckForCall(MD, VTable, CFITCK_NVCall, CE->getLocStart());
}

if (getLangOpts().AppleKext && MD->isVirtual() && HasQualifier)
Expand Down
8 changes: 6 additions & 2 deletions clang/lib/CodeGen/CGExprScalar.cpp
Expand Up @@ -1386,7 +1386,9 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
if (CGF.SanOpts.has(SanitizerKind::CFIUnrelatedCast)) {
if (auto PT = DestTy->getAs<PointerType>())
CGF.EmitVTablePtrCheckForCast(PT->getPointeeType(), Src,
/*MayBeNull=*/true);
/*MayBeNull=*/true,
CodeGenFunction::CFITCK_UnrelatedCast,
CE->getLocStart());
}

return Builder.CreateBitCast(Src, DstTy);
Expand Down Expand Up @@ -1420,7 +1422,9 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {

if (CGF.SanOpts.has(SanitizerKind::CFIDerivedCast))
CGF.EmitVTablePtrCheckForCast(DestTy->getPointeeType(), Derived,
/*MayBeNull=*/true);
/*MayBeNull=*/true,
CodeGenFunction::CFITCK_DerivedCast,
CE->getLocStart());

return Derived;
}
Expand Down
16 changes: 13 additions & 3 deletions clang/lib/CodeGen/CodeGenFunction.h
Expand Up @@ -1311,19 +1311,29 @@ class CodeGenFunction : public CodeGenTypeCache {
/// to by This.
llvm::Value *GetVTablePtr(llvm::Value *This, llvm::Type *Ty);

enum CFITypeCheckKind {
CFITCK_VCall,
CFITCK_NVCall,
CFITCK_DerivedCast,
CFITCK_UnrelatedCast,
};

/// \brief Derived is the presumed address of an object of type T after a
/// cast. If T is a polymorphic class type, emit a check that the virtual
/// table for Derived belongs to a class derived from T.
void EmitVTablePtrCheckForCast(QualType T, llvm::Value *Derived,
bool MayBeNull);
bool MayBeNull, CFITypeCheckKind TCK,
SourceLocation Loc);

/// EmitVTablePtrCheckForCall - Virtual method MD is being called via VTable.
/// If vptr CFI is enabled, emit a check that VTable is valid.
void EmitVTablePtrCheckForCall(const CXXMethodDecl *MD, llvm::Value *VTable);
void EmitVTablePtrCheckForCall(const CXXMethodDecl *MD, llvm::Value *VTable,
CFITypeCheckKind TCK, SourceLocation Loc);

/// EmitVTablePtrCheck - Emit a check that VTable is a valid virtual table for
/// RD using llvm.bitset.test.
void EmitVTablePtrCheck(const CXXRecordDecl *RD, llvm::Value *VTable);
void EmitVTablePtrCheck(const CXXRecordDecl *RD, llvm::Value *VTable,
CFITypeCheckKind TCK, SourceLocation Loc);

/// CanDevirtualizeMemberFunctionCalls - Checks whether virtual calls on given
/// expr can be devirtualized.
Expand Down
12 changes: 8 additions & 4 deletions clang/lib/CodeGen/ItaniumCXXABI.cpp
Expand Up @@ -204,7 +204,8 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {

llvm::Value *getVirtualFunctionPointer(CodeGenFunction &CGF, GlobalDecl GD,
llvm::Value *This,
llvm::Type *Ty) override;
llvm::Type *Ty,
SourceLocation Loc) override;

llvm::Value *EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
Expand Down Expand Up @@ -1439,13 +1440,15 @@ llvm::GlobalVariable *ItaniumCXXABI::getAddrOfVTable(const CXXRecordDecl *RD,
llvm::Value *ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
GlobalDecl GD,
llvm::Value *This,
llvm::Type *Ty) {
llvm::Type *Ty,
SourceLocation Loc) {
GD = GD.getCanonicalDecl();
Ty = Ty->getPointerTo()->getPointerTo();
llvm::Value *VTable = CGF.GetVTablePtr(This, Ty);

if (CGF.SanOpts.has(SanitizerKind::CFIVCall))
CGF.EmitVTablePtrCheckForCall(cast<CXXMethodDecl>(GD.getDecl()), VTable);
CGF.EmitVTablePtrCheckForCall(cast<CXXMethodDecl>(GD.getDecl()), VTable,
CodeGenFunction::CFITCK_VCall, Loc);

uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD);
llvm::Value *VFuncPtr =
Expand All @@ -1463,7 +1466,8 @@ llvm::Value *ItaniumCXXABI::EmitVirtualDestructorCall(
Dtor, getFromDtorType(DtorType));
llvm::Type *Ty = CGF.CGM.getTypes().GetFunctionType(*FInfo);
llvm::Value *Callee =
getVirtualFunctionPointer(CGF, GlobalDecl(Dtor, DtorType), This, Ty);
getVirtualFunctionPointer(CGF, GlobalDecl(Dtor, DtorType), This, Ty,
CE ? CE->getLocStart() : SourceLocation());

CGF.EmitCXXMemberOrOperatorCall(Dtor, Callee, ReturnValueSlot(), This,
/*ImplicitParam=*/nullptr, QualType(), CE);
Expand Down
10 changes: 6 additions & 4 deletions clang/lib/CodeGen/MicrosoftCXXABI.cpp
Expand Up @@ -221,8 +221,8 @@ class MicrosoftCXXABI : public CGCXXABI {
CharUnits VPtrOffset) override;

llvm::Value *getVirtualFunctionPointer(CodeGenFunction &CGF, GlobalDecl GD,
llvm::Value *This,
llvm::Type *Ty) override;
llvm::Value *This, llvm::Type *Ty,
SourceLocation Loc) override;

llvm::Value *EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
Expand Down Expand Up @@ -1588,7 +1588,8 @@ llvm::GlobalVariable *MicrosoftCXXABI::getAddrOfVTable(const CXXRecordDecl *RD,
llvm::Value *MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
GlobalDecl GD,
llvm::Value *This,
llvm::Type *Ty) {
llvm::Type *Ty,
SourceLocation Loc) {
GD = GD.getCanonicalDecl();
CGBuilderTy &Builder = CGF.Builder;

Expand Down Expand Up @@ -1616,7 +1617,8 @@ llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall(
const CGFunctionInfo *FInfo = &CGM.getTypes().arrangeCXXStructorDeclaration(
Dtor, StructorType::Deleting);
llvm::Type *Ty = CGF.CGM.getTypes().GetFunctionType(*FInfo);
llvm::Value *Callee = getVirtualFunctionPointer(CGF, GD, This, Ty);
llvm::Value *Callee = getVirtualFunctionPointer(
CGF, GD, This, Ty, CE ? CE->getLocStart() : SourceLocation());

ASTContext &Context = getContext();
llvm::Value *ImplicitParam = llvm::ConstantInt::get(
Expand Down
8 changes: 6 additions & 2 deletions clang/lib/Driver/SanitizerArgs.cpp
Expand Up @@ -25,7 +25,7 @@ using namespace clang::driver;
using namespace llvm::opt;

enum : SanitizerMask {
NeedsUbsanRt = Undefined | Integer,
NeedsUbsanRt = Undefined | Integer | CFI,
NotAllowedWithTrap = Vptr,
RequiresPIE = Memory | DataFlow,
NeedsUnwindTables = Address | Thread | Memory | DataFlow,
Expand All @@ -35,7 +35,8 @@ enum : SanitizerMask {
LegacyFsanitizeRecoverMask = Undefined | Integer,
NeedsLTO = CFI,
TrappingSupported =
(Undefined & ~Vptr) | UnsignedIntegerOverflow | LocalBounds,
(Undefined & ~Vptr) | UnsignedIntegerOverflow | LocalBounds | CFI,
TrappingDefault = CFI,
};

enum CoverageFeature {
Expand Down Expand Up @@ -166,6 +167,9 @@ static SanitizerMask parseSanitizeTrapArgs(const Driver &D,
}
}

// Apply default trapping behavior.
TrappingKinds |= TrappingDefault & ~TrapRemove;

return TrappingKinds;
}

Expand Down
18 changes: 9 additions & 9 deletions clang/test/CodeGenCXX/cfi-cast.cpp
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-derived-cast -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-DCAST %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-unrelated-cast -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-UCAST %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-unrelated-cast,cfi-cast-strict -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-UCAST-STRICT %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-derived-cast -fsanitize-trap=cfi-derived-cast -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-DCAST %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-unrelated-cast -fsanitize-trap=cfi-unrelated-cast -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-UCAST %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-unrelated-cast,cfi-cast-strict -fsanitize-trap=cfi-unrelated-cast,cfi-cast-strict -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-UCAST-STRICT %s

// In this test the main thing we are searching for is something like
// 'metadata !"1B"' where "1B" is the mangled name of the class we are
Expand All @@ -19,7 +19,7 @@ struct C : A {};
// CHECK-DCAST-LABEL: define void @_Z3abpP1A
void abp(A *a) {
// CHECK-DCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B")
// CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]]
// CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]]

// CHECK-DCAST: [[TRAPBB]]
// CHECK-DCAST-NEXT: call void @llvm.trap()
Expand All @@ -33,7 +33,7 @@ void abp(A *a) {
// CHECK-DCAST-LABEL: define void @_Z3abrR1A
void abr(A &a) {
// CHECK-DCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B")
// CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]]
// CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]]

// CHECK-DCAST: [[TRAPBB]]
// CHECK-DCAST-NEXT: call void @llvm.trap()
Expand All @@ -47,7 +47,7 @@ void abr(A &a) {
// CHECK-DCAST-LABEL: define void @_Z4abrrO1A
void abrr(A &&a) {
// CHECK-DCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B")
// CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]]
// CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]]

// CHECK-DCAST: [[TRAPBB]]
// CHECK-DCAST-NEXT: call void @llvm.trap()
Expand All @@ -61,7 +61,7 @@ void abrr(A &&a) {
// CHECK-UCAST-LABEL: define void @_Z3vbpPv
void vbp(void *p) {
// CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B")
// CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]]
// CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]]

// CHECK-UCAST: [[TRAPBB]]
// CHECK-UCAST-NEXT: call void @llvm.trap()
Expand All @@ -75,7 +75,7 @@ void vbp(void *p) {
// CHECK-UCAST-LABEL: define void @_Z3vbrRc
void vbr(char &r) {
// CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B")
// CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]]
// CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]]

// CHECK-UCAST: [[TRAPBB]]
// CHECK-UCAST-NEXT: call void @llvm.trap()
Expand All @@ -89,7 +89,7 @@ void vbr(char &r) {
// CHECK-UCAST-LABEL: define void @_Z4vbrrOc
void vbrr(char &&r) {
// CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B")
// CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]]
// CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]]

// CHECK-UCAST: [[TRAPBB]]
// CHECK-UCAST-NEXT: call void @llvm.trap()
Expand Down

0 comments on commit 6708c4a

Please sign in to comment.