CFI: Introduce -fsanitize=cfi-icall flag.

This flag causes the compiler to emit bit set entries for functions as well
as runtime bitset checks at indirect call sites. Depends on the new function
bitset mechanism.

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

llvm-svn: 247238
This commit is contained in:
Peter Collingbourne 2015-09-10 02:17:40 +00:00
parent d3b904d440
commit 2c7f7e31c4
19 changed files with 229 additions and 125 deletions

View File

@ -20,12 +20,14 @@ program's control flow. These schemes have been optimized for performance,
allowing developers to enable them in release builds.
To enable Clang's available CFI schemes, use the flag ``-fsanitize=cfi``.
As currently implemented, CFI relies on link-time optimization (LTO); so it is
required to specify ``-flto``, and the linker used must support LTO, for example
via the `gold plugin`_. To allow the checks to be implemented efficiently,
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.
As currently implemented, all of Clang's CFI schemes (``cfi-vcall``,
``cfi-derived-cast``, ``cfi-unrelated-cast``, ``cfi-nvcall``, ``cfi-icall``)
rely on link-time optimization (LTO); so it is required to specify
``-flto``, and the linker used must support LTO, for example via the `gold
plugin`_. To allow the checks to be implemented efficiently, 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 member function calls and
bad cast checking. More schemes are under development.
@ -123,6 +125,54 @@ member functions on class instances with specific properties that works under
most compilers and should not have security implications, so we allow it by
default. It can be disabled with ``-fsanitize=cfi-cast-strict``.
Indirect Function Call Checking
-------------------------------
This scheme checks that function calls take place using a function of the
correct dynamic type; that is, the dynamic type of the function must match
the static type used at the call. This CFI scheme can be enabled on its own
using ``-fsanitize=cfi-icall``.
For this scheme to work, each indirect function call in the program, other
than calls in :ref:`blacklisted <cfi-blacklist>` functions, must call a
function which was either compiled with ``-fsanitize=cfi-icall`` enabled,
or whose address was taken by a function in a translation unit compiled with
``-fsanitize=cfi-icall``.
If a function in a translation unit compiled with ``-fsanitize=cfi-icall``
takes the address of a function not compiled with ``-fsanitize=cfi-icall``,
that address may differ from the address taken by a function in a translation
unit not compiled with ``-fsanitize=cfi-icall``. This is technically a
violation of the C and C++ standards, but it should not affect most programs.
Each translation unit compiled with ``-fsanitize=cfi-icall`` must be
statically linked into the program or shared library, and calls across
shared library boundaries are handled as if the callee was not compiled with
``-fsanitize=cfi-icall``.
This scheme is currently only supported on the x86 and x86_64 architectures.
``-fsanitize=cfi-icall`` and ``-fsanitize=function``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This tool is similar to ``-fsanitize=function`` in that both tools check
the types of function calls. However, the two tools occupy different points
on the design space; ``-fsanitize=function`` is a developer tool designed
to find bugs in local development builds, whereas ``-fsanitize=cfi-icall``
is a security hardening mechanism designed to be deployed in release builds.
``-fsanitize=function`` has a higher space and time overhead due to a more
complex type check at indirect call sites, as well as a need for run-time
type information (RTTI), which may make it unsuitable for deployment. Because
of the need for RTTI, ``-fsanitize=function`` can only be used with C++
programs, whereas ``-fsanitize=cfi-icall`` can protect both C and C++ programs.
On the other hand, ``-fsanitize=function`` conforms more closely with the C++
standard and user expectations around interaction with shared libraries;
the identity of function pointers is maintained, and calls across shared
library boundaries are no different from calls within a single program or
shared library.
.. _cfi-blacklist:
Blacklist

View File

@ -273,3 +273,11 @@ Eliminating Bit Vector Checks for All-Ones Bit Vectors
If the bit vector is all ones, the bit vector check is redundant; we simply
need to check that the address is in range and well aligned. This is more
likely to occur if the virtual tables are padded.
Forward-Edge CFI for Indirect Function Calls
============================================
Sorry, no documentation yet, but see the comments at the top of
``LowerBitSets::buildBitSetsFromFunctions`` in `LowerBitSets.cpp`_.
.. _LowerBitSets.cpp: http://llvm.org/klaus/llvm/blob/master/lib/Transforms/IPO/LowerBitSets.cpp

View File

@ -144,9 +144,6 @@ public:
/// across translation units so it can be used with LTO.
virtual void mangleTypeName(QualType T, raw_ostream &) = 0;
virtual void mangleCXXVTableBitSet(const CXXRecordDecl *RD,
raw_ostream &) = 0;
/// @}
};

View File

@ -84,11 +84,13 @@ SANITIZER("dataflow", DataFlow)
// Control Flow Integrity
SANITIZER("cfi-cast-strict", CFICastStrict)
SANITIZER("cfi-derived-cast", CFIDerivedCast)
SANITIZER("cfi-icall", CFIICall)
SANITIZER("cfi-unrelated-cast", CFIUnrelatedCast)
SANITIZER("cfi-nvcall", CFINVCall)
SANITIZER("cfi-vcall", CFIVCall)
SANITIZER_GROUP("cfi", CFI,
CFIDerivedCast | CFIUnrelatedCast | CFINVCall | CFIVCall)
CFIDerivedCast | CFIICall | CFIUnrelatedCast | CFINVCall |
CFIVCall)
// Safe Stack
SANITIZER("safe-stack", SafeStack)

View File

@ -174,8 +174,6 @@ public:
void mangleStringLiteral(const StringLiteral *, raw_ostream &) override;
void mangleCXXVTableBitSet(const CXXRecordDecl *RD, raw_ostream &) override;
bool getNextDiscriminator(const NamedDecl *ND, unsigned &disc) {
// Lambda closure types are already numbered.
if (isLambda(ND))
@ -4098,21 +4096,6 @@ void ItaniumMangleContextImpl::mangleTypeName(QualType Ty, raw_ostream &Out) {
mangleCXXRTTIName(Ty, Out);
}
void ItaniumMangleContextImpl::mangleCXXVTableBitSet(const CXXRecordDecl *RD,
raw_ostream &Out) {
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() << "]";
}
CXXNameMangler Mangler(*this, Out);
Mangler.mangleType(QualType(RD->getTypeForDecl(), 0));
}
void ItaniumMangleContextImpl::mangleStringLiteral(const StringLiteral *, raw_ostream &) {
llvm_unreachable("Can't mangle string literals");
}

View File

@ -161,8 +161,6 @@ public:
void mangleSEHFinallyBlock(const NamedDecl *EnclosingDecl,
raw_ostream &Out) override;
void mangleStringLiteral(const StringLiteral *SL, raw_ostream &Out) override;
void mangleCXXVTableBitSet(const CXXRecordDecl *RD,
raw_ostream &Out) override;
bool getNextDiscriminator(const NamedDecl *ND, unsigned &disc) {
// Lambda closure types are already numbered.
if (isLambda(ND))
@ -2776,21 +2774,6 @@ void MicrosoftMangleContextImpl::mangleStringLiteral(const StringLiteral *SL,
Mangler.getStream() << '@';
}
void MicrosoftMangleContextImpl::mangleCXXVTableBitSet(const CXXRecordDecl *RD,
raw_ostream &Out) {
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 *
MicrosoftMangleContext::create(ASTContext &Context, DiagnosticsEngine &Diags) {
return new MicrosoftMangleContextImpl(Context, Diags);

View File

@ -2473,12 +2473,9 @@ void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD,
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()));
getLLVMContext(),
CGM.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0)));
llvm::Value *CastedVTable = Builder.CreateBitCast(VTable, Int8PtrTy);
llvm::Value *BitSetTest =

View File

@ -3780,6 +3780,29 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, llvm::Value *Callee,
}
}
// If we are checking indirect calls and this call is indirect, check that the
// function pointer is a member of the bit set for the function type.
if (SanOpts.has(SanitizerKind::CFIICall) &&
(!TargetDecl || !isa<FunctionDecl>(TargetDecl))) {
SanitizerScope SanScope(this);
llvm::Value *BitSetName = llvm::MetadataAsValue::get(
getLLVMContext(),
CGM.CreateMetadataIdentifierForType(QualType(FnType, 0)));
llvm::Value *CastedCallee = Builder.CreateBitCast(Callee, Int8PtrTy);
llvm::Value *BitSetTest =
Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::bitset_test),
{CastedCallee, BitSetName});
llvm::Constant *StaticData[] = {
EmitCheckSourceLocation(E->getLocStart()),
EmitCheckTypeDescriptor(QualType(FnType, 0)),
};
EmitCheck(std::make_pair(BitSetTest, SanitizerKind::CFIICall),
"cfi_bad_icall", StaticData, CastedCallee);
}
CallArgList Args;
if (Chain)
Args.add(RValue::get(Builder.CreateBitCast(Chain, CGM.VoidPtrTy)),

View File

@ -893,41 +893,45 @@ void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable,
CharUnits PointerWidth =
Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0));
std::vector<llvm::MDTuple *> BitsetEntries;
typedef std::pair<const CXXRecordDecl *, unsigned> BSEntry;
std::vector<BSEntry> BitsetEntries;
// Create a bit set entry for each address point.
for (auto &&AP : VTLayout.getAddressPoints()) {
if (IsCFIBlacklistedRecord(AP.first.getBase()))
continue;
BitsetEntries.push_back(CreateVTableBitSetEntry(
VTable, PointerWidth * AP.second, AP.first.getBase()));
BitsetEntries.push_back(std::make_pair(AP.first.getBase(), AP.second));
}
// Sort the bit set entries for determinism.
std::sort(BitsetEntries.begin(), BitsetEntries.end(), [](llvm::MDTuple *T1,
llvm::MDTuple *T2) {
if (T1 == T2)
std::sort(BitsetEntries.begin(), BitsetEntries.end(),
[this](const BSEntry &E1, const BSEntry &E2) {
if (&E1 == &E2)
return false;
StringRef S1 = cast<llvm::MDString>(T1->getOperand(0))->getString();
StringRef S2 = cast<llvm::MDString>(T2->getOperand(0))->getString();
std::string S1;
llvm::raw_string_ostream O1(S1);
getCXXABI().getMangleContext().mangleTypeName(
QualType(E1.first->getTypeForDecl(), 0), O1);
O1.flush();
std::string S2;
llvm::raw_string_ostream O2(S2);
getCXXABI().getMangleContext().mangleTypeName(
QualType(E2.first->getTypeForDecl(), 0), O2);
O2.flush();
if (S1 < S2)
return true;
if (S1 != S2)
return false;
uint64_t Offset1 = cast<llvm::ConstantInt>(
cast<llvm::ConstantAsMetadata>(T1->getOperand(2))
->getValue())->getZExtValue();
uint64_t Offset2 = cast<llvm::ConstantInt>(
cast<llvm::ConstantAsMetadata>(T2->getOperand(2))
->getValue())->getZExtValue();
assert(Offset1 != Offset2);
return Offset1 < Offset2;
return E1.second < E2.second;
});
llvm::NamedMDNode *BitsetsMD =
getModule().getOrInsertNamedMetadata("llvm.bitsets");
for (auto BitsetEntry : BitsetEntries)
BitsetsMD->addOperand(BitsetEntry);
BitsetsMD->addOperand(CreateVTableBitSetEntry(
VTable, PointerWidth * BitsetEntry.second, BitsetEntry.first));
}

View File

@ -941,6 +941,20 @@ void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, llvm::Function *F,
if (FD->isReplaceableGlobalAllocationFunction())
F->addAttribute(llvm::AttributeSet::FunctionIndex,
llvm::Attribute::NoBuiltin);
// If we are checking indirect calls and this is not a non-static member
// function, emit a bit set entry for the function type.
if (LangOpts.Sanitize.has(SanitizerKind::CFIICall) &&
!(isa<CXXMethodDecl>(FD) && !cast<CXXMethodDecl>(FD)->isStatic())) {
llvm::NamedMDNode *BitsetsMD =
getModule().getOrInsertNamedMetadata("llvm.bitsets");
llvm::Metadata *BitsetOps[] = {
CreateMetadataIdentifierForType(FD->getType()),
llvm::ConstantAsMetadata::get(F),
llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(Int64Ty, 0))};
BitsetsMD->addOperand(llvm::MDTuple::get(getLLVMContext(), BitsetOps));
}
}
void CodeGenModule::addUsedGlobal(llvm::GlobalValue *GV) {
@ -3824,12 +3838,8 @@ llvm::Metadata *CodeGenModule::CreateMetadataIdentifierForType(QualType T) {
llvm::MDTuple *CodeGenModule::CreateVTableBitSetEntry(
llvm::GlobalVariable *VTable, CharUnits Offset, const CXXRecordDecl *RD) {
std::string OutName;
llvm::raw_string_ostream Out(OutName);
getCXXABI().getMangleContext().mangleCXXVTableBitSet(RD, Out);
llvm::Metadata *BitsetOps[] = {
llvm::MDString::get(getLLVMContext(), Out.str()),
CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0)),
llvm::ConstantAsMetadata::get(VTable),
llvm::ConstantAsMetadata::get(
llvm::ConstantInt::get(Int64Ty, Offset.getQuantity()))};

View File

@ -495,6 +495,6 @@ SanitizerMask ToolChain::getSupportedSanitizers() const {
// Return sanitizers which don't require runtime support and are not
// platform or architecture-dependent.
using namespace SanitizerKind;
return (Undefined & ~Vptr & ~Function) | CFI | CFICastStrict |
return (Undefined & ~Vptr & ~Function) | (CFI & ~CFIICall) | CFICastStrict |
UnsignedIntegerOverflow | LocalBounds;
}

View File

@ -3764,6 +3764,7 @@ SanitizerMask Linux::getSupportedSanitizers() const {
if (IsX86_64 || IsMIPS64 || IsPowerPC64)
Res |= SanitizerKind::Memory;
if (IsX86 || IsX86_64) {
Res |= SanitizerKind::CFIICall;
Res |= SanitizerKind::Function;
Res |= SanitizerKind::SafeStack;
}

View File

@ -0,0 +1,20 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-icall -fsanitize-trap=cfi-icall -emit-llvm -o - %s | FileCheck --check-prefix=ITANIUM %s
// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fsanitize=cfi-icall -fsanitize-trap=cfi-icall -emit-llvm -o - %s | FileCheck --check-prefix=MS %s
// Tests that we assign appropriate identifiers to unprototyped functions.
void f() {
}
void xf();
void g(int b) {
void (*fp)() = b ? f : xf;
// ITANIUM: call i1 @llvm.bitset.test(i8* {{.*}}, metadata !"_ZTSFvE")
fp();
}
// ITANIUM-DAG: !{!"_ZTSFvE", void ()* @f, i64 0}
// ITANIUM-DAG: !{!"_ZTSFvE", void (...)* @xf, i64 0}
// MS-DAG: !{!"?6AX@Z", void ()* @f, i64 0}
// MS-DAG: !{!"?6AX@Z", void (...)* @xf, i64 0}

View File

@ -18,7 +18,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: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B")
// CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]]
// CHECK-DCAST: [[TRAPBB]]
@ -32,7 +32,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: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B")
// CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]]
// CHECK-DCAST: [[TRAPBB]]
@ -46,7 +46,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: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B")
// CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]]
// CHECK-DCAST: [[TRAPBB]]
@ -60,7 +60,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: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B")
// CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]]
// CHECK-UCAST: [[TRAPBB]]
@ -74,7 +74,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: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B")
// CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]]
// CHECK-UCAST: [[TRAPBB]]
@ -88,7 +88,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: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B")
// CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]]
// CHECK-UCAST: [[TRAPBB]]
@ -103,23 +103,23 @@ void vbrr(char &&r) {
// CHECK-UCAST-LABEL: define void @_Z3vcpPv
// CHECK-UCAST-STRICT-LABEL: define void @_Z3vcpPv
void vcp(void *p) {
// CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1A")
// CHECK-UCAST-STRICT: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1C")
// CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1A")
// CHECK-UCAST-STRICT: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1C")
static_cast<C*>(p);
}
// CHECK-UCAST-LABEL: define void @_Z3bcpP1B
// CHECK-UCAST-STRICT-LABEL: define void @_Z3bcpP1B
void bcp(B *p) {
// CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1A")
// CHECK-UCAST-STRICT: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1C")
// CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1A")
// CHECK-UCAST-STRICT: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1C")
(C *)p;
}
// CHECK-UCAST-LABEL: define void @_Z8bcp_callP1B
// CHECK-UCAST-STRICT-LABEL: define void @_Z8bcp_callP1B
void bcp_call(B *p) {
// CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1A")
// CHECK-UCAST-STRICT: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1C")
// CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1A")
// CHECK-UCAST-STRICT: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1C")
((C *)p)->f();
}

View File

@ -0,0 +1,23 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-icall -fsanitize-trap=cfi-icall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM %s
// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fsanitize=cfi-icall -fsanitize-trap=cfi-icall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=MS %s
// Tests that we assign unnamed metadata nodes to functions whose types have
// internal linkage.
namespace {
struct S {};
void f(S *s) {
}
}
void g() {
void (*fp)(S *) = f;
// CHECK: call i1 @llvm.bitset.test(i8* {{.*}}, metadata ![[VOIDS:[0-9]+]])
fp(0);
}
// ITANIUM: !{![[VOIDS]], void (%"struct.(anonymous namespace)::S"*)* @_ZN12_GLOBAL__N_11fEPNS_1SE, i64 0}
// MS: !{![[VOIDS]], void (%"struct.(anonymous namespace)::S"*)* @"\01?f@?A@@YAXPEAUS@?A@@@Z", i64 0}

View File

@ -8,5 +8,5 @@ struct A {
A::A() {}
// RTTI: !{!"A@@", [2 x i8*]* {{.*}}, i64 8}
// NO-RTTI: !{!"A@@", [1 x i8*]* {{.*}}, i64 0}
// RTTI: !{!"?AUA@@", [2 x i8*]* {{.*}}, i64 8}
// NO-RTTI: !{!"?AUA@@", [1 x i8*]* {{.*}}, i64 0}

View File

@ -17,8 +17,8 @@ struct C : A {
// 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")
// CHECK: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B")
// CHECK-STRICT: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B")
b->g();
}
@ -29,7 +29,7 @@ extern "C" void cg(C *c) {
// 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")
// CHECK: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1A")
// CHECK-STRICT: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1C")
c->g();
}

View File

@ -60,8 +60,8 @@ void D::h() {
// ITANIUM: define void @_Z2afP1A
// MS: define void @"\01?af@@YAXPEAUA@@@Z"
void af(A *a) {
// ITANIUM: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"1A")
// MS: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"A@@")
// ITANIUM: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"_ZTS1A")
// MS: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"?AUA@@")
// CHECK-NEXT: br i1 [[P]], label %[[CONTBB:[^ ,]*]], label %[[TRAPBB:[^ ,]*]]
// CHECK-NEXT: {{^$}}
@ -82,24 +82,24 @@ void af(A *a) {
// ITANIUM: define internal void @_Z3df1PN12_GLOBAL__N_11DE
// MS: define internal void @"\01?df1@@YAXPEAUD@?A@@@Z"
void df1(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 !"A@@")
// ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]])
// MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"?AUA@@")
d->f();
}
// 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@@")
// ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B")
// MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"?AUB@@")
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@@")
// ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata ![[DTYPE]])
// MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]])
d->h();
}
@ -150,8 +150,8 @@ struct D : C {
// ITANIUM: define void @_ZN5test21fEPNS_1DE
// MS: define void @"\01?f@test2@@YAXPEAUD@1@@Z"
void f(D *d) {
// ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"N5test21DE")
// MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"A@test2@@")
// ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTSN5test21DE")
// MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"?AUA@test2@@")
d->m_fn1();
}
@ -161,28 +161,28 @@ void f(D *d) {
// 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}
// ITANIUM-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTV1A, i64 16}
// ITANIUM-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32}
// ITANIUM-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32}
// ITANIUM-DAG: !{!"_ZTS1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64}
// ITANIUM-DAG: !{!"_ZTS1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32}
// ITANIUM-DAG: !{!"_ZTS1A", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
// ITANIUM-DAG: !{!"_ZTS1B", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
// ITANIUM-DAG: !{!"_ZTS1C", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88}
// ITANIUM-DAG: !{![[DTYPE]], [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
// ITANIUM-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTV1B, i64 32}
// ITANIUM-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTV1B, i64 32}
// ITANIUM-DAG: !{!"_ZTS1A", [5 x i8*]* @_ZTV1C, i64 32}
// ITANIUM-DAG: !{!"_ZTS1C", [5 x i8*]* @_ZTV1C, i64 32}
// ITANIUM-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTVZ3foovE2FA, i64 16}
// ITANIUM-DAG: !{!{{[0-9]+}}, [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}
// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTA]], i64 8}
// MS-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTB]], i64 8}
// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinB]], i64 8}
// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinC]], i64 8}
// MS-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTBinD]], i64 8}
// MS-DAG: !{![[DTYPE]], [3 x i8*]* @[[VTBinD]], i64 8}
// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinBinD]], i64 8}
// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTFA]], i64 8}
// MS-DAG: !{!{{[0-9]+}}, [2 x i8*]* @[[VTFA]], i64 8}

View File

@ -234,7 +234,7 @@
// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-unrelated-cast -flto -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-UCAST
// RUN: %clang -target x86_64-linux-gnu -flto -fsanitize=cfi-nvcall -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-NVCALL
// RUN: %clang -target x86_64-linux-gnu -flto -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: -emit-llvm-bc{{.*}}-fsanitize=cfi-derived-cast,cfi-icall,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-NVCALL: -emit-llvm-bc{{.*}}-fsanitize=cfi-nvcall
@ -243,6 +243,9 @@
// RUN: %clang -target x86_64-linux-gnu -flto -fsanitize=cfi-derived-cast -fno-lto -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-NOLTO
// CHECK-CFI-NOLTO: '-fsanitize=cfi-derived-cast' only allowed with '-flto'
// RUN: %clang -target mips-unknown-linux -fsanitize=cfi-icall %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-ICALL-MIPS
// CHECK-CFI-ICALL-MIPS: unsupported option '-fsanitize=cfi-icall' for target 'mips-unknown-linux'
// RUN: %clang -target x86_64-linux-gnu -fsanitize-trap=address -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-TRAP
// CHECK-ASAN-TRAP: error: unsupported argument 'address' to option '-fsanitize-trap'