KCFI: Add KCFI arity indicator support

Adds KCFI arity indicator support to the Rust compiler (see rust-lang/rust#138311,
https://github.com/llvm/llvm-project/pull/121070, and
https://lore.kernel.org/lkml/CANiq72=3ghFxy8E=AU9p+0imFxKr5iU3sd0hVUXed5BA+KjdNQ@mail.gmail.com/).
This commit is contained in:
Ramon de C Valle 2025-03-11 19:04:35 +00:00
parent bad13a970a
commit a98546b961
14 changed files with 148 additions and 0 deletions

View File

@ -56,6 +56,8 @@ codegen_llvm_prepare_thin_lto_module_with_llvm_err = failed to prepare thin LTO
codegen_llvm_run_passes = failed to run LLVM passes
codegen_llvm_run_passes_with_llvm_err = failed to run LLVM passes: {$llvm_err}
codegen_llvm_sanitizer_kcfi_arity_requires_llvm_21_0_0 = `-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later.
codegen_llvm_sanitizer_memtag_requires_mte =
`-Zsanitizer=memtag` requires `-Ctarget-feature=+mte`

View File

@ -327,6 +327,22 @@ pub(crate) unsafe fn create_module<'ll>(
pfe.prefix().into(),
);
}
// Add "kcfi-arity" module flag if KCFI arity indicator is enabled. (See
// https://github.com/llvm/llvm-project/pull/117121.)
if sess.is_sanitizer_kcfi_arity_enabled() {
// KCFI arity indicator requires LLVM 21.0.0 or later.
if llvm_version < (21, 0, 0) {
tcx.dcx().emit_err(crate::errors::SanitizerKcfiArityRequiresLLVM2100);
}
llvm::add_module_flag_u32(
llmod,
llvm::ModuleFlagMergeBehavior::Override,
"kcfi-arity",
1,
);
}
}
// Control Flow Guard is currently only supported by MSVC and LLVM on Windows.

View File

@ -217,3 +217,7 @@ pub(crate) struct MismatchedDataLayout<'a> {
pub(crate) struct FixedX18InvalidArch<'a> {
pub arch: &'a str,
}
#[derive(Diagnostic)]
#[diag(codegen_llvm_sanitizer_kcfi_arity_requires_llvm_21_0_0)]
pub(crate) struct SanitizerKcfiArityRequiresLLVM2100;

View File

@ -853,6 +853,7 @@ fn test_unstable_options_tracking_hash() {
tracked!(sanitizer_cfi_generalize_pointers, Some(true));
tracked!(sanitizer_cfi_normalize_integers, Some(true));
tracked!(sanitizer_dataflow_abilist, vec![String::from("/rustc/abc")]);
tracked!(sanitizer_kcfi_arity, Some(true));
tracked!(sanitizer_memory_track_origins, 2);
tracked!(sanitizer_recover, SanitizerSet::ADDRESS);
tracked!(saturating_float_casts, Some(true));

View File

@ -94,6 +94,8 @@ session_sanitizer_cfi_requires_lto = `-Zsanitizer=cfi` requires `-Clto` or `-Cli
session_sanitizer_cfi_requires_single_codegen_unit = `-Zsanitizer=cfi` with `-Clto` requires `-Ccodegen-units=1`
session_sanitizer_kcfi_arity_requires_kcfi = `-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi`
session_sanitizer_kcfi_requires_panic_abort = `-Z sanitizer=kcfi` requires `-C panic=abort`
session_sanitizer_not_supported = {$us} sanitizer is not supported for this target

View File

@ -147,6 +147,10 @@ pub(crate) struct SanitizerCfiGeneralizePointersRequiresCfi;
#[diag(session_sanitizer_cfi_normalize_integers_requires_cfi)]
pub(crate) struct SanitizerCfiNormalizeIntegersRequiresCfi;
#[derive(Diagnostic)]
#[diag(session_sanitizer_kcfi_arity_requires_kcfi)]
pub(crate) struct SanitizerKcfiArityRequiresKcfi;
#[derive(Diagnostic)]
#[diag(session_sanitizer_kcfi_requires_panic_abort)]
pub(crate) struct SanitizerKcfiRequiresPanicAbort;

View File

@ -2443,6 +2443,8 @@ written to standard error output)"),
"enable normalizing integer types (default: no)"),
sanitizer_dataflow_abilist: Vec<String> = (Vec::new(), parse_comma_list, [TRACKED],
"additional ABI list files that control how shadow parameters are passed (comma separated)"),
sanitizer_kcfi_arity: Option<bool> = (None, parse_opt_bool, [TRACKED],
"enable KCFI arity indicator (default: no)"),
sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED],
"enable origins tracking in MemorySanitizer"),
sanitizer_recover: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],

View File

@ -381,6 +381,10 @@ impl Session {
self.opts.unstable_opts.sanitizer_cfi_normalize_integers == Some(true)
}
pub fn is_sanitizer_kcfi_arity_enabled(&self) -> bool {
self.opts.unstable_opts.sanitizer_kcfi_arity == Some(true)
}
pub fn is_sanitizer_kcfi_enabled(&self) -> bool {
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::KCFI)
}
@ -1211,6 +1215,11 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
}
}
// KCFI arity indicator requires KCFI.
if sess.is_sanitizer_kcfi_arity_enabled() && !sess.is_sanitizer_kcfi_enabled() {
sess.dcx().emit_err(errors::SanitizerKcfiArityRequiresKcfi);
}
// LLVM CFI pointer generalization requires CFI or KCFI.
if sess.is_sanitizer_cfi_generalize_pointers_enabled() {
if !(sess.is_sanitizer_cfi_enabled() || sess.is_sanitizer_kcfi_enabled()) {

View File

@ -0,0 +1,61 @@
// Verifies that KCFI arity indicator is emitted.
//
//@ add-core-stubs
//@ revisions: x86_64
//@ assembly-output: emit-asm
//@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu -Cllvm-args=-x86-asm-syntax=intel -Ctarget-feature=-crt-static -Cpanic=abort -Zsanitizer=kcfi -Zsanitizer-kcfi-arity -Copt-level=0
//@ [x86_64] needs-llvm-components: x86
//@ min-llvm-version: 21.0.0
#![crate_type = "lib"]
pub fn add_one(x: i32) -> i32 {
// CHECK-LABEL: __cfi__{{.*}}7add_one{{.*}}:
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: mov ecx, 2628068948
x + 1
}
pub fn add_two(x: i32, _y: i32) -> i32 {
// CHECK-LABEL: __cfi__{{.*}}7add_two{{.*}}:
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: mov edx, 2505940310
x + 2
}
pub fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
// CHECK-LABEL: __cfi__{{.*}}8do_twice{{.*}}:
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: nop
// CHECK-NEXT: mov edx, 653723426
f(arg) + f(arg)
}

View File

@ -0,0 +1,19 @@
// Verifies that "kcfi-arity" module flag is added.
//
//@ add-core-stubs
//@ revisions: x86_64
//@ [x86_64] compile-flags: --target x86_64-unknown-none
//@ [x86_64] needs-llvm-components: x86
//@ compile-flags: -Ctarget-feature=-crt-static -Cpanic=abort -Zsanitizer=kcfi -Zsanitizer-kcfi-arity
//@ min-llvm-version: 21.0.0
#![feature(no_core, lang_items)]
#![crate_type = "lib"]
#![no_core]
extern crate minicore;
use minicore::*;
pub fn foo() {}
// CHECK: !{{[0-9]+}} = !{i32 4, !"kcfi-arity", i32 1}

View File

@ -0,0 +1,9 @@
// Verifies that `-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi`.
//
//@ needs-sanitizer-kcfi
//@ compile-flags: -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer-kcfi-arity
//~? ERROR `-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi`
#![feature(no_core)]
#![no_core]
#![no_main]

View File

@ -0,0 +1,4 @@
error: `-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi`
error: aborting due to 1 previous error

View File

@ -0,0 +1,11 @@
// Verifies that `-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later.
//
//@ needs-sanitizer-kcfi
//@ compile-flags: -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Cpanic=abort -Zsanitizer=kcfi -Zsanitizer-kcfi-arity
//@ build-fail
//@ max-llvm-major-version: 20
//~? ERROR `-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later.
#![feature(no_core)]
#![no_core]
#![no_main]

View File

@ -0,0 +1,4 @@
error: `-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later.
error: aborting due to 1 previous error