Cross-DSO control flow integrity (Clang part).

Clang-side cross-DSO CFI.

* Adds a command line flag -f[no-]sanitize-cfi-cross-dso.
* Links a runtime library when enabled.
* Emits __cfi_slowpath calls is bitset test fails.
* Emits extra hash-based bitsets for external CFI checks.
* Sets a module flag to enable __cfi_check generation during LTO.

This mode does not yet support diagnostics.

llvm-svn: 255694
This commit is contained in:
Evgeniy Stepanov
2015-12-15 23:00:20 +00:00
parent 67849d56c3
commit fd6f92d5cb
20 changed files with 489 additions and 52 deletions

View File

@@ -53,6 +53,7 @@
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MD5.h"
using namespace clang;
using namespace CodeGen;
@@ -439,6 +440,11 @@ void CodeGenModule::Release() {
getModule().addModuleFlag(llvm::Module::Error, "min_enum_size", EnumWidth);
}
if (CodeGenOpts.SanitizeCfiCrossDso) {
// Indicate that we want cross-DSO control flow integrity checks.
getModule().addModuleFlag(llvm::Module::Override, "Cross-DSO CFI", 1);
}
if (uint32_t PLevel = Context.getLangOpts().PICLevel) {
llvm::PICLevel::Level PL = llvm::PICLevel::Default;
switch (PLevel) {
@@ -736,6 +742,21 @@ void CodeGenModule::setFunctionDLLStorageClass(GlobalDecl GD, llvm::Function *F)
F->setDLLStorageClass(llvm::GlobalVariable::DefaultStorageClass);
}
llvm::ConstantInt *
CodeGenModule::CreateCfiIdForTypeMetadata(llvm::Metadata *MD) {
llvm::MDString *MDS = dyn_cast<llvm::MDString>(MD);
if (!MDS) return nullptr;
llvm::MD5 md5;
llvm::MD5::MD5Result result;
md5.update(MDS->getString());
md5.final(result);
uint64_t id = 0;
for (int i = 0; i < 8; ++i)
id |= static_cast<uint64_t>(result[i]) << (i * 8);
return llvm::ConstantInt::get(Int64Ty, id);
}
void CodeGenModule::setFunctionDefinitionAttributes(const FunctionDecl *D,
llvm::Function *F) {
setNonAliasAttributes(D, F);
@@ -928,6 +949,49 @@ static void setLinkageAndVisibilityForGV(llvm::GlobalValue *GV,
}
}
void CodeGenModule::CreateFunctionBitSetEntry(const FunctionDecl *FD,
llvm::Function *F) {
// Only if we are checking indirect calls.
if (!LangOpts.Sanitize.has(SanitizerKind::CFIICall))
return;
// Non-static class methods are handled via vtable pointer checks elsewhere.
if (isa<CXXMethodDecl>(FD) && !cast<CXXMethodDecl>(FD)->isStatic())
return;
// Additionally, if building with cross-DSO support...
if (CodeGenOpts.SanitizeCfiCrossDso) {
// Don't emit entries for function declarations. In cross-DSO mode these are
// handled with better precision at run time.
if (!FD->hasBody())
return;
// Skip available_externally functions. They won't be codegen'ed in the
// current module anyway.
if (getContext().GetGVALinkageForFunction(FD) == GVA_AvailableExternally)
return;
}
llvm::NamedMDNode *BitsetsMD =
getModule().getOrInsertNamedMetadata("llvm.bitsets");
llvm::Metadata *MD = CreateMetadataIdentifierForType(FD->getType());
llvm::Metadata *BitsetOps[] = {
MD, llvm::ConstantAsMetadata::get(F),
llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(Int64Ty, 0))};
BitsetsMD->addOperand(llvm::MDTuple::get(getLLVMContext(), BitsetOps));
// Emit a hash-based bit set entry for cross-DSO calls.
if (CodeGenOpts.SanitizeCfiCrossDso) {
if (auto TypeId = CreateCfiIdForTypeMetadata(MD)) {
llvm::Metadata *BitsetOps2[] = {
llvm::ConstantAsMetadata::get(TypeId),
llvm::ConstantAsMetadata::get(F),
llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(Int64Ty, 0))};
BitsetsMD->addOperand(llvm::MDTuple::get(getLLVMContext(), BitsetOps2));
}
}
}
void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, llvm::Function *F,
bool IsIncompleteFunction,
bool IsThunk) {
@@ -970,19 +1034,7 @@ void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, llvm::Function *F,
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));
}
CreateFunctionBitSetEntry(FD, F);
}
void CodeGenModule::addUsedGlobal(llvm::GlobalValue *GV) {
@@ -3874,14 +3926,28 @@ llvm::Metadata *CodeGenModule::CreateMetadataIdentifierForType(QualType T) {
return InternalId;
}
llvm::MDTuple *CodeGenModule::CreateVTableBitSetEntry(
llvm::GlobalVariable *VTable, CharUnits Offset, const CXXRecordDecl *RD) {
void CodeGenModule::CreateVTableBitSetEntry(llvm::NamedMDNode *BitsetsMD,
llvm::GlobalVariable *VTable,
CharUnits Offset,
const CXXRecordDecl *RD) {
llvm::Metadata *MD =
CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0));
llvm::Metadata *BitsetOps[] = {
CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0)),
llvm::ConstantAsMetadata::get(VTable),
MD, llvm::ConstantAsMetadata::get(VTable),
llvm::ConstantAsMetadata::get(
llvm::ConstantInt::get(Int64Ty, Offset.getQuantity()))};
return llvm::MDTuple::get(getLLVMContext(), BitsetOps);
BitsetsMD->addOperand(llvm::MDTuple::get(getLLVMContext(), BitsetOps));
if (CodeGenOpts.SanitizeCfiCrossDso) {
if (auto TypeId = CreateCfiIdForTypeMetadata(MD)) {
llvm::Metadata *BitsetOps2[] = {
llvm::ConstantAsMetadata::get(TypeId),
llvm::ConstantAsMetadata::get(VTable),
llvm::ConstantAsMetadata::get(
llvm::ConstantInt::get(Int64Ty, Offset.getQuantity()))};
BitsetsMD->addOperand(llvm::MDTuple::get(getLLVMContext(), BitsetOps2));
}
}
}
// Fills in the supplied string map with the set of target features for the