[ubsan] Using metadata instead of prologue data for function sanitizer

Information in the function `Prologue Data` is intentionally opaque.
When a function with `Prologue Data` is duplicated. The self (global
value) references inside `Prologue Data` is still pointing to the
original function. This may cause errors like `fatal error: error in backend: Cannot represent a difference across sections`.

This patch detaches the information from function `Prologue Data`
and attaches it to a function metadata node.

This and D116130 fix https://github.com/llvm/llvm-project/issues/49689.

Reviewed By: pcc

Differential Revision: https://reviews.llvm.org/D115844
This commit is contained in:
Yuanfang Chen 2022-06-27 11:33:45 -07:00
parent ae90bc8467
commit 6678f8e505
16 changed files with 144 additions and 62 deletions

View File

@ -559,29 +559,6 @@ bool CodeGenFunction::AlwaysEmitXRayTypedEvents() const {
XRayInstrKind::Typed);
}
llvm::Constant *
CodeGenFunction::EncodeAddrForUseInPrologue(llvm::Function *F,
llvm::Constant *Addr) {
// Addresses stored in prologue data can't require run-time fixups and must
// be PC-relative. Run-time fixups are undesirable because they necessitate
// writable text segments, which are unsafe. And absolute addresses are
// undesirable because they break PIE mode.
// Add a layer of indirection through a private global. Taking its address
// won't result in a run-time fixup, even if Addr has linkonce_odr linkage.
auto *GV = new llvm::GlobalVariable(CGM.getModule(), Addr->getType(),
/*isConstant=*/true,
llvm::GlobalValue::PrivateLinkage, Addr);
// Create a PC-relative address.
auto *GOTAsInt = llvm::ConstantExpr::getPtrToInt(GV, IntPtrTy);
auto *FuncAsInt = llvm::ConstantExpr::getPtrToInt(F, IntPtrTy);
auto *PCRelAsInt = llvm::ConstantExpr::getSub(GOTAsInt, FuncAsInt);
return (IntPtrTy == Int32Ty)
? PCRelAsInt
: llvm::ConstantExpr::getTrunc(PCRelAsInt, Int32Ty);
}
llvm::Value *
CodeGenFunction::DecodeAddrUsedInPrologue(llvm::Value *F,
llvm::Value *EncodedAddr) {
@ -937,12 +914,13 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
FD->getType(), EST_None);
llvm::Constant *FTRTTIConst =
CGM.GetAddrOfRTTIDescriptor(ProtoTy, /*ForEH=*/true);
llvm::Constant *FTRTTIConstEncoded =
EncodeAddrForUseInPrologue(Fn, FTRTTIConst);
llvm::Constant *PrologueStructElems[] = {PrologueSig, FTRTTIConstEncoded};
llvm::Constant *PrologueStructConst =
llvm::ConstantStruct::getAnon(PrologueStructElems, /*Packed=*/true);
Fn->setPrologueData(PrologueStructConst);
llvm::GlobalVariable *FTRTTIProxy =
CGM.GetOrCreateRTTIProxyGlobalVariable(FTRTTIConst);
llvm::LLVMContext &Ctx = Fn->getContext();
llvm::MDBuilder MDB(Ctx);
Fn->setMetadata(llvm::LLVMContext::MD_func_sanitize,
MDB.createRTTIPointerPrologue(PrologueSig, FTRTTIProxy));
CGM.addCompilerUsedGlobal(FTRTTIProxy);
}
}

View File

@ -2356,10 +2356,6 @@ public:
/// XRay typed event handling calls.
bool AlwaysEmitXRayTypedEvents() const;
/// Encode an address into a form suitable for use in a function prologue.
llvm::Constant *EncodeAddrForUseInPrologue(llvm::Function *F,
llvm::Constant *Addr);
/// Decode an address used in a function prologue, encoded by \c
/// EncodeAddrForUseInPrologue.
llvm::Value *DecodeAddrUsedInPrologue(llvm::Value *F,

View File

@ -1890,6 +1890,22 @@ CodeGenModule::getMostBaseClasses(const CXXRecordDecl *RD) {
return MostBases.takeVector();
}
llvm::GlobalVariable *
CodeGenModule::GetOrCreateRTTIProxyGlobalVariable(llvm::Constant *Addr) {
auto It = RTTIProxyMap.find(Addr);
if (It != RTTIProxyMap.end())
return It->second;
auto *FTRTTIProxy = new llvm::GlobalVariable(
TheModule, Addr->getType(),
/*isConstant=*/true, llvm::GlobalValue::PrivateLinkage, Addr,
"__llvm_rtti_proxy");
FTRTTIProxy->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
RTTIProxyMap[Addr] = FTRTTIProxy;
return FTRTTIProxy;
}
void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
llvm::Function *F) {
llvm::AttrBuilder B(F->getContext());

View File

@ -567,6 +567,8 @@ private:
MetadataTypeMap VirtualMetadataIdMap;
MetadataTypeMap GeneralizedMetadataIdMap;
llvm::DenseMap<const llvm::Constant *, llvm::GlobalVariable *> RTTIProxyMap;
public:
CodeGenModule(ASTContext &C, const HeaderSearchOptions &headersearchopts,
const PreprocessorOptions &ppopts,
@ -1442,6 +1444,9 @@ public:
std::vector<const CXXRecordDecl *>
getMostBaseClasses(const CXXRecordDecl *RD);
llvm::GlobalVariable *
GetOrCreateRTTIProxyGlobalVariable(llvm::Constant *Addr);
/// Get the declaration of std::terminate for the platform.
llvm::FunctionCallee getTerminateFn();

View File

@ -369,6 +369,19 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
Add &= ~NotAllowedWithMinimalRuntime;
}
if (llvm::opt::Arg *A = Args.getLastArg(options::OPT_mcmodel_EQ)) {
StringRef CM = A->getValue();
if (CM != "small" &&
(Add & SanitizerKind::Function & ~DiagnosedKinds)) {
if (DiagnoseErrors)
D.Diag(diag::err_drv_argument_only_allowed_with)
<< "-fsanitize=function"
<< "-mcmodel=small";
Add &= ~SanitizerKind::Function;
DiagnosedKinds |= SanitizerKind::Function;
}
}
// FIXME: Make CFI on member function calls compatible with cross-DSO CFI.
// There are currently two problems:
// - Virtual function call checks need to pass a pointer to the function

View File

@ -1,6 +1,7 @@
// RUN: %clang_cc1 -no-opaque-pointers -triple x86_64-linux-gnu -emit-llvm -o - %s -fsanitize=function -fno-sanitize-recover=all | FileCheck %s
// CHECK-LABEL: define{{.*}} void @_Z3funv() #0 prologue <{ i32, i32 }> <{ i32 846595819, i32 trunc (i64 sub (i64 ptrtoint (i8** @0 to i64), i64 ptrtoint (void ()* @_Z3funv to i64)) to i32) }> {
// CHECK: @[[PROXY:.*]] = private unnamed_addr constant i8* bitcast ({ i8*, i8* }* @_ZTIFvvE to i8*)
// CHECK: define{{.*}} void @_Z3funv() #0 !func_sanitize ![[FUNCSAN:.*]] {
void fun() {}
// CHECK-LABEL: define{{.*}} void @_Z6callerPFvvE(void ()* noundef %f)
@ -20,3 +21,5 @@ void fun() {}
// CHECK: [[LABEL3]]:
// CHECK: br label %[[LABEL4]], !nosanitize
void caller(void (*f)()) { f(); }
// CHECK: ![[FUNCSAN]] = !{i32 846595819, i8** @[[PROXY]]}

View File

@ -1,8 +1,8 @@
// RUN: %clang_cc1 -no-opaque-pointers -no-enable-noundef-analysis -std=c++11 -fsanitize=signed-integer-overflow,integer-divide-by-zero,float-divide-by-zero,shift-base,shift-exponent,unreachable,return,vla-bound,alignment,null,vptr,object-size,float-cast-overflow,bool,enum,array-bounds,function -fsanitize-recover=signed-integer-overflow,integer-divide-by-zero,float-divide-by-zero,shift-base,shift-exponent,vla-bound,alignment,null,vptr,object-size,float-cast-overflow,bool,enum,array-bounds,function -emit-llvm %s -o - -triple x86_64-linux-gnu | opt -instnamer -S | FileCheck %s
// RUN: %clang_cc1 -no-opaque-pointers -no-enable-noundef-analysis -std=c++11 -fsanitize=signed-integer-overflow,integer-divide-by-zero,float-divide-by-zero,shift-base,shift-exponent,unreachable,return,vla-bound,alignment,null,vptr,object-size,float-cast-overflow,bool,enum,array-bounds,function -fsanitize-recover=signed-integer-overflow,integer-divide-by-zero,float-divide-by-zero,shift-base,shift-exponent,vla-bound,alignment,null,vptr,object-size,float-cast-overflow,bool,enum,array-bounds,function -emit-llvm %s -o - -triple x86_64-linux-gnu | opt -instnamer -S | FileCheck %s --check-prefixes=CHECK,CHECK-FUNCSAN
// RUN: %clang_cc1 -no-opaque-pointers -no-enable-noundef-analysis -std=c++11 -fsanitize=vptr,address -fsanitize-recover=vptr,address -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-ASAN
// RUN: %clang_cc1 -no-opaque-pointers -no-enable-noundef-analysis -std=c++11 -fsanitize=vptr -fsanitize-recover=vptr -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=DOWNCAST-NULL
// RUN: %clang_cc1 -no-opaque-pointers -no-enable-noundef-analysis -std=c++11 -fsanitize=function -emit-llvm %s -o - -triple x86_64-linux-gnux32 | FileCheck %s --check-prefix=CHECK-X32
// RUN: %clang_cc1 -no-opaque-pointers -no-enable-noundef-analysis -std=c++11 -fsanitize=function -emit-llvm %s -o - -triple i386-linux-gnu | FileCheck %s --check-prefix=CHECK-X86
// RUN: %clang_cc1 -no-opaque-pointers -no-enable-noundef-analysis -std=c++11 -fsanitize=function -emit-llvm %s -o - -triple x86_64-linux-gnux32 | FileCheck %s --check-prefix=CHECK-FUNCSAN
// RUN: %clang_cc1 -no-opaque-pointers -no-enable-noundef-analysis -std=c++11 -fsanitize=function -emit-llvm %s -o - -triple i386-linux-gnu | FileCheck %s --check-prefix=CHECK-FUNCSAN
struct S {
double d;
@ -16,9 +16,7 @@ struct S {
// Check that type mismatch handler is not modified by ASan.
// CHECK-ASAN: private unnamed_addr global { { [{{.*}} x i8]*, i32, i32 }, { i16, i16, [4 x i8] }*, i8*, i8 } { {{.*}}, { i16, i16, [4 x i8] }* [[TYPE_DESCR]], {{.*}} }
// CHECK: [[IndirectRTTI_ZTIFvPFviEE:@.+]] = private constant i8* bitcast ({ i8*, i8* }* @_ZTIFvPFviEE to i8*)
// CHECK-X86: [[IndirectRTTI_ZTIFvPFviEE:@.+]] = private constant i8* bitcast ({ i8*, i8* }* @_ZTIFvPFviEE to i8*)
// CHECK-X32: [[IndirectRTTI_ZTIFvPFviEE:@.+]] = private constant i8* bitcast ({ i8*, i8* }* @_ZTIFvPFviEE to i8*)
// CHECK-FUNCSAN: [[PROXY:@.+]] = private unnamed_addr constant i8* bitcast ({ i8*, i8* }* @_ZTIFvPFviEE to i8*)
struct T : S {};
@ -399,10 +397,7 @@ void downcast_reference(B &b) {
// CHECK-NEXT: br i1 [[AND]]
}
//
// CHECK-LABEL: @_Z22indirect_function_callPFviE({{.*}} prologue <{ i32, i32 }> <{ i32 846595819, i32 trunc (i64 sub (i64 ptrtoint (i8** {{.*}} to i64), i64 ptrtoint (void (void (i32)*)* @_Z22indirect_function_callPFviE to i64)) to i32) }>
// CHECK-X32: @_Z22indirect_function_callPFviE({{.*}} prologue <{ i32, i32 }> <{ i32 846595819, i32 sub (i32 ptrtoint (i8** [[IndirectRTTI_ZTIFvPFviEE]] to i32), i32 ptrtoint (void (void (i32)*)* @_Z22indirect_function_callPFviE to i32)) }>
// CHECK-X86: @_Z22indirect_function_callPFviE({{.*}} prologue <{ i32, i32 }> <{ i32 846595819, i32 sub (i32 ptrtoint (i8** [[IndirectRTTI_ZTIFvPFviEE]] to i32), i32 ptrtoint (void (void (i32)*)* @_Z22indirect_function_callPFviE to i32)) }>
// CHECK-FUNCSAN: @_Z22indirect_function_callPFviE({{.*}} !func_sanitize ![[FUNCSAN:.*]] {
void indirect_function_call(void (*p)(int)) {
// CHECK: [[PTR:%.+]] = bitcast void (i32)* {{.*}} to <{ i32, i32 }>*
@ -483,34 +478,34 @@ void force_irgen() {
}
// CHECK-LABEL: define{{.*}} void @_ZN29FunctionSanitizerVirtualCalls1B1fEv
// CHECK-NOT: prologue
// CHECK-NOT: !func_sanitize
//
// CHECK-LABEL: define{{.*}} void @_ZTv0_n24_N29FunctionSanitizerVirtualCalls1B1fEv
// CHECK-NOT: prologue
// CHECK-NOT: !func_sanitize
//
// CHECK-LABEL: define{{.*}} void @_ZN29FunctionSanitizerVirtualCalls11force_irgenEv()
// CHECK: prologue
// CHECK: !func_sanitize
//
// CHECK-LABEL: define linkonce_odr void @_ZN29FunctionSanitizerVirtualCalls1AC1Ev
// CHECK-NOT: prologue
// CHECK-NOT: !func_sanitize
//
// CHECK-LABEL: define linkonce_odr void @_ZN29FunctionSanitizerVirtualCalls1A1gEv
// CHECK-NOT: prologue
// CHECK-NOT: !func_sanitize
//
// CHECK-LABEL: define linkonce_odr void @_ZN29FunctionSanitizerVirtualCalls1A1hEv
// CHECK-NOT: prologue
// CHECK-NOT: !func_sanitize
//
// CHECK-LABEL: define linkonce_odr void @_ZN29FunctionSanitizerVirtualCalls1BC1Ev
// CHECK-NOT: prologue
// CHECK-NOT: !func_sanitize
//
// CHECK-LABEL: define linkonce_odr void @_ZN29FunctionSanitizerVirtualCalls1B1bEv
// CHECK-NOT: prologue
// CHECK-NOT: !func_sanitize
//
// CHECK-LABEL: define linkonce_odr void @_ZN29FunctionSanitizerVirtualCalls1B1gEv
// CHECK-NOT: prologue
// CHECK-NOT: !func_sanitize
//
// CHECK-LABEL: define linkonce_odr void @_ZN29FunctionSanitizerVirtualCalls1B1qEv
// CHECK: prologue
// CHECK: !func_sanitize
}
@ -754,3 +749,5 @@ void ThisAlign::this_align_lambda_2() {
}
// CHECK: attributes [[NR_NUW]] = { noreturn nounwind }
// CHECK-FUNCSAN: ![[FUNCSAN]] = !{i32 846595819, i8** [[PROXY]]}

View File

@ -2,8 +2,8 @@
// Check that typeinfo recorded in function prolog doesn't have "Do" noexcept
// qualifier in its mangled name.
// CHECK: @[[RTTI:[0-9]+]] = private constant i8* bitcast ({ i8*, i8* }* @_ZTIFvvE to i8*)
// CHECK: define{{.*}} void @_Z1fv() #{{.*}} prologue <{ i32, i32 }> <{ i32 {{.*}}, i32 trunc (i64 sub (i64 ptrtoint (i8** @[[RTTI]] to i64), i64 ptrtoint (void ()* @_Z1fv to i64)) to i32) }>
// CHECK: [[PROXY:@.*]] = private unnamed_addr constant i8* bitcast ({ i8*, i8* }* @_ZTIFvvE to i8*)
// CHECK: define{{.*}} void @_Z1fv() #{{.*}} !func_sanitize ![[FUNCSAN:.*]] {
void f() noexcept {}
// CHECK: define{{.*}} void @_Z1gPDoFvvE
@ -13,3 +13,5 @@ void g(void (*p)() noexcept) {
// CHECK: icmp eq i8* %{{.*}}, bitcast ({ i8*, i8* }* @_ZTIFvvE to i8*), !nosanitize
p();
}
// CHECK: ![[FUNCSAN]] = !{i32 846595819, i8** [[PROXY]]}

View File

@ -942,3 +942,6 @@
// RUN: %clang -fsanitize=undefined,float-divide-by-zero %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-DIVBYZERO-UBSAN
// CHECK-DIVBYZERO-UBSAN: "-fsanitize={{.*}},float-divide-by-zero,{{.*}}"
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined,function -mcmodel=large %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION-CODE-MODEL
// CHECK-UBSAN-FUNCTION-CODE-MODEL: error: invalid argument '-fsanitize=function' only allowed with '-mcmodel=small'

View File

@ -5287,7 +5287,7 @@ multiple metadata attachments with the same identifier.
A transformation is required to drop any metadata attachment that it does not
know or know it can't preserve. Currently there is an exception for metadata
attachment to globals for ``!type`` and ``!absolute_symbol`` which can't be
attachment to globals for ``!func_sanitize``, ``!type`` and ``!absolute_symbol`` which can't be
unconditionally dropped unless the global is itself deleted.
Metadata attached to a module using named metadata may not be dropped, with
@ -7175,6 +7175,26 @@ Example:
%a.addr = alloca float*, align 8, !annotation !0
!0 = !{!"auto-init"}
'``func_sanitize``' Metadata
^^^^^^^^^^^^^^^^^^^^^^^^^
The ``func_sanitize`` metadata is used to attach two values for the function
sanitizer instrumentation. The first value is the ubsan function signature.
The second value is the address of the proxy variable which stores the address
of the RTTI descriptor. If :ref:`prologue <prologuedata>` and '``func_sanitize``'
are used at the same time, :ref:`prologue <prologuedata>` is emitted before
'``func_sanitize``' in the output.
Example:
.. code-block:: text
@__llvm_rtti_proxy = private unnamed_addr constant i8* bitcast ({ i8*, i8* }* @_ZTIFvvE to i8*)
define void @_Z3funv() !func_sanitize !0 {
return void
}
!0 = !{i32 846595819, i8** @__llvm_rtti_proxy}
Module Flags Metadata
=====================

View File

@ -43,3 +43,4 @@ LLVM_FIXED_MD_KIND(MD_vcall_visibility, "vcall_visibility", 28)
LLVM_FIXED_MD_KIND(MD_noundef, "noundef", 29)
LLVM_FIXED_MD_KIND(MD_annotation, "annotation", 30)
LLVM_FIXED_MD_KIND(MD_nosanitize, "nosanitize", 31)
LLVM_FIXED_MD_KIND(MD_func_sanitize, "func_sanitize", 32)

View File

@ -108,6 +108,10 @@ public:
/// Merge the new callback encoding \p NewCB into \p ExistingCallbacks.
MDNode *mergeCallbackEncodings(MDNode *ExistingCallbacks, MDNode *NewCB);
/// Return metadata feeding to the CodeGen about how to generate a function
/// prologue for the "function" santizier.
MDNode *createRTTIPointerPrologue(Constant *PrologueSig, Constant *RTTI);
//===------------------------------------------------------------------===//
// AA metadata.
//===------------------------------------------------------------------===//

View File

@ -1004,6 +1004,24 @@ void AsmPrinter::emitFunctionHeader() {
// Emit the prologue data.
if (F.hasPrologueData())
emitGlobalConstant(F.getParent()->getDataLayout(), F.getPrologueData());
// Emit the function prologue data for the indirect call sanitizer.
if (const MDNode *MD = F.getMetadata(LLVMContext::MD_func_sanitize)) {
assert(TM.getTargetTriple().getArch() == Triple::x86 ||
TM.getTargetTriple().getArch() == Triple::x86_64);
assert(MD->getNumOperands() == 2);
auto *PrologueSig = mdconst::extract<Constant>(MD->getOperand(0));
auto *FTRTTIProxy = mdconst::extract<Constant>(MD->getOperand(1));
assert(PrologueSig && FTRTTIProxy);
emitGlobalConstant(F.getParent()->getDataLayout(), PrologueSig);
const MCExpr *Proxy = lowerConstant(FTRTTIProxy);
const MCExpr *FnExp = MCSymbolRefExpr::create(CurrentFnSym, OutContext);
const MCExpr *PCRel = MCBinaryExpr::createSub(Proxy, FnExp, OutContext);
// Use 32 bit since only small code model is supported.
OutStreamer->emitValue(PCRel, 4u);
}
}
/// EmitFunctionEntryLabel - Emit the label that is the entrypoint for the

View File

@ -150,6 +150,14 @@ MDNode *MDBuilder::mergeCallbackEncodings(MDNode *ExistingCallbacks,
return MDNode::get(Context, Ops);
}
MDNode *MDBuilder::createRTTIPointerPrologue(Constant *PrologueSig,
Constant *RTTI) {
SmallVector<Metadata *, 4> Ops;
Ops.push_back(createConstant(PrologueSig));
Ops.push_back(createConstant(RTTI));
return MDNode::get(Context, Ops);
}
MDNode *MDBuilder::createAnonymousAARoot(StringRef Name, MDNode *Extra) {
SmallVector<Metadata *, 3> Args(1, nullptr);
if (Extra)

View File

@ -1217,7 +1217,11 @@ static GlobalVariable *createPrivateGlobalForSourceLoc(Module &M,
/// Check if \p G has been created by a trusted compiler pass.
static bool GlobalWasGeneratedByCompiler(GlobalVariable *G) {
// Do not instrument @llvm.global_ctors, @llvm.used, etc.
if (G->getName().startswith("llvm."))
if (G->getName().startswith("llvm.") ||
// Do not instrument gcov counter arrays.
G->getName().startswith("__llvm_gcov_ctr") ||
// Do not instrument rtti proxy symbols for function sanitizer.
G->getName().startswith("__llvm_rtti_proxy"))
return true;
// Do not instrument asan globals.
@ -1226,10 +1230,6 @@ static bool GlobalWasGeneratedByCompiler(GlobalVariable *G) {
G->getName().startswith(kODRGenPrefix))
return true;
// Do not instrument gcov counter arrays.
if (G->getName() == "__llvm_gcov_ctr")
return true;
return false;
}

View File

@ -0,0 +1,18 @@
; RUN: llc -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck %s
; CHECK: _Z3funv:
; CHECK: .cfi_startproc
; CHECK: .long 846595819
; CHECK: .long .L__llvm_rtti_proxy-_Z3funv
; CHECK: .L__llvm_rtti_proxy:
; CHECK: .quad i
; CHECK: .size .L__llvm_rtti_proxy, 8
@i = linkonce_odr constant i32 1
@__llvm_rtti_proxy = private unnamed_addr constant i32* @i
define dso_local void @_Z3funv() !func_sanitize !0 {
ret void
}
!0 = !{i32 846595819, i32** @__llvm_rtti_proxy}