From 92174f988b0cde631ffac0dbcc9f8fca413d566e Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 6 Jul 2022 19:07:52 -0700 Subject: [PATCH 1/2] Stop emitting CET prologues for naked functions We can apply nocf_check as a hack for now. --- compiler/rustc_codegen_llvm/src/attributes.rs | 4 ++++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + .../rustc_llvm/llvm-wrapper/LLVMWrapper.h | 1 + .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 2 ++ .../assembly/x86_64-naked-fn-no-cet-prolog.rs | 24 +++++++++++++++++++ src/test/codegen/naked-noinline.rs | 2 +- 6 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/test/assembly/x86_64-naked-fn-no-cet-prolog.rs diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 9394d60134f8c..8c72f7dc06f25 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -299,6 +299,10 @@ pub fn from_fn_attrs<'ll, 'tcx>( } if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { 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)); } if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) { // apply to return place instead of function (unlike all other attributes applied in this function) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 5ebc2d6139fc8..2c8e804fff581 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -191,6 +191,7 @@ pub enum AttributeKind { StackProtect = 32, NoUndef = 33, SanitizeMemTag = 34, + NoCfCheck = 35, } /// LLVMIntPredicate diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index a2b0e9b4d29d8..9fe84a6309b9c 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -84,6 +84,7 @@ enum LLVMRustAttribute { StackProtect = 32, NoUndef = 33, SanitizeMemTag = 34, + NoCfCheck = 35, }; typedef struct OpaqueRustString *RustStringRef; diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 8c5b4e2dc96f9..c4a2f6d0640e1 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -176,6 +176,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { return Attribute::NoAlias; case NoCapture: return Attribute::NoCapture; + case NoCfCheck: + return Attribute::NoCfCheck; case NoInline: return Attribute::NoInline; case NonNull: diff --git a/src/test/assembly/x86_64-naked-fn-no-cet-prolog.rs b/src/test/assembly/x86_64-naked-fn-no-cet-prolog.rs new file mode 100644 index 0000000000000..bedcded731d95 --- /dev/null +++ b/src/test/assembly/x86_64-naked-fn-no-cet-prolog.rs @@ -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 diff --git a/src/test/codegen/naked-noinline.rs b/src/test/codegen/naked-noinline.rs index 13bc139ecd05a..c0ac69f4ed778 100644 --- a/src/test/codegen/naked-noinline.rs +++ b/src/test/codegen/naked-noinline.rs @@ -28,4 +28,4 @@ pub unsafe fn g() { f(); } -// CHECK: attributes [[ATTR]] = { naked noinline{{.*}} } +// CHECK: attributes [[ATTR]] = { naked{{.*}}noinline{{.*}} } From 530b5da49b822fd65214ecbe85455114e0ce725f Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 6 Jul 2022 19:09:33 -0700 Subject: [PATCH 2/2] Also stop emitting BTI prologues for naked functions Same idea but for AArch64. --- compiler/rustc_codegen_llvm/src/attributes.rs | 2 ++ .../aarch64-naked-fn-no-bti-prolog.rs | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 src/test/assembly/aarch64-naked-fn-no-bti-prolog.rs diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 8c72f7dc06f25..c51c8391fa5a4 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -303,6 +303,8 @@ pub fn from_fn_attrs<'ll, 'tcx>( // 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) { // apply to return place instead of function (unlike all other attributes applied in this function) diff --git a/src/test/assembly/aarch64-naked-fn-no-bti-prolog.rs b/src/test/assembly/aarch64-naked-fn-no-bti-prolog.rs new file mode 100644 index 0000000000000..79b0bb2d7eead --- /dev/null +++ b/src/test/assembly/aarch64-naked-fn-no-bti-prolog.rs @@ -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)) +}