Rollup merge of #98998 - workingjubilee:naked-means-no-clothes-enforcement-technology, r=Amanieu

Remove branch target prologues from `#[naked] fn`

This patch hacks around rust-lang/rust#98768 for now via injecting appropriate attributes into the LLVMIR we emit for naked functions. I intend to pursue this upstream so that these attributes can be removed in general, but it's slow going wading through C++ for me.
This commit is contained in:
Dylan DPC 2022-07-18 21:14:43 +05:30 committed by GitHub
commit a027b01f33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 56 additions and 1 deletions

View file

@ -299,6 +299,12 @@ pub fn from_fn_attrs<'ll, 'tcx>(
} }
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
to_add.push(AttributeKind::Naked.create_attr(cx.llcx)); to_add.push(AttributeKind::Naked.create_attr(cx.llcx));
// HACK(jubilee): "indirect branch tracking" works by attaching prologues to functions.
// And it is a module-level attribute, so the alternative is pulling naked functions into new LLVM modules.
// Otherwise LLVM's "naked" functions come with endbr prefixes per https://github.com/rust-lang/rust/issues/98768
to_add.push(AttributeKind::NoCfCheck.create_attr(cx.llcx));
// Need this for AArch64.
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "branch-target-enforcement", "false"));
} }
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) { if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
// apply to return place instead of function (unlike all other attributes applied in this function) // apply to return place instead of function (unlike all other attributes applied in this function)

View file

@ -191,6 +191,7 @@ pub enum AttributeKind {
StackProtect = 32, StackProtect = 32,
NoUndef = 33, NoUndef = 33,
SanitizeMemTag = 34, SanitizeMemTag = 34,
NoCfCheck = 35,
} }
/// LLVMIntPredicate /// LLVMIntPredicate

View file

@ -84,6 +84,7 @@ enum LLVMRustAttribute {
StackProtect = 32, StackProtect = 32,
NoUndef = 33, NoUndef = 33,
SanitizeMemTag = 34, SanitizeMemTag = 34,
NoCfCheck = 35,
}; };
typedef struct OpaqueRustString *RustStringRef; typedef struct OpaqueRustString *RustStringRef;

View file

@ -176,6 +176,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
return Attribute::NoAlias; return Attribute::NoAlias;
case NoCapture: case NoCapture:
return Attribute::NoCapture; return Attribute::NoCapture;
case NoCfCheck:
return Attribute::NoCfCheck;
case NoInline: case NoInline:
return Attribute::NoInline; return Attribute::NoInline;
case NonNull: case NonNull:

View file

@ -0,0 +1,21 @@
// compile-flags: -C no-prepopulate-passes -Zbranch-protection=bti
// assembly-output: emit-asm
// needs-asm-support
// only-aarch64
#![crate_type = "lib"]
#![feature(naked_functions)]
use std::arch::asm;
// The problem at hand: Rust has adopted a fairly strict meaning for "naked functions",
// meaning "no prologue whatsoever, no, really, not one instruction."
// Unfortunately, aarch64's "branch target identification" works via hints at landing sites.
// LLVM implements this via making sure of that, even for functions with the naked attribute.
// So, we must emit an appropriate instruction instead!
#[no_mangle]
#[naked]
pub unsafe extern "C" fn _hlt() -> ! {
// CHECK-NOT: hint #34
// CHECK: hlt #0x1
asm!("hlt #1", options(noreturn))
}

View file

@ -0,0 +1,24 @@
// compile-flags: -C no-prepopulate-passes -Zcf-protection=full
// assembly-output: emit-asm
// needs-asm-support
// only-x86_64
#![crate_type = "lib"]
#![feature(naked_functions)]
use std::arch::asm;
// The problem at hand: Rust has adopted a fairly strict meaning for "naked functions",
// meaning "no prologue whatsoever, no, really, not one instruction."
// Unfortunately, x86's control-flow enforcement, specifically indirect branch protection,
// works by using an instruction for each possible landing site,
// and LLVM implements this via making sure of that.
#[no_mangle]
#[naked]
pub unsafe extern "sysv64" fn will_halt() -> ! {
// CHECK-NOT: endbr{{32|64}}
// CHECK: hlt
asm!("hlt", options(noreturn))
}
// what about aarch64?
// "branch-protection"=false

View file

@ -28,4 +28,4 @@ pub unsafe fn g() {
f(); f();
} }
// CHECK: attributes [[ATTR]] = { naked noinline{{.*}} } // CHECK: attributes [[ATTR]] = { naked{{.*}}noinline{{.*}} }