From 88d314ad4cdebe248170d2fd7fe28580c02acecb Mon Sep 17 00:00:00 2001 From: sjwang05 <63834813+sjwang05@users.noreply.github.com> Date: Sat, 6 Jun 2026 23:01:26 -0700 Subject: [PATCH 01/19] typeck enum discrs before attempting to compute layout --- compiler/rustc_ty_utils/src/layout.rs | 12 +++++++ tests/crashes/138660.rs | 7 ---- ...on-int-discriminant-promoted-ice-138660.rs | 13 ++++++++ ...nt-discriminant-promoted-ice-138660.stderr | 33 +++++++++++++++++++ .../size-of-discriminant-no-cycle.rs | 8 +++++ 5 files changed, 66 insertions(+), 7 deletions(-) delete mode 100644 tests/crashes/138660.rs create mode 100644 tests/ui/enum-discriminant/non-int-discriminant-promoted-ice-138660.rs create mode 100644 tests/ui/enum-discriminant/non-int-discriminant-promoted-ice-138660.stderr create mode 100644 tests/ui/enum-discriminant/size-of-discriminant-no-cycle.rs diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index ee6afdf330a77..81d3185fea01a 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -707,6 +707,18 @@ fn layout_of_uncached<'tcx>( let discr_range_of_repr = |min, max| abi::Integer::discr_range_of_repr(tcx, ty, &def.repr(), min, max); + // We shouldn't be computing the layout of discrs that fail typeck + if def.is_enum() { + for v in def.variants() { + if let ty::VariantDiscr::Explicit(def_id) = v.discr + && let Some(local_did) = def_id.as_local() + && let Some(guar) = tcx.typeck(local_did).tainted_by_errors + { + return Err(error(cx, LayoutError::ReferencesError(guar))); + } + } + } + let discriminants_iter = || { def.is_enum() .then(|| def.discriminants(tcx).map(|(v, d)| (v, d.val as i128))) diff --git a/tests/crashes/138660.rs b/tests/crashes/138660.rs deleted file mode 100644 index 90eb8026f072f..0000000000000 --- a/tests/crashes/138660.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ known-bug: #138660 -enum A { - V1(isize) = 1..=10, - V0 = 1..=10, -} -const B: &'static [A] = &[A::V0, A::V1(111)]; -fn main() {} diff --git a/tests/ui/enum-discriminant/non-int-discriminant-promoted-ice-138660.rs b/tests/ui/enum-discriminant/non-int-discriminant-promoted-ice-138660.rs new file mode 100644 index 0000000000000..c1e968494540c --- /dev/null +++ b/tests/ui/enum-discriminant/non-int-discriminant-promoted-ice-138660.rs @@ -0,0 +1,13 @@ +// Previously, enums with non-int discrs ICEd during CTFE of promoteds. + +enum A { +//~^ ERROR `#[repr(inttype)]` must be specified + V1(isize) = 1..=10, + //~^ ERROR mismatched types + V0 = 1..=10, + //~^ ERROR mismatched types +} + +const B: &'static [A] = &[A::V0, A::V1(111)]; + +fn main() {} diff --git a/tests/ui/enum-discriminant/non-int-discriminant-promoted-ice-138660.stderr b/tests/ui/enum-discriminant/non-int-discriminant-promoted-ice-138660.stderr new file mode 100644 index 0000000000000..19491ce733d09 --- /dev/null +++ b/tests/ui/enum-discriminant/non-int-discriminant-promoted-ice-138660.stderr @@ -0,0 +1,33 @@ +error[E0308]: mismatched types + --> $DIR/non-int-discriminant-promoted-ice-138660.rs:5:17 + | +LL | V1(isize) = 1..=10, + | ^^^^^^ expected `isize`, found `RangeInclusive<{integer}>` + | + = note: expected type `isize` + found struct `std::ops::RangeInclusive<{integer}>` + = note: enum variant discriminant can only be of a primitive type compatible with the enum's `repr` + +error[E0308]: mismatched types + --> $DIR/non-int-discriminant-promoted-ice-138660.rs:7:10 + | +LL | V0 = 1..=10, + | ^^^^^^ expected `isize`, found `RangeInclusive<{integer}>` + | + = note: expected type `isize` + found struct `std::ops::RangeInclusive<{integer}>` + = note: enum variant discriminant can only be of a primitive type compatible with the enum's `repr` + +error[E0732]: `#[repr(inttype)]` must be specified for enums with explicit discriminants and non-unit variants + --> $DIR/non-int-discriminant-promoted-ice-138660.rs:3:1 + | +LL | enum A { + | ^^^^^^ +LL | +LL | V1(isize) = 1..=10, + | ------ explicit discriminant on non-unit variant specified here + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0732. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/enum-discriminant/size-of-discriminant-no-cycle.rs b/tests/ui/enum-discriminant/size-of-discriminant-no-cycle.rs new file mode 100644 index 0000000000000..09313735fd4c5 --- /dev/null +++ b/tests/ui/enum-discriminant/size-of-discriminant-no-cycle.rs @@ -0,0 +1,8 @@ +//@ check-pass +// Ensure that `size_of::()` as a discriminant does not cause a cycle error. + +enum Thing { + Variant = size_of::() as isize, +} + +fn main() {} From 1bd00d4f7277abbcdaa62382aff4e314869d8b28 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Tue, 1 Sep 2026 14:36:21 +0000 Subject: [PATCH 02/19] Add support for -Zsanitizer-cfi-minimal-runtime For production use, we should only link in the ubsan_minimal runtime, instead of the complete ubsan runtime. This adds support for both cfi-recover and cfi-diag to use the minimal runtime when `-Zsanitizer-cfi-minimal-runtime` is specified. This also includes tests, to ensure the flag can only be used if either cfi-recover or cfi-diag is enabled, it doesn't disrupt the original behavior, and links in the correct runtime when specified. Co-Authored-By: Bastian Kersting --- compiler/rustc_codegen_llvm/src/builder.rs | 33 +++- compiler/rustc_codegen_ssa/src/back/link.rs | 6 +- compiler/rustc_session/src/diagnostics.rs | 6 + compiler/rustc_session/src/options.rs | 2 + compiler/rustc_session/src/session.rs | 11 ++ src/bootstrap/src/core/build_steps/llvm.rs | 14 +- .../cfi/emit-type-checks-diag-mode.rs | 5 +- .../cfi/emit-type-checks-recover-mode.rs | 5 +- .../run-make/sanitizer-cfi-runtime/program.rs | 17 ++ tests/run-make/sanitizer-cfi-runtime/rmake.rs | 171 ++++++++++++++++++ .../cfi/fn-ptr-type-mismatch-recover.rs | 38 ++++ .../ui/sanitizer/cfi/fn-ptr-type-mismatch.rs | 9 +- ...al-runtime-requires-cfi-recover-or-diag.rs | 11 ++ ...untime-requires-cfi-recover-or-diag.stderr | 4 + 14 files changed, 319 insertions(+), 13 deletions(-) create mode 100644 tests/run-make/sanitizer-cfi-runtime/program.rs create mode 100644 tests/run-make/sanitizer-cfi-runtime/rmake.rs create mode 100644 tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs create mode 100644 tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs create mode 100644 tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.stderr diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index abc71f450a515..438accb85f377 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -2065,17 +2065,31 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { let is_diag = self.tcx.sess.opts.unstable_opts.sanitizer_cfi_diag.unwrap_or(false); let is_recover = self.tcx.sess.opts.unstable_opts.sanitizer_cfi_recover.unwrap_or(false); + let is_minimal = + self.tcx.sess.opts.unstable_opts.sanitizer_cfi_minimal_runtime.unwrap_or(false); if is_diag || is_recover { - let fty = self.cx.type_func( - &[self.cx.type_ptr(), self.cx.type_isize(), self.cx.type_isize()], - self.cx.type_void(), - ); + let fty = if is_minimal { + self.cx.type_func(&[], self.cx.type_void()) + } else { + self.cx.type_func( + &[self.cx.type_ptr(), self.cx.type_isize(), self.cx.type_isize()], + self.cx.type_void(), + ) + }; let ubsan_handler = self.declare_cfn( if is_recover { - "__ubsan_handle_cfi_check_fail" + if is_minimal { + "__ubsan_handle_cfi_check_fail_minimal" + } else { + "__ubsan_handle_cfi_check_fail" + } } else { - "__ubsan_handle_cfi_check_fail_abort" + if is_minimal { + "__ubsan_handle_cfi_check_fail_minimal_abort" + } else { + "__ubsan_handle_cfi_check_fail_abort" + } }, llvm::UnnamedAddr::Global, fty, @@ -2101,13 +2115,18 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { self.generate_ubsan_cfi_diag_data(self.span, expected_ty, check_kind); let function_address = self.ptrtoint(llfn, self.cx.type_isize()); + let arguments: &[_] = if is_minimal { + &[] + } else { + &[diag_data, function_address, self.const_usize(0)] + }; self.call( fty, None, None, ubsan_handler, ReturnSlot::Direct, - &[diag_data, function_address, self.const_usize(0)], + arguments, None, None, ); diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index c283e464821e5..7c8bb4f59dfba 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1764,7 +1764,11 @@ fn add_sanitizer_libraries( && (sess.opts.unstable_opts.sanitizer_cfi_diag.unwrap_or(false) || sess.opts.unstable_opts.sanitizer_cfi_recover.unwrap_or(false)) { - link_sanitizer_runtime(sess, flavor, linker, "ubsan"); + if sess.opts.unstable_opts.sanitizer_cfi_minimal_runtime.unwrap_or(false) { + link_sanitizer_runtime(sess, flavor, linker, "ubsan_minimal"); + } else { + link_sanitizer_runtime(sess, flavor, linker, "ubsan"); + } } } diff --git a/compiler/rustc_session/src/diagnostics.rs b/compiler/rustc_session/src/diagnostics.rs index a2bf9a0d2dfc3..d27b616d7e359 100644 --- a/compiler/rustc_session/src/diagnostics.rs +++ b/compiler/rustc_session/src/diagnostics.rs @@ -340,6 +340,12 @@ pub(crate) struct SanitizerCfiRecoverRequiresCfi; #[diag("`-Zsanitizer-cfi-diag` requires `-Zsanitizer=cfi`")] pub(crate) struct SanitizerCfiDiagRequiresCfi; +#[derive(Diagnostic)] +#[diag( + "`-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag`" +)] +pub(crate) struct SanitizerCfiMinimalRuntimeRequiresCfiRecoverOrDiag; + #[derive(Diagnostic)] #[diag("`-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi`")] pub(crate) struct SanitizerKcfiArityRequiresKcfi; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 13fc598d86bfa..f11bdadf5b67d 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2825,6 +2825,8 @@ written to standard error output)"), "enable CFI diagnostics (default: no)"), sanitizer_cfi_recover: Option = (None, parse_opt_bool, [TRACKED], "enable CFI recovery (default: no)"), + sanitizer_cfi_minimal_runtime: Option = (None, parse_opt_bool, [TRACKED], + "enable minimal UBSan runtime for CFI (default: no)"), sanitizer_dataflow_abilist: Vec = (Vec::new(), parse_comma_list, [TRACKED], "additional ABI list files that control how shadow parameters are passed (comma separated)"), sanitizer_kcfi_arity: Option = (None, parse_opt_bool, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 274cfe8d7eb8a..bf310270d0a19 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -746,6 +746,10 @@ impl Session { self.opts.unstable_opts.sanitizer_cfi_diag == Some(true) } + pub fn is_sanitizer_cfi_minimal_runtime_enabled(&self) -> bool { + self.opts.unstable_opts.sanitizer_cfi_minimal_runtime == Some(true) + } + pub fn is_sanitizer_kcfi_arity_enabled(&self) -> bool { self.opts.unstable_opts.sanitizer_kcfi_arity == Some(true) } @@ -1684,6 +1688,13 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } } + // LLVM CFI minimal runtime requires CFI Recover or CFI Diag. + if sess.is_sanitizer_cfi_minimal_runtime_enabled() { + if !(sess.is_sanitizer_cfi_recover_enabled() || sess.is_sanitizer_cfi_diag_enabled()) { + sess.dcx().emit_err(diagnostics::SanitizerCfiMinimalRuntimeRequiresCfiRecoverOrDiag); + } + } + // LLVM CFI integer normalization requires CFI or KCFI. if sess.is_sanitizer_cfi_normalize_integers_enabled() { if !(sess.is_sanitizer_cfi_enabled() || sess.is_sanitizer_kcfi_enabled()) { diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 096184ee9e5b4..2062461de4c2a 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -1903,7 +1903,7 @@ fn supported_sanitizers( "aarch64-unknown-linux-gnu" => common_libs( "linux", "aarch64", - &["asan", "lsan", "msan", "tsan", "hwasan", "rtsan", "ubsan"], + &["asan", "lsan", "msan", "tsan", "hwasan", "rtsan", "ubsan", "ubsan_minimal"], ), "aarch64-unknown-linux-ohos" => { common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"]) @@ -1924,7 +1924,17 @@ fn supported_sanitizers( "x86_64-unknown-linux-gnu" => common_libs( "linux", "x86_64", - &["asan", "dfsan", "lsan", "msan", "safestack", "tsan", "rtsan", "ubsan"], + &[ + "asan", + "dfsan", + "lsan", + "msan", + "safestack", + "tsan", + "rtsan", + "ubsan", + "ubsan_minimal", + ], ), "x86_64-unknown-linux-gnuasan" => common_libs("linux", "x86_64", &["asan"]), "x86_64-unknown-linux-gnumsan" => common_libs("linux", "x86_64", &["msan"]), diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-diag-mode.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-diag-mode.rs index 07688a8e5cb0b..96cfb94289724 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-diag-mode.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-diag-mode.rs @@ -1,7 +1,9 @@ // Verifies that pointer type membership tests for indirect calls are emitted. // +//@ revisions: cfi-diag cfi-diag-minimal-runtime //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-diag=true -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer +//@ [cfi-diag-minimal-runtime] compile-flags: -Zsanitizer-cfi-minimal-runtime=true #![crate_type = "lib"] @@ -14,7 +16,8 @@ pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { // CHECK-NEXT: {{%.+}} = call i32 %f(i32{{.*}} %arg) // CHECK: type_test.fail: // CHECK-NEXT: {{%.+}} = ptrtoint ptr {{%f|%0}} to i64 - // CHECK-NEXT: call void @__ubsan_handle_cfi_check_fail_abort( + // cfi-diag-NEXT: call void @__ubsan_handle_cfi_check_fail_abort( + // cfi-diag-minimal-runtime-NEXT: call void @__ubsan_handle_cfi_check_fail_minimal_abort() // CHECK-NEXT: unreachable f(arg) } diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-recover-mode.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-recover-mode.rs index 0dbcb7a833fb7..b3a1e2cb0b02e 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-recover-mode.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-recover-mode.rs @@ -1,7 +1,9 @@ // Verifies that pointer type membership tests for indirect calls are emitted. // +//@ revisions: cfi-recover cfi-recover-minimal-runtime //@ needs-sanitizer-cfi //@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-recover=true -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer +//@ [cfi-recover-minimal-runtime] compile-flags: -Zsanitizer-cfi-minimal-runtime=true #![crate_type = "lib"] @@ -14,7 +16,8 @@ pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { // CHECK-NEXT: {{%.+}} = call i32 %f(i32{{.*}} %arg) // CHECK: type_test.fail: // CHECK-NEXT: {{%.+}} = ptrtoint ptr {{%f|%0}} to i64 - // CHECK-NEXT: call void @__ubsan_handle_cfi_check_fail( + // cfi-recover-NEXT: call void @__ubsan_handle_cfi_check_fail( + // cfi-recover-minimal-runtime-NEXT: call void @__ubsan_handle_cfi_check_fail_minimal() // CHECK-NEXT: br label %type_test.pass f(arg) } diff --git a/tests/run-make/sanitizer-cfi-runtime/program.rs b/tests/run-make/sanitizer-cfi-runtime/program.rs new file mode 100644 index 0000000000000..0a46dcedef9de --- /dev/null +++ b/tests/run-make/sanitizer-cfi-runtime/program.rs @@ -0,0 +1,17 @@ +use std::hint::black_box; +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +#[inline(never)] +fn call_with_mismatch(f: fn(i32) -> i32) { + let g: fn(i32, i32) -> i32 = unsafe { mem::transmute(f) }; + let res = g(1, 2); + assert_eq!(res, 2); +} + +fn main() { + call_with_mismatch(black_box(add_one)); +} diff --git a/tests/run-make/sanitizer-cfi-runtime/rmake.rs b/tests/run-make/sanitizer-cfi-runtime/rmake.rs new file mode 100644 index 0000000000000..adb739e43c341 --- /dev/null +++ b/tests/run-make/sanitizer-cfi-runtime/rmake.rs @@ -0,0 +1,171 @@ +//@ needs-sanitizer-support +//@ needs-sanitizer-cfi + +use run_make_support::{run, run_fail, rustc}; + +fn main() { + // 1. Check link args for default CFI (no diag/recover, no UBSan runtime) + let link_args_default = rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .print("link-args") + .input("program.rs") + .run() + .stdout_utf8(); + assert!( + !link_args_default.contains("ubsan"), + "did not expect any ubsan runtime in link args, got: {link_args_default}" + ); + + // 2. Check link args for full UBSan runtime with CFI diag mode + let link_args_full_diag = rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .arg("-Zsanitizer-cfi-diag=true") + .print("link-args") + .input("program.rs") + .run() + .stdout_utf8(); + assert!( + link_args_full_diag.contains("rt.ubsan.") || link_args_full_diag.contains("rt.ubsan\""), + "expected ubsan runtime in link args, got: {link_args_full_diag}" + ); + assert!( + !link_args_full_diag.contains("ubsan_minimal"), + "did not expect ubsan_minimal in link args, got: {link_args_full_diag}" + ); + + // 3. Check link args for full UBSan runtime with CFI recover mode + let link_args_full_recover = rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .arg("-Zsanitizer-cfi-recover=true") + .print("link-args") + .input("program.rs") + .run() + .stdout_utf8(); + assert!( + link_args_full_recover.contains("rt.ubsan.") + || link_args_full_recover.contains("rt.ubsan\""), + "expected ubsan runtime in link args, got: {link_args_full_recover}" + ); + assert!( + !link_args_full_recover.contains("ubsan_minimal"), + "did not expect ubsan_minimal in link args, got: {link_args_full_recover}" + ); + + // 4. Check link args for minimal UBSan runtime with CFI diag mode + let link_args_min_diag = rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .arg("-Zsanitizer-cfi-diag=true") + .arg("-Zsanitizer-cfi-minimal-runtime=true") + .print("link-args") + .input("program.rs") + .run() + .stdout_utf8(); + assert!( + link_args_min_diag.contains("rt.ubsan_minimal.") + || link_args_min_diag.contains("rt.ubsan_minimal\""), + "expected ubsan_minimal runtime in link args, got: {link_args_min_diag}" + ); + + // 5. Check link args for minimal UBSan runtime with CFI recover mode + let link_args_min_recover = rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .arg("-Zsanitizer-cfi-recover=true") + .arg("-Zsanitizer-cfi-minimal-runtime=true") + .print("link-args") + .input("program.rs") + .run() + .stdout_utf8(); + assert!( + link_args_min_recover.contains("rt.ubsan_minimal.") + || link_args_min_recover.contains("rt.ubsan_minimal\""), + "expected ubsan_minimal runtime in link args, got: {link_args_min_recover}" + ); + + // 6. Build and run binary with default CFI (trap mode, no runtime) + rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .output("program_default") + .input("program.rs") + .run(); + run_fail("program_default"); + + // 7. Build and run binary with full runtime in diag (abort) mode + rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .arg("-Zsanitizer-cfi-diag=true") + .output("program_full_diag") + .input("program.rs") + .run(); + run_fail("program_full_diag") + .assert_stderr_contains("runtime error: control flow integrity check for type"); + + // 8. Build and run binary with full runtime in recover mode + rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .arg("-Zsanitizer-cfi-recover=true") + .output("program_full_recover") + .input("program.rs") + .run(); + run("program_full_recover") + .assert_stderr_contains("runtime error: control flow integrity check for type"); + + // 9. Build and run binary with minimal runtime in diag (abort) mode + rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .arg("-Zsanitizer-cfi-diag=true") + .arg("-Zsanitizer-cfi-minimal-runtime=true") + .output("program_min_diag") + .input("program.rs") + .run(); + run_fail("program_min_diag").assert_stderr_contains("ubsan: cfi-check-fail"); + + // 10. Build and run binary with minimal runtime in recover mode + rustc() + .arg("-Clto") + .arg("-Ccodegen-units=1") + .arg("-Ctarget-feature=-crt-static") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Zsanitizer=cfi") + .arg("-Zsanitizer-cfi-recover=true") + .arg("-Zsanitizer-cfi-minimal-runtime=true") + .output("program_min_recover") + .input("program.rs") + .run(); + run("program_min_recover").assert_stderr_contains("ubsan: cfi-check-fail"); +} diff --git a/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs b/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs new file mode 100644 index 0000000000000..21953675684ee --- /dev/null +++ b/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs @@ -0,0 +1,38 @@ +// Verifies that calling a function pointer with a mismatched type with CFI +// recovery enabled reports the failure and continues execution. + +//@ revisions: cfi cfi-minimal-runtime +// FIXME(#122848) Remove only-linux once OSX CFI binaries work +//@ only-linux +//@ ignore-backends: gcc +//@ [cfi] needs-sanitizer-cfi +//@ [cfi] needs-sanitizer-support +//@ [cfi-minimal-runtime] needs-sanitizer-cfi +//@ [cfi-minimal-runtime] needs-sanitizer-support +//@ compile-flags: -C target-feature=-crt-static +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer +//@ compile-flags: -C opt-level=0 -C codegen-units=1 -C lto +//@ compile-flags: -C prefer-dynamic=off +//@ compile-flags: -Z sanitizer=cfi +//@ [cfi] compile-flags: -Z sanitizer-cfi-recover=true +//@ [cfi-minimal-runtime] compile-flags: -Z sanitizer-cfi-recover=true +//@ [cfi-minimal-runtime] compile-flags: -Z sanitizer-cfi-minimal-runtime=true +//@ run-pass + +use std::hint::black_box; +use std::mem; + +fn add_one(x: i32) -> i32 { + x + 1 +} + +#[inline(never)] +fn call_with_mismatch(f: fn(i32) -> i32) { + let g: fn(i32, i32) -> i32 = unsafe { mem::transmute(f) }; + let res = g(1, 2); + assert_eq!(res, 2); +} + +fn main() { + call_with_mismatch(black_box(add_one)); +} diff --git a/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs b/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs index 6fe7eec7c6920..d64f6c1f43965 100644 --- a/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs +++ b/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs @@ -1,12 +1,14 @@ // Verifies that calling a function pointer with a mismatched type triggers a // CFI violation and causes the process to trap. -//@ revisions: cfi kcfi +//@ revisions: cfi cfi-minimal-runtime kcfi // FIXME(#122848) Remove only-linux once OSX CFI binaries work //@ only-linux //@ ignore-backends: gcc //@ [cfi] needs-sanitizer-cfi //@ [cfi] needs-sanitizer-support +//@ [cfi-minimal-runtime] needs-sanitizer-cfi +//@ [cfi-minimal-runtime] needs-sanitizer-support //@ [kcfi] needs-sanitizer-kcfi //@ compile-flags: -C target-feature=-crt-static //@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer @@ -14,6 +16,11 @@ //@ [cfi] compile-flags: -C prefer-dynamic=off //@ [cfi] compile-flags: -Z sanitizer=cfi //@ [cfi] compile-flags: -Z sanitizer-cfi-diag=true +//@ [cfi-minimal-runtime] compile-flags: -C opt-level=0 -C codegen-units=1 -C lto +//@ [cfi-minimal-runtime] compile-flags: -C prefer-dynamic=off +//@ [cfi-minimal-runtime] compile-flags: -Z sanitizer=cfi +//@ [cfi-minimal-runtime] compile-flags: -Z sanitizer-cfi-diag=true +//@ [cfi-minimal-runtime] compile-flags: -Z sanitizer-cfi-minimal-runtime=true //@ [kcfi] compile-flags: -Z sanitizer=kcfi //@ [kcfi] compile-flags: -C panic=abort -C prefer-dynamic=off //@ run-fail-or-crash diff --git a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs new file mode 100644 index 0000000000000..f241598131d65 --- /dev/null +++ b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs @@ -0,0 +1,11 @@ +// Verifies that `-Zsanitizer-cfi-minimal-runtime` requires +// `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag`. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer-cfi-minimal-runtime + +#![feature(no_core)] +#![no_core] +#![no_main] + +//~? ERROR `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag` diff --git a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.stderr b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.stderr new file mode 100644 index 0000000000000..f3d483bef7eb7 --- /dev/null +++ b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.stderr @@ -0,0 +1,4 @@ +error: `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag` + +error: aborting due to 1 previous error + From 4d68be3d806fd0894977dea0a5758800cab25ac6 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Thu, 10 Sep 2026 14:11:10 +0000 Subject: [PATCH 03/19] minor cleanup for minimal runtime test --- .../cfi/minimal-runtime-requires-cfi-recover-or-diag.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs index f241598131d65..cbc0bac40c2b3 100644 --- a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs +++ b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs @@ -2,7 +2,7 @@ // `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag`. // //@ needs-sanitizer-cfi -//@ compile-flags: -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer-cfi-minimal-runtime +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer-cfi-minimal-runtime #![feature(no_core)] #![no_core] From 855d8a275b860bf5bb6d0a2499278a90a8d1b547 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Thu, 10 Sep 2026 14:42:35 +0000 Subject: [PATCH 04/19] Add target modifier for -Zsanitizer-cfi-minimal-runtime Co-Authored-By: Bastian Kersting --- compiler/rustc_session/src/options.rs | 22 ++++++++++++++++++- .../auxiliary/cfi-minimal-runtime.rs | 7 ++++++ .../sanitizer-cfi-minimal-runtime.rs | 18 +++++++++++++++ ...izer-cfi-minimal-runtime.wrong_flag.stderr | 9 ++++++++ ...cfi-minimal-runtime.wrong_sanitizer.stderr | 9 ++++++++ 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 tests/ui/target_modifiers/auxiliary/cfi-minimal-runtime.rs create mode 100644 tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.rs create mode 100644 tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.wrong_flag.stderr create mode 100644 tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.wrong_sanitizer.stderr diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index f11bdadf5b67d..63bc2da30f063 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -136,6 +136,21 @@ mod target_modifier_consistency_check { } true } + pub(super) fn sanitizer_cfi_minimal_runtime( + sess: &Session, + l: &TargetModifier, + r: Option<&TargetModifier>, + ) -> bool { + // For CFI, the helper flag -Zsanitizer-cfi-minimal-runtime should also be a target modifier + if sess.sanitizers().contains(SanitizerSet::CFI) { + if let Some(r) = r { + return l.extend().tech_value == r.extend().tech_value; + } else { + return false; + } + } + true + } pub(super) fn target_cpu( sess: &Session, l: &TargetModifier, @@ -178,6 +193,11 @@ impl TargetModifier { sess, self, other, ); } + UnstableOptionsTargetModifiers::SanitizerCfiMinimalRuntime => { + return target_modifier_consistency_check::sanitizer_cfi_minimal_runtime( + sess, self, other, + ); + } _ => {} }, OptionsTargetModifiers::CodegenOptions(codegen) => match codegen { @@ -2825,7 +2845,7 @@ written to standard error output)"), "enable CFI diagnostics (default: no)"), sanitizer_cfi_recover: Option = (None, parse_opt_bool, [TRACKED], "enable CFI recovery (default: no)"), - sanitizer_cfi_minimal_runtime: Option = (None, parse_opt_bool, [TRACKED], + sanitizer_cfi_minimal_runtime: Option = (None, parse_opt_bool, [TRACKED] { TARGET_MODIFIER: SanitizerCfiMinimalRuntime }, "enable minimal UBSan runtime for CFI (default: no)"), sanitizer_dataflow_abilist: Vec = (Vec::new(), parse_comma_list, [TRACKED], "additional ABI list files that control how shadow parameters are passed (comma separated)"), diff --git a/tests/ui/target_modifiers/auxiliary/cfi-minimal-runtime.rs b/tests/ui/target_modifiers/auxiliary/cfi-minimal-runtime.rs new file mode 100644 index 0000000000000..aacea1ee65dd2 --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/cfi-minimal-runtime.rs @@ -0,0 +1,7 @@ +//@ no-prefer-dynamic +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Zsanitizer=cfi -Zsanitizer-cfi-recover -Zsanitizer-cfi-minimal-runtime + +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] diff --git a/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.rs b/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.rs new file mode 100644 index 0000000000000..45c6ad7b521a3 --- /dev/null +++ b/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.rs @@ -0,0 +1,18 @@ +// For CFI, the helper flag -Zsanitizer-cfi-minimal-runtime should also be a target modifier. + +//@ needs-sanitizer-cfi +//@ aux-build:cfi-minimal-runtime.rs + +//@ revisions: ok wrong_flag wrong_sanitizer +//@[ok] compile-flags: -Clto -Zsanitizer=cfi -Zsanitizer-cfi-recover -Zsanitizer-cfi-minimal-runtime +//@[wrong_flag] compile-flags: -Clto -Zsanitizer=cfi -Zsanitizer-cfi-recover +//@[ok] check-pass + +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] + +extern crate cfi_minimal_runtime; + +//[wrong_flag]~? ERROR mixing `-Zsanitizer-cfi-minimal-runtime` will cause an ABI mismatch in crate `sanitizer_cfi_minimal_runtime` +//[wrong_sanitizer]~? ERROR mixing `-Zsanitizer` will cause an ABI mismatch in crate `sanitizer_cfi_minimal_runtime` diff --git a/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.wrong_flag.stderr b/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.wrong_flag.stderr new file mode 100644 index 0000000000000..6dfc1a3bb7d16 --- /dev/null +++ b/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.wrong_flag.stderr @@ -0,0 +1,9 @@ +error: mixing `-Zsanitizer-cfi-minimal-runtime` will cause an ABI mismatch in crate `sanitizer_cfi_minimal_runtime` + | + = help: the `-Zsanitizer-cfi-minimal-runtime` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: `-Zsanitizer-cfi-minimal-runtime` is unset in this crate which is incompatible with `-Zsanitizer-cfi-minimal-runtime` being set in dependency `cfi_minimal_runtime` + = help: set `-Zsanitizer-cfi-minimal-runtime` in this crate or unset `-Zsanitizer-cfi-minimal-runtime` in `cfi_minimal_runtime` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=sanitizer-cfi-minimal-runtime` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.wrong_sanitizer.stderr b/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.wrong_sanitizer.stderr new file mode 100644 index 0000000000000..32ba848b3b2ad --- /dev/null +++ b/tests/ui/target_modifiers/sanitizer-cfi-minimal-runtime.wrong_sanitizer.stderr @@ -0,0 +1,9 @@ +error: mixing `-Zsanitizer` will cause an ABI mismatch in crate `sanitizer_cfi_minimal_runtime` + | + = help: the `-Zsanitizer` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: `-Zsanitizer` is unset in this crate which is incompatible with `-Zsanitizer=cfi` in dependency `cfi_minimal_runtime` + = help: set `-Zsanitizer=cfi` in this crate or unset `-Zsanitizer` in `cfi_minimal_runtime` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=sanitizer` to silence this error + +error: aborting due to 1 previous error + From a42ab2f36cf031d3f95181edc8a613d38c7e00b5 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Fri, 11 Sep 2026 08:47:12 +0000 Subject: [PATCH 05/19] Make -Zsanitizer-cfi-minimal-runtime also dependent on -Zsanitizer=cfi --- compiler/rustc_session/src/diagnostics.rs | 6 ++++++ compiler/rustc_session/src/session.rs | 3 +++ .../minimal-runtime-requires-cfi-recover-or-diag.rs | 2 +- .../ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs | 11 +++++++++++ .../sanitizer/cfi/minimal-runtime-requires-cfi.stderr | 6 ++++++ 5 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs create mode 100644 tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr diff --git a/compiler/rustc_session/src/diagnostics.rs b/compiler/rustc_session/src/diagnostics.rs index d27b616d7e359..b0bbccd7fe91f 100644 --- a/compiler/rustc_session/src/diagnostics.rs +++ b/compiler/rustc_session/src/diagnostics.rs @@ -340,6 +340,12 @@ pub(crate) struct SanitizerCfiRecoverRequiresCfi; #[diag("`-Zsanitizer-cfi-diag` requires `-Zsanitizer=cfi`")] pub(crate) struct SanitizerCfiDiagRequiresCfi; +#[derive(Diagnostic)] +#[diag( + "`-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer=cfi`" +)] +pub(crate) struct SanitizerCfiMinimalRuntimeRequiresCfi; + #[derive(Diagnostic)] #[diag( "`-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag`" diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index bf310270d0a19..a209219758026 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1690,6 +1690,9 @@ fn validate_commandline_args_with_session_available(sess: &Session) { // LLVM CFI minimal runtime requires CFI Recover or CFI Diag. if sess.is_sanitizer_cfi_minimal_runtime_enabled() { + if !sess.is_sanitizer_cfi_enabled() { + sess.dcx().emit_err(diagnostics::SanitizerCfiMinimalRuntimeRequiresCfi); + } if !(sess.is_sanitizer_cfi_recover_enabled() || sess.is_sanitizer_cfi_diag_enabled()) { sess.dcx().emit_err(diagnostics::SanitizerCfiMinimalRuntimeRequiresCfiRecoverOrDiag); } diff --git a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs index cbc0bac40c2b3..16f39f09ec7a0 100644 --- a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs +++ b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi-recover-or-diag.rs @@ -2,7 +2,7 @@ // `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag`. // //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer-cfi-minimal-runtime +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-minimal-runtime #![feature(no_core)] #![no_core] diff --git a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs new file mode 100644 index 0000000000000..13b296b4329ee --- /dev/null +++ b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs @@ -0,0 +1,11 @@ +// Verifies that `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer=cfi`. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer-cfi-minimal-runtime + +#![feature(no_core)] +#![no_core] +#![no_main] + +//~? ERROR `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer=cfi` +//~? ERROR `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag` diff --git a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr new file mode 100644 index 0000000000000..251d0d0817631 --- /dev/null +++ b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr @@ -0,0 +1,6 @@ +error: `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer=cfi` + +error: `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag` + +error: aborting due to 2 previous errors + From bba4a3f986f4e2d4b0bea8711c436bf3ffe4a92a Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Fri, 11 Sep 2026 09:00:38 +0000 Subject: [PATCH 06/19] Fix tests with new cfi-minimal-runtime target modifier --- tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs | 2 +- tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs b/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs index 21953675684ee..069ac811f481d 100644 --- a/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs +++ b/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch-recover.rs @@ -10,7 +10,7 @@ //@ [cfi-minimal-runtime] needs-sanitizer-cfi //@ [cfi-minimal-runtime] needs-sanitizer-support //@ compile-flags: -C target-feature=-crt-static -//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-minimal-runtime //@ compile-flags: -C opt-level=0 -C codegen-units=1 -C lto //@ compile-flags: -C prefer-dynamic=off //@ compile-flags: -Z sanitizer=cfi diff --git a/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs b/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs index d64f6c1f43965..1e32a1042c848 100644 --- a/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs +++ b/tests/ui/sanitizer/cfi/fn-ptr-type-mismatch.rs @@ -11,7 +11,7 @@ //@ [cfi-minimal-runtime] needs-sanitizer-support //@ [kcfi] needs-sanitizer-kcfi //@ compile-flags: -C target-feature=-crt-static -//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer +//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-minimal-runtime //@ [cfi] compile-flags: -C opt-level=0 -C codegen-units=1 -C lto //@ [cfi] compile-flags: -C prefer-dynamic=off //@ [cfi] compile-flags: -Z sanitizer=cfi From 775c187f3add8c381e818575687df32c01abf92a Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Fri, 11 Sep 2026 09:37:04 +0000 Subject: [PATCH 07/19] Fix linter issue in diagnostics.rs --- compiler/rustc_session/src/diagnostics.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/rustc_session/src/diagnostics.rs b/compiler/rustc_session/src/diagnostics.rs index b0bbccd7fe91f..0ec847767336e 100644 --- a/compiler/rustc_session/src/diagnostics.rs +++ b/compiler/rustc_session/src/diagnostics.rs @@ -341,9 +341,7 @@ pub(crate) struct SanitizerCfiRecoverRequiresCfi; pub(crate) struct SanitizerCfiDiagRequiresCfi; #[derive(Diagnostic)] -#[diag( - "`-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer=cfi`" -)] +#[diag("`-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer=cfi`")] pub(crate) struct SanitizerCfiMinimalRuntimeRequiresCfi; #[derive(Diagnostic)] From 3058339f0e35413263d71e595a8bb1421646edd3 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Fri, 11 Sep 2026 10:53:13 +0000 Subject: [PATCH 08/19] Fix broken test introduced by cfi-minimal-runtime target modifier --- .../sanitizer/cfi/emit-type-checks-diag-mode.rs | 2 +- .../sanitizer/cfi/emit-type-checks-recover-mode.rs | 2 +- tests/run-make/sanitizer-cfi-runtime/rmake.rs | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-diag-mode.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-diag-mode.rs index 96cfb94289724..4d58872db7c45 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-diag-mode.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-diag-mode.rs @@ -2,7 +2,7 @@ // //@ revisions: cfi-diag cfi-diag-minimal-runtime //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-diag=true -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-diag=true -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-minimal-runtime //@ [cfi-diag-minimal-runtime] compile-flags: -Zsanitizer-cfi-minimal-runtime=true #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-recover-mode.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-recover-mode.rs index b3a1e2cb0b02e..3ea97e44c21e7 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-recover-mode.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-checks-recover-mode.rs @@ -2,7 +2,7 @@ // //@ revisions: cfi-recover cfi-recover-minimal-runtime //@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-recover=true -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Zsanitizer-cfi-recover=true -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-minimal-runtime //@ [cfi-recover-minimal-runtime] compile-flags: -Zsanitizer-cfi-minimal-runtime=true #![crate_type = "lib"] diff --git a/tests/run-make/sanitizer-cfi-runtime/rmake.rs b/tests/run-make/sanitizer-cfi-runtime/rmake.rs index adb739e43c341..8e92f5927d952 100644 --- a/tests/run-make/sanitizer-cfi-runtime/rmake.rs +++ b/tests/run-make/sanitizer-cfi-runtime/rmake.rs @@ -68,7 +68,7 @@ fn main() { .arg("-Clto") .arg("-Ccodegen-units=1") .arg("-Ctarget-feature=-crt-static") - .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-minimal-runtime") .arg("-Zsanitizer=cfi") .arg("-Zsanitizer-cfi-diag=true") .arg("-Zsanitizer-cfi-minimal-runtime=true") @@ -87,7 +87,7 @@ fn main() { .arg("-Clto") .arg("-Ccodegen-units=1") .arg("-Ctarget-feature=-crt-static") - .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-minimal-runtime") .arg("-Zsanitizer=cfi") .arg("-Zsanitizer-cfi-recover=true") .arg("-Zsanitizer-cfi-minimal-runtime=true") @@ -146,7 +146,7 @@ fn main() { .arg("-Clto") .arg("-Ccodegen-units=1") .arg("-Ctarget-feature=-crt-static") - .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-minimal-runtime") .arg("-Zsanitizer=cfi") .arg("-Zsanitizer-cfi-diag=true") .arg("-Zsanitizer-cfi-minimal-runtime=true") @@ -160,7 +160,7 @@ fn main() { .arg("-Clto") .arg("-Ccodegen-units=1") .arg("-Ctarget-feature=-crt-static") - .arg("-Cunsafe-allow-abi-mismatch=sanitizer") + .arg("-Cunsafe-allow-abi-mismatch=sanitizer,sanitizer-cfi-minimal-runtime") .arg("-Zsanitizer=cfi") .arg("-Zsanitizer-cfi-recover=true") .arg("-Zsanitizer-cfi-minimal-runtime=true") From 7aacc8a2dab9300860afc1d912193e490ceeb5a1 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Fri, 11 Sep 2026 12:05:31 +0000 Subject: [PATCH 09/19] Skip gcc for CFI runtime tests --- tests/run-make/sanitizer-cfi-runtime/rmake.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run-make/sanitizer-cfi-runtime/rmake.rs b/tests/run-make/sanitizer-cfi-runtime/rmake.rs index 8e92f5927d952..3cad996a14054 100644 --- a/tests/run-make/sanitizer-cfi-runtime/rmake.rs +++ b/tests/run-make/sanitizer-cfi-runtime/rmake.rs @@ -1,5 +1,6 @@ //@ needs-sanitizer-support //@ needs-sanitizer-cfi +//@ ignore-backends: gcc use run_make_support::{run, run_fail, rustc}; From dddede5a8b0d0973da41559d0e818f2f48337201 Mon Sep 17 00:00:00 2001 From: zakrad <49591476+zakrad@users.noreply.github.com> Date: Mon, 21 Sep 2026 14:30:40 +0330 Subject: [PATCH 10/19] Add regression test for hang on mutually recursive trait impls --- ...recursive-impls-hang-143018.current.stderr | 9 ++++++ ...ly-recursive-impls-hang-143018.next.stderr | 15 ++++++++++ .../mutually-recursive-impls-hang-143018.rs | 29 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 tests/ui/traits/mutually-recursive-impls-hang-143018.current.stderr create mode 100644 tests/ui/traits/mutually-recursive-impls-hang-143018.next.stderr create mode 100644 tests/ui/traits/mutually-recursive-impls-hang-143018.rs diff --git a/tests/ui/traits/mutually-recursive-impls-hang-143018.current.stderr b/tests/ui/traits/mutually-recursive-impls-hang-143018.current.stderr new file mode 100644 index 0000000000000..c6410d7eac39c --- /dev/null +++ b/tests/ui/traits/mutually-recursive-impls-hang-143018.current.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/mutually-recursive-impls-hang-143018.rs:26:5 + | +LL | impls_trait::(); + | ^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `V` declared on the function `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/traits/mutually-recursive-impls-hang-143018.next.stderr b/tests/ui/traits/mutually-recursive-impls-hang-143018.next.stderr new file mode 100644 index 0000000000000..680182b7350c8 --- /dev/null +++ b/tests/ui/traits/mutually-recursive-impls-hang-143018.next.stderr @@ -0,0 +1,15 @@ +error[E0275]: overflow evaluating the requirement `A: Trait<_>` + --> $DIR/mutually-recursive-impls-hang-143018.rs:26:19 + | +LL | impls_trait::(); + | ^ + | +note: required by a bound in `impls_trait` + --> $DIR/mutually-recursive-impls-hang-143018.rs:24:19 + | +LL | fn impls_trait, V>() {} + | ^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/mutually-recursive-impls-hang-143018.rs b/tests/ui/traits/mutually-recursive-impls-hang-143018.rs new file mode 100644 index 0000000000000..8db92502a8621 --- /dev/null +++ b/tests/ui/traits/mutually-recursive-impls-hang-143018.rs @@ -0,0 +1,29 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Regression test for . + +trait Trait {} +struct A; +struct B; + +impl Trait for A +where + A: Trait, + B: Trait, +{ +} + +impl Trait for B +where + A: Trait, +{ +} + +fn impls_trait, V>() {} +fn main() { + impls_trait::(); + //[current]~^ ERROR type annotations needed + //[next]~^^ ERROR overflow evaluating the requirement +} From 8e5ad6c1e4d2590797636dd5c8e7049e002b3b39 Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Mon, 21 Sep 2026 11:14:06 +0000 Subject: [PATCH 11/19] use match to determine correct ubsan cfi handler --- compiler/rustc_codegen_llvm/src/builder.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 438accb85f377..7fa079a01a6b1 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -2078,18 +2078,11 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { ) }; let ubsan_handler = self.declare_cfn( - if is_recover { - if is_minimal { - "__ubsan_handle_cfi_check_fail_minimal" - } else { - "__ubsan_handle_cfi_check_fail" - } - } else { - if is_minimal { - "__ubsan_handle_cfi_check_fail_minimal_abort" - } else { - "__ubsan_handle_cfi_check_fail_abort" - } + match (is_minimal, is_recover) { + (true, true) => "__ubsan_handle_cfi_check_fail_minimal", + (true, false) => "__ubsan_handle_cfi_check_fail_minimal_abort", + (false, true) => "__ubsan_handle_cfi_check_fail", + (false, false) => "__ubsan_handle_cfi_check_fail_abort", }, llvm::UnnamedAddr::Global, fty, From 3285cce3dad8e74b1ecaf98675cc8b218c900758 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 19 Sep 2026 16:30:33 +0200 Subject: [PATCH 12/19] fix `va_arg` on `f128` on `x86` --- compiler/rustc_codegen_llvm/src/va_arg.rs | 31 +++++++---- tests/assembly-llvm/c-variadic/x86-linux.rs | 57 +++++++++++++++++---- 2 files changed, 68 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index b21ce3f2be8e4..18229e6d14ba5 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -1057,15 +1057,28 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( let stability = target.supports_c_variadic_definitions(); match target.arch { - Arch::X86 => emit_ptr_va_arg( - bx, - addr, - layout, - PassMode::Direct, - SlotSize::Bytes4, - if target.is_like_windows { AllowHigherAlign::No } else { AllowHigherAlign::Yes }, - ForceRightAdjust::No, - ), + Arch::X86 => { + // A small deviation from clang to get the right behavior for f128. + // + // Note that i64 and f64 have an alignment of only 4 on this architecture. + // We need to be careful when adding future types with an alignment bigger + // than 4 (e.g. i128), clang has a bunch of custom logic for them. + let allow_higher_align = if layout.ty == bx.tcx().types.f128 { + AllowHigherAlign::Yes + } else { + AllowHigherAlign::No + }; + + emit_ptr_va_arg( + bx, + addr, + layout, + PassMode::Direct, + SlotSize::Bytes4, + allow_higher_align, + ForceRightAdjust::No, + ) + } Arch::Arm64EC => emit_ptr_va_arg( bx, addr, diff --git a/tests/assembly-llvm/c-variadic/x86-linux.rs b/tests/assembly-llvm/c-variadic/x86-linux.rs index d2f5c1144a991..de647ca861cb6 100644 --- a/tests/assembly-llvm/c-variadic/x86-linux.rs +++ b/tests/assembly-llvm/c-variadic/x86-linux.rs @@ -9,7 +9,7 @@ //@ [I686] compile-flags: -Copt-level=3 -Cllvm-args=-x86-asm-syntax=intel //@ [I686] compile-flags: --target i686-unknown-linux-gnu //@ needs-llvm-components: x86 -#![feature(no_core, lang_items, intrinsics, rustc_attrs)] +#![feature(no_core, lang_items, intrinsics, rustc_attrs, f128)] #![no_core] #![crate_type = "lib"] @@ -64,20 +64,55 @@ unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { ap.next_arg() } +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f128(ap: &mut VaList<'_>) -> f128 { + // CHECK-LABEL: read_f128 + + // X86_64: mov ecx, dword ptr [rdi + 4] + // X86_64-NEXT: cmp rcx, 160 + // X86_64-NEXT: ja .LBB1_2 + // X86_64-NEXT: mov rax, rcx + // X86_64-NEXT: add rax, qword ptr [rdi + 16] + // X86_64-NEXT: add ecx, 16 + // X86_64-NEXT: mov dword ptr [rdi + 4], ecx + // X86_64-NEXT: movaps xmm0, xmmword ptr [rax] + // X86_64-NEXT: ret + // X86_64-NEXT: .LBB1_2: + // X86_64-NEXT: mov rax, qword ptr [rdi + 8] + // X86_64-NEXT: add rax, 15 + // X86_64-NEXT: and rax, -16 + // X86_64-NEXT: lea rcx, [rax + 16] + // X86_64-NEXT: mov qword ptr [rdi + 8], rcx + // X86_64-NEXT: movaps xmm0, xmmword ptr [rax] + // X86_64-NEXT: ret + + // I686: mov ecx, dword ptr [esp + 12] + // I686-NEXT: mov eax, dword ptr [esp + 8] + // I686-NEXT: mov edx, dword ptr [ecx] + // I686-NEXT: add edx, 15 + // I686-NEXT: and edx, -16 + // I686-NEXT: lea esi, [edx + 16] + // I686-NEXT: mov dword ptr [ecx], esi + // I686-NEXT: movaps xmm0, xmmword ptr [edx] + // I686-NEXT: movaps xmmword ptr [eax], xmm0 + // I686-NEXT: pop esi + ap.next_arg() +} + #[unsafe(no_mangle)] unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { // CHECK-LABEL: read_i32 // // X86_64: mov ecx, dword ptr [rdi] // X86_64-NEXT: cmp rcx, 40 - // X86_64-NEXT: ja .LBB1_2 + // X86_64-NEXT: ja .LBB2_2 // X86_64-NEXT: mov rax, rcx // X86_64-NEXT: add rax, qword ptr [rdi + 16] // X86_64-NEXT: add ecx, 8 // X86_64-NEXT: mov dword ptr [rdi], ecx // X86_64-NEXT: mov eax, dword ptr [rax] // X86_64-NEXT: ret - // X86_64-NEXT: .LBB1_2: + // X86_64-NEXT: .LBB2_2: // X86_64-NEXT: mov rax, qword ptr [rdi + 8] // X86_64-NEXT: lea rcx, [rax + 8] // X86_64-NEXT: mov qword ptr [rdi + 8], rcx @@ -86,14 +121,14 @@ unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { // X86_64-NEXT_GNUX32: mov ecx, dword ptr [edi] // X86_64-NEXT_GNUX32-NEXT: cmp ecx, 40 - // X86_64-NEXT_GNUX32-NEXT: ja .LBB1_2 + // X86_64-NEXT_GNUX32-NEXT: ja .LBB2_2 // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [edi + 12] // X86_64-NEXT_GNUX32-NEXT: add eax, ecx // X86_64-NEXT_GNUX32-NEXT: add ecx, 8 // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi], ecx // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [eax] // X86_64-NEXT_GNUX32-NEXT: ret - // X86_64-NEXT_GNUX32-NEXT: .LBB1_2: + // X86_64-NEXT_GNUX32-NEXT: .LBB2_2: // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [edi + 8] // X86_64-NEXT_GNUX32-NEXT: lea ecx, [rax + 8] // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi + 8], ecx @@ -115,14 +150,14 @@ unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { // X86_64: mov ecx, dword ptr [rdi] // X86_64-NEXT: cmp rcx, 40 - // X86_64-NEXT: ja .LBB2_2 + // X86_64-NEXT: ja .LBB3_2 // X86_64-NEXT: mov rax, rcx // X86_64-NEXT: add rax, qword ptr [rdi + 16] // X86_64-NEXT: add ecx, 8 // X86_64-NEXT: mov dword ptr [rdi], ecx // X86_64-NEXT: mov rax, qword ptr [rax] // X86_64-NEXT: ret - // X86_64-NEXT: .LBB2_2: + // X86_64-NEXT: .LBB3_2: // X86_64-NEXT: mov rax, qword ptr [rdi + 8] // X86_64-NEXT: lea rcx, [rax + 8] // X86_64-NEXT: mov qword ptr [rdi + 8], rcx @@ -131,7 +166,7 @@ unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { // X86_64-NEXT_GNUX32: mov ecx, dword ptr [edi] // X86_64-NEXT_GNUX32-NEXT: cmp ecx, 40 - // X86_64-NEXT_GNUX32-NEXT: ja .LBB2_2 + // X86_64-NEXT_GNUX32-NEXT: ja .LBB3_2 // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [edi + 12] // X86_64-NEXT_GNUX32-NEXT: add eax, ecx // X86_64-NEXT_GNUX32-NEXT: add ecx, 8 @@ -162,14 +197,14 @@ unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { // // X86_64: mov ecx, dword ptr [rdi] // X86_64-NEXT: cmp rcx, 32 - // X86_64-NEXT: ja .LBB3_2 + // X86_64-NEXT: ja .LBB4_2 // X86_64-NEXT: mov rdx, qword ptr [rdi + 16] // X86_64-NEXT: mov rax, qword ptr [rdx + rcx] // X86_64-NEXT: mov rdx, qword ptr [rdx + rcx + 8] // X86_64-NEXT: add ecx, 16 // X86_64-NEXT: mov dword ptr [rdi], ecx // X86_64-NEXT: ret - // X86_64-NEXT: .LBB3_2: + // X86_64-NEXT: .LBB4_2: // X86_64-NEXT: mov rcx, qword ptr [rdi + 8] // X86_64-NEXT: add rcx, 15 // X86_64-NEXT: and rcx, -16 @@ -181,7 +216,7 @@ unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { // X86_64-NEXT_GNUX32: mov ecx, dword ptr [edi] // X86_64-NEXT_GNUX32-NEXT: cmp ecx, 32 - // X86_64-NEXT_GNUX32-NEXT: ja .LBB3_2 + // X86_64-NEXT_GNUX32-NEXT: ja .LBB4_2 // X86_64-NEXT_GNUX32-NEXT: mov edx, dword ptr [edi + 12] // X86_64-NEXT_GNUX32-NEXT: mov rax, qword ptr [edx + ecx] // X86_64-NEXT_GNUX32-NEXT: mov rdx, qword ptr [edx + ecx + 8] From 20b6fd622136db582c32a964e3cec9a56030af0d Mon Sep 17 00:00:00 2001 From: Jakob Koschel Date: Wed, 23 Sep 2026 20:06:33 +0000 Subject: [PATCH 13/19] Address review feedback Only check `SanitizerCfiMinimalRuntimeRequiresCfiRecoverOrDiag` if CFI is actually enabled. Use `is_sanitizer_cfi_minimal_runtime_enabled()` helper within `link.rs`. --- compiler/rustc_codegen_ssa/src/back/link.rs | 2 +- compiler/rustc_session/src/session.rs | 6 +++--- tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs | 1 - tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr | 4 +--- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 7c8bb4f59dfba..3a405f275cf4b 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1764,7 +1764,7 @@ fn add_sanitizer_libraries( && (sess.opts.unstable_opts.sanitizer_cfi_diag.unwrap_or(false) || sess.opts.unstable_opts.sanitizer_cfi_recover.unwrap_or(false)) { - if sess.opts.unstable_opts.sanitizer_cfi_minimal_runtime.unwrap_or(false) { + if sess.is_sanitizer_cfi_minimal_runtime_enabled() { link_sanitizer_runtime(sess, flavor, linker, "ubsan_minimal"); } else { link_sanitizer_runtime(sess, flavor, linker, "ubsan"); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index a209219758026..cea3a32729b98 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1688,12 +1688,12 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } } - // LLVM CFI minimal runtime requires CFI Recover or CFI Diag. + // LLVM CFI minimal runtime requires CFI recovery or CFI diagnostics. if sess.is_sanitizer_cfi_minimal_runtime_enabled() { if !sess.is_sanitizer_cfi_enabled() { sess.dcx().emit_err(diagnostics::SanitizerCfiMinimalRuntimeRequiresCfi); - } - if !(sess.is_sanitizer_cfi_recover_enabled() || sess.is_sanitizer_cfi_diag_enabled()) { + } else if !(sess.is_sanitizer_cfi_recover_enabled() || sess.is_sanitizer_cfi_diag_enabled()) + { sess.dcx().emit_err(diagnostics::SanitizerCfiMinimalRuntimeRequiresCfiRecoverOrDiag); } } diff --git a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs index 13b296b4329ee..cb68734608ac7 100644 --- a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs +++ b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.rs @@ -8,4 +8,3 @@ #![no_main] //~? ERROR `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer=cfi` -//~? ERROR `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag` diff --git a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr index 251d0d0817631..5cb83e40dc0c4 100644 --- a/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr +++ b/tests/ui/sanitizer/cfi/minimal-runtime-requires-cfi.stderr @@ -1,6 +1,4 @@ error: `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer=cfi` -error: `-Zsanitizer-cfi-minimal-runtime` requires `-Zsanitizer-cfi-recover` or `-Zsanitizer-cfi-diag` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error From caadd686be32b2116709820c231ca09bc66ceac5 Mon Sep 17 00:00:00 2001 From: Adwin White Date: Fri, 4 Sep 2026 11:37:27 +0800 Subject: [PATCH 14/19] dont lower depth for coroutine witness and rigid opaques in auto trait proving --- .../src/solve/eval_ctxt/mod.rs | 130 +++++++++++++++--- .../src/solve/project_goals/mod.rs | 12 +- ...ower-depth-for-witness-and-rigid-opaque.rs | 31 +++++ 3 files changed, 145 insertions(+), 28 deletions(-) create mode 100644 tests/ui/traits/next-solver/overflow/dont-lower-depth-for-witness-and-rigid-opaque.rs diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index ae5cf61aac91e..823e606eea0a0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -636,12 +636,119 @@ where source: GoalSource, goal: Goal, ) -> Result, NoSolutionOrRerunNonErased> { - let (normalization_nested_goals, goal_evaluation) = - self.evaluate_goal_raw(source, goal, LowerAvailableDepth::Yes)?; + let (normalization_nested_goals, goal_evaluation) = self.evaluate_goal_raw(source, goal)?; assert!(normalization_nested_goals.is_empty()); Ok(goal_evaluation) } + fn evaluate_in_search_graph( + &mut self, + canonical_goal: I::CanonicalInput, + step_kind: PathKind, + ) -> (Result, NoSolution>, AccessedOpaques) { + let increase_depth_for_nested = + match canonical_goal.canonical.value.goal.predicate.kind().skip_binder() { + // We don't lower the available depth for the `NormalizesTo` goal, as evaluating + // it is an extra step only exists in the new solver that behaves like a function + // call rather than an independent nested goal evaluation. So, decreasing the + // available depth may end up regressions which hit the recursion limits for crates + // compiled well with the old solver. + ty::PredicateKind::NormalizesTo(_) => LowerAvailableDepth::No, + // We also don't lower depth for witness and rigid opaque when proving auto traits. + // This is to mitigate the overflow FCW warnings in deeply nested async calls. + // See #159228. + // + // We're eventually going to remove the witness type so it's okay to ignore the + // depth. + // For rigid opaques, revealing hidden types can be viewed as normalization so it's + // consistent with the `NormalizesTo` reasoning above. + ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { + if self.cx().trait_is_auto(pred.trait_ref.def_id) { + match pred.self_ty().kind() { + ty::CoroutineWitness(..) => LowerAvailableDepth::No, + ty::Alias( + ty::IsRigid::Yes, + ty::AliasTy { kind: ty::Opaque { def_id, .. }, .. }, + ) => { + // We only want to skip lowering depth when the proving is done via + // auto trait leakage. If the goal can be proved via item bounds, + // we should lower depth faithfully. + // + // FIXME: We can have param env candidates via TAIT or RTN. + // Ideally we'd instead lower the depth for the nested goal instead. + // Implementing this is a bit harder. + if self + .cx() + .item_self_bounds(def_id.into()) + .skip_binder() + .into_iter() + .any(|bound| { + bound + .as_trait_clause() + .is_some_and(|b| b.def_id() == pred.def_id()) + }) + { + LowerAvailableDepth::Yes + } else { + LowerAvailableDepth::No + } + } + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Str + | ty::Pat(..) + | ty::FnPtr(..) + | ty::Array(..) + | ty::Slice(..) + | ty::RawPtr(..) + | ty::Never + | ty::Tuple(..) + | ty::UnsafeBinder(_) + | ty::Param(..) + | ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(..) + | ty::Alias(_, _) + | ty::Ref(_, _, _) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Dynamic(..) + | ty::Error(_) + | ty::FnDef(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) => LowerAvailableDepth::Yes, + } + } else { + LowerAvailableDepth::Yes + } + } + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(_)) + | ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) + | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(_)) + | ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(_)) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(_, _)) + | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) + | ty::PredicateKind::Subtype(_) + | ty::PredicateKind::Coerce(_) + | ty::PredicateKind::DynCompatible(_) + | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(_)) + | ty::PredicateKind::ConstEquate(_, _) + | ty::PredicateKind::Ambiguous => LowerAvailableDepth::Yes, + }; + self.search_graph.evaluate_goal( + self.cx(), + canonical_goal, + step_kind, + increase_depth_for_nested, + &mut inspect::ProofTreeBuilder::new_noop(), + ) + } + /// Recursively evaluates `goal`, returning the nested goals in case /// the nested goal is a `NormalizesTo` goal. /// @@ -653,7 +760,6 @@ where &mut self, source: GoalSource, goal: Goal, - increase_depth_for_nested: LowerAvailableDepth, ) -> Result<(NestedNormalizationGoals, GoalEvaluation), NoSolutionOrRerunNonErased> { // We only care about one entry per `OpaqueTypeKey` here, // so we only canonicalize the lookup table and ignore @@ -719,13 +825,8 @@ where TypingMode::ErasedNotCoherence(MayBeErased), ); - let (canonical_result, accessed_opaques) = self.search_graph.evaluate_goal( - self.cx(), - canonical_goal, - step_kind, - increase_depth_for_nested, - &mut inspect::ProofTreeBuilder::new_noop(), - ); + let (canonical_result, accessed_opaques) = + self.evaluate_in_search_graph(canonical_goal, step_kind); let should_rerun = should_rerun_after_erased_canonicalization( accessed_opaques, @@ -759,13 +860,8 @@ where let (orig_values, canonical_goal) = canonicalize_goal(self.delegate, goal, &opaque_types, typing_mode); - let (canonical_result, accessed_opaques) = self.search_graph.evaluate_goal( - self.cx(), - canonical_goal, - step_kind, - increase_depth_for_nested, - &mut inspect::ProofTreeBuilder::new_noop(), - ); + let (canonical_result, accessed_opaques) = + self.evaluate_in_search_graph(canonical_goal, step_kind); assert!( !accessed_opaques.might_rerun(), "we run without TypingMode::ErasedNotCoherence, so opaques are available, and we don't retry if the outer typing mode is ErasedNotCoherence: {accessed_opaques:?} after {goal:?}" diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs index db326e6d736a4..9d6b8875071ba 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs @@ -3,7 +3,6 @@ mod free_alias; mod inherent; mod opaque_types; -use rustc_type_ir::search_graph::LowerAvailableDepth; use rustc_type_ir::solve::QueryResultOrRerunNonErased; use rustc_type_ir::{self as ty, Interner, ProjectionClause}; use tracing::{instrument, trace}; @@ -70,16 +69,7 @@ where let ( NestedNormalizationGoals(nested_goals), GoalEvaluation { goal: _, certainty, stalled_on: _, has_changed: _ }, - ) = self.evaluate_goal_raw( - GoalSource::TypeRelating, - normalizes_to, - // We don't lower thr available depth for this `NormalizesTo` goal, as evaluating - // it is an extra step only exists in the new solver that behaves like a function - // call rather than an independent nested goal evaluation. So, decreasing the - // available depth may end up regressions which hit the recursion limits for crates - // compiled well with the old solver. - LowerAvailableDepth::No, - )?; + ) = self.evaluate_goal_raw(GoalSource::TypeRelating, normalizes_to)?; trace!(?nested_goals); diff --git a/tests/ui/traits/next-solver/overflow/dont-lower-depth-for-witness-and-rigid-opaque.rs b/tests/ui/traits/next-solver/overflow/dont-lower-depth-for-witness-and-rigid-opaque.rs new file mode 100644 index 0000000000000..c4d493ec0f8d2 --- /dev/null +++ b/tests/ui/traits/next-solver/overflow/dont-lower-depth-for-witness-and-rigid-opaque.rs @@ -0,0 +1,31 @@ +//@ compile-flags: -Znext-solver +//@ edition: 2024 +//@ check-pass + +// We don't increase recursion depth when proving auto traits for witness +// and rigid opaques. This is to mitigate the FCW warnings in deeply nested +// async calls. See #159228. + +#![recursion_limit = "6"] + +async fn foo1() {} + +async fn foo2() { + foo1().await +} +async fn foo3() { + foo2().await +} +async fn foo4() { + foo3().await +} + +async fn foo5() { + foo4().await +} + +fn assert_send(_: T) {} + +fn main() { + assert_send(foo5()); +} From ec51dae63f95027d6acb731b759de05698892551 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 17 Apr 2026 14:13:43 +0200 Subject: [PATCH 15/19] Check the entire library and cg_clif workspaces for permitted deps in tidy We ship the entire library workspace in the rust-src component, so even if the standard library doesn't depend on a crate, having it in the library workspace still increases the amount of storage a rust install takes. And for cg_clif I personally want to keep deps to a minimum even those not for the backend itself. --- src/tools/tidy/src/deps.rs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 65a7890a5ecc2..2c3a47228ac3f 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -96,10 +96,11 @@ pub(crate) struct WorkspaceInfo<'a> { /// The list of license exceptions. pub(crate) exceptions: ExceptionList, /// Optionally: - /// * A list of crates for which dependencies need to be explicitly allowed. + /// * A list of crates for which dependencies need to be explicitly allowed + /// or None to check the entire workspace. /// * The list of allowed dependencies. /// * The source code location of the allowed dependencies list - crates_and_deps: Option<(&'a [&'a str], &'a [&'a str], ListLocation)>, + crates_and_deps: Option<(Option<&'a [&'a str]>, &'a [&'a str], ListLocation)>, /// Submodules required for the workspace pub(crate) submodules: &'a [&'a str], } @@ -114,7 +115,7 @@ pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ path: ".", exceptions: EXCEPTIONS, crates_and_deps: Some(( - &["rustc-main"], + Some(&["rustc-main"]), PERMITTED_RUSTC_DEPENDENCIES, PERMITTED_RUSTC_DEPS_LOCATION, )), @@ -124,7 +125,7 @@ pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ path: "library", exceptions: EXCEPTIONS_STDLIB, crates_and_deps: Some(( - &["sysroot"], + None, PERMITTED_STDLIB_DEPENDENCIES, PERMITTED_STDLIB_DEPS_LOCATION, )), @@ -140,7 +141,7 @@ pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ path: "compiler/rustc_codegen_cranelift", exceptions: EXCEPTIONS_CRANELIFT, crates_and_deps: Some(( - &["rustc_codegen_cranelift"], + None, PERMITTED_CRANELIFT_DEPENDENCIES, PERMITTED_CRANELIFT_DEPS_LOCATION, )), @@ -664,7 +665,7 @@ pub fn check(root: &Path, cargo: &Path, tidy_ctx: TidyCtx) { } check_license_exceptions(&metadata, path, exceptions, &mut check); if let Some((crates, permitted_deps, location)) = crates_and_deps { - let descr = crates.get(0).unwrap_or(&path); + let descr = crates.map_or(path, |crates| crates.get(0).unwrap_or(&path)); check_permitted_dependencies( &metadata, descr, @@ -927,15 +928,21 @@ fn check_permitted_dependencies( metadata: &Metadata, descr: &str, permitted_dependencies: &[&'static str], - restricted_dependency_crates: &[&'static str], + restricted_dependency_crates: Option<&[&'static str]>, permitted_location: ListLocation, check: &mut RunningCheck, ) { let mut has_permitted_dep_error = false; let mut deps = HashSet::new(); - for to_check in restricted_dependency_crates { - let to_check = pkg_from_name(metadata, to_check); - deps_of(metadata, &to_check.id, &mut deps); + if let Some(restricted_dependency_crates) = restricted_dependency_crates { + for to_check in restricted_dependency_crates { + let to_check = pkg_from_name(metadata, to_check); + deps_of(metadata, &to_check.id, &mut deps); + } + } else { + for to_check in &metadata.packages { + deps_of(metadata, &to_check.id, &mut deps); + } } // Check that the PERMITTED_DEPENDENCIES does not have unused entries. From 96b181cdac89beb9ae230078007d4789dd68712e Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Thu, 3 Sep 2026 10:11:56 +0000 Subject: [PATCH 16/19] tests: Run more pauth tests in CI and make them pass Some pauth tests have ended up failing since they were not run in CI. Make them run and make them pass. --- tests/assembly-llvm/pauth-basic.rs | 2 +- tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs | 1 - tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs | 2 ++ .../pauth/pauth-extern-c-direct-indirect-call.rs | 4 +++- tests/codegen-llvm/pauth/pauth-extern-c.rs | 1 - tests/codegen-llvm/pauth/pauth-extern-weak-global.rs | 1 - tests/codegen-llvm/pauth/pauth-init-fini.rs | 1 - tests/ui/statics/crt-static-pauthtest.rs | 3 +-- tests/ui/target_modifiers/auxiliary/pauth.rs | 3 ++- .../incompatible_pauth.error_generated.stderr | 4 ---- tests/ui/target_modifiers/incompatible_pauth.rs | 6 +++--- 11 files changed, 12 insertions(+), 16 deletions(-) diff --git a/tests/assembly-llvm/pauth-basic.rs b/tests/assembly-llvm/pauth-basic.rs index e240e1317f3a3..34b9c4b5c328c 100644 --- a/tests/assembly-llvm/pauth-basic.rs +++ b/tests/assembly-llvm/pauth-basic.rs @@ -1,8 +1,8 @@ //@ add-minicore //@ assembly-output: emit-asm -//@ only-pauthtest //@ revisions: aarch64_unknown_linux_pauthtest //@ [aarch64_unknown_linux_pauthtest] compile-flags: --target=aarch64-unknown-linux-pauthtest +//@ [aarch64_unknown_linux_pauthtest] compile-flags: -Copt-level=3 //@ [aarch64_unknown_linux_pauthtest] needs-llvm-components: aarch64 #![feature(no_core, lang_items)] diff --git a/tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs b/tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs index 04af1b66140dd..513850f1dd4db 100644 --- a/tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs +++ b/tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs @@ -1,5 +1,4 @@ // ignore-tidy-file-linelength -//@ only-pauthtest //@ revisions: DEFAULT ALL DISABLE_JUMP DISABLE_AUTH_TRAPS DISABLE_CALLS DISABLE_INDIRCT_GOTOS DISABLE_RETURNS DISABLE_INTRINSICS DISABLE_TYPEINFO DISABLE_VT_PTR_ADDR DISABLE_VT_PTR_TYPE NONE //@ add-minicore diff --git a/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs b/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs index 2751494b9de7a..24ab1abb10e67 100644 --- a/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs +++ b/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs @@ -3,6 +3,8 @@ // Make sure that compiler generated functions (main wrapper and __rust_try) also have ptrauth // attributes set correctly. Rustc only generates __rust_try at O0, so use that opt level for the // test. +// Cannot be converted to minicore: catch_unwind is a std API and is what causes rustc to generate +// the __rust_try wrapper this test is checking. //@ needs-llvm-components: aarch64 diff --git a/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs b/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs index 643b428339b73..d01815954b237 100644 --- a/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs +++ b/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs @@ -1,10 +1,12 @@ //@ add-minicore // ignore-tidy-linelength -//@ only-pauthtest //@ revisions: O0_PAUTH O3_PAUTH //@ [O0_PAUTH] needs-llvm-components: aarch64 //@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 +// O3_PAUTH relies on an InstCombine fold that removes the redundant `ptrauth` bundle from calls. +// That fold first shipped in LLVM 22, gate this revision accordingly. +//@ [O3_PAUTH] min-llvm-version: 22 //@ [O3_PAUTH] needs-llvm-components: aarch64 //@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 diff --git a/tests/codegen-llvm/pauth/pauth-extern-c.rs b/tests/codegen-llvm/pauth/pauth-extern-c.rs index b22cf6943a804..07e27965e9ebf 100644 --- a/tests/codegen-llvm/pauth/pauth-extern-c.rs +++ b/tests/codegen-llvm/pauth/pauth-extern-c.rs @@ -1,5 +1,4 @@ // ignore-tidy-file-linelength -//@ only-pauthtest //@ add-minicore //@ revisions: O0_PAUTH O3_PAUTH O0_PAUTH-ELF-GOT O3_PAUTH-ELF-GOT O0_NO_PAUTH O3_NO_PAUTH diff --git a/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs b/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs index a83298dd5725c..b7c1f5fd06479 100644 --- a/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs +++ b/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs @@ -1,5 +1,4 @@ // ignore-tidy-linelength -//@ only-pauthtest //@ revisions: O0_PAUTH O3_PAUTH O0_NO_PAUTH O3_NO_PAUTH //@ add-minicore diff --git a/tests/codegen-llvm/pauth/pauth-init-fini.rs b/tests/codegen-llvm/pauth/pauth-init-fini.rs index b54006e56f1f4..5550100ae7f92 100644 --- a/tests/codegen-llvm/pauth/pauth-init-fini.rs +++ b/tests/codegen-llvm/pauth/pauth-init-fini.rs @@ -1,6 +1,5 @@ // ignore-tidy-file-linelength //@ add-minicore -//@ only-pauthtest //@ revisions: O0_PAUTH O3_PAUTH O0_PAUTH-ADDR-DISC O3_PAUTH-ADDR-DISC O0_PAUTH-NO-INIT-FINI O3_PAUTH-NO-INIT-FINI //@ [O0_PAUTH] needs-llvm-components: aarch64 diff --git a/tests/ui/statics/crt-static-pauthtest.rs b/tests/ui/statics/crt-static-pauthtest.rs index bc5badc600d88..a3fed7f9379ef 100644 --- a/tests/ui/statics/crt-static-pauthtest.rs +++ b/tests/ui/statics/crt-static-pauthtest.rs @@ -1,9 +1,8 @@ //@ compile-flags: -C target-feature=+crt-static --target aarch64-unknown-linux-pauthtest //@ needs-llvm-components: aarch64 -//@ only-pauthtest - #![feature(no_core)] +#![no_core] #![no_main] //~? ERROR pointer authentication requires dynamic linking. Statically linked libc is incompatible, disable it using `-C target-feature=-crt-static` diff --git a/tests/ui/target_modifiers/auxiliary/pauth.rs b/tests/ui/target_modifiers/auxiliary/pauth.rs index 761f4a520a4d2..32bd7651c0b44 100644 --- a/tests/ui/target_modifiers/auxiliary/pauth.rs +++ b/tests/ui/target_modifiers/auxiliary/pauth.rs @@ -1,6 +1,7 @@ //@ compile-flags: --target aarch64-unknown-linux-pauthtest -Zpointer-authentication=+calls,+init-fini //@ needs-llvm-components: aarch64 -//@ only-pauthtest +//@ no-prefer-dynamic +//@ ignore-backends: gcc #![feature(no_core)] #![crate_type = "rlib"] diff --git a/tests/ui/target_modifiers/incompatible_pauth.error_generated.stderr b/tests/ui/target_modifiers/incompatible_pauth.error_generated.stderr index ddc05e4b55322..1e9390d37e822 100644 --- a/tests/ui/target_modifiers/incompatible_pauth.error_generated.stderr +++ b/tests/ui/target_modifiers/incompatible_pauth.error_generated.stderr @@ -1,8 +1,4 @@ error: mixing `-Zpointer-authentication` will cause an ABI mismatch in crate `incompatible_pauth` - --> $DIR/incompatible_pauth.rs:14:1 - | -LL | #![feature(no_core)] - | ^ | = help: the `-Zpointer-authentication` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely = note: `-Zpointer-authentication=+calls,-init-fini` in this crate is incompatible with `-Zpointer-authentication=+calls,+init-fini` in dependency `pauth` diff --git a/tests/ui/target_modifiers/incompatible_pauth.rs b/tests/ui/target_modifiers/incompatible_pauth.rs index 6fe77a6916568..302e845e8bd4f 100644 --- a/tests/ui/target_modifiers/incompatible_pauth.rs +++ b/tests/ui/target_modifiers/incompatible_pauth.rs @@ -9,12 +9,12 @@ //@ [ok_reverse_order] check-pass //@ [error_generated] compile-flags: -Zpointer-authentication=+calls,-init-fini //@ needs-llvm-components: aarch64 -//@ only-pauthtest +//@ ignore-backends: gcc #![feature(no_core)] -//[error_generated]~^ ERROR mixing `-Zpointer-authentication` will cause an ABI mismatch in crate -//`incompatible_pauth` #![crate_type = "rlib"] #![no_core] extern crate pauth; + +//[error_generated]~? ERROR mixing `-Zpointer-authentication` will cause an ABI mismatch in crate `incompatible_pauth` From fc538ecc32511eb73230edf12960ca4f8f977be7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 24 Sep 2026 15:58:46 +0200 Subject: [PATCH 17/19] Add PermittedDeps type --- src/tools/tidy/src/deps.rs | 63 +++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 2c3a47228ac3f..e1a5495f81954 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -95,16 +95,34 @@ pub(crate) struct WorkspaceInfo<'a> { pub(crate) path: &'a str, /// The list of license exceptions. pub(crate) exceptions: ExceptionList, - /// Optionally: - /// * A list of crates for which dependencies need to be explicitly allowed - /// or None to check the entire workspace. - /// * The list of allowed dependencies. - /// * The source code location of the allowed dependencies list - crates_and_deps: Option<(Option<&'a [&'a str]>, &'a [&'a str], ListLocation)>, + /// The list of dependencies that are allowed. If None, any crate with an + /// acceptable license is allowed. + allowed_deps: Option>, /// Submodules required for the workspace pub(crate) submodules: &'a [&'a str], } +#[derive(Clone, Copy)] +struct PermittedDeps<'a> { + /// A list of crates for which dependencies need to be explicitly allowed + /// or None to check the entire workspace. + roots: Option<&'a [&'a str]>, + /// The list of allowed dependencies. + deps: &'a [&'a str], + /// The source code location of the allowed dependencies list. + deps_loc: ListLocation, +} + +impl<'a> PermittedDeps<'a> { + const fn new( + roots: Option<&'a [&'a str]>, + deps: &'a [&'a str], + deps_loc: ListLocation, + ) -> Self { + Self { roots, deps, deps_loc } + } +} + const WORKSPACE_LOCATION: ListLocation = location!(+4); /// The workspaces to check for licensing and optionally permitted dependencies. @@ -114,7 +132,7 @@ pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ WorkspaceInfo { path: ".", exceptions: EXCEPTIONS, - crates_and_deps: Some(( + allowed_deps: Some(PermittedDeps::new( Some(&["rustc-main"]), PERMITTED_RUSTC_DEPENDENCIES, PERMITTED_RUSTC_DEPS_LOCATION, @@ -124,7 +142,7 @@ pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ WorkspaceInfo { path: "library", exceptions: EXCEPTIONS_STDLIB, - crates_and_deps: Some(( + allowed_deps: Some(PermittedDeps::new( None, PERMITTED_STDLIB_DEPENDENCIES, PERMITTED_STDLIB_DEPS_LOCATION, @@ -134,13 +152,13 @@ pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ WorkspaceInfo { path: "library/stdarch", exceptions: EXCEPTIONS_STDARCH, - crates_and_deps: None, + allowed_deps: None, submodules: &[], }, WorkspaceInfo { path: "compiler/rustc_codegen_cranelift", exceptions: EXCEPTIONS_CRANELIFT, - crates_and_deps: Some(( + allowed_deps: Some(PermittedDeps::new( None, PERMITTED_CRANELIFT_DEPENDENCIES, PERMITTED_CRANELIFT_DEPS_LOCATION, @@ -150,19 +168,19 @@ pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ WorkspaceInfo { path: "compiler/rustc_codegen_gcc", exceptions: EXCEPTIONS_GCC, - crates_and_deps: None, + allowed_deps: None, submodules: &[], }, WorkspaceInfo { path: "src/bootstrap", exceptions: EXCEPTIONS_BOOTSTRAP, - crates_and_deps: None, + allowed_deps: None, submodules: &[], }, WorkspaceInfo { path: "src/tools/cargo", exceptions: EXCEPTIONS_CARGO, - crates_and_deps: None, + allowed_deps: None, submodules: &["src/tools/cargo"], }, // FIXME uncomment once all deps are vendored @@ -179,25 +197,25 @@ pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ WorkspaceInfo { path: "src/tools/rust-analyzer", exceptions: EXCEPTIONS_RUST_ANALYZER, - crates_and_deps: None, + allowed_deps: None, submodules: &[], }, WorkspaceInfo { path: "src/tools/rustbook", exceptions: EXCEPTIONS_RUSTBOOK, - crates_and_deps: None, + allowed_deps: None, submodules: &["src/doc/book", "src/doc/reference"], }, WorkspaceInfo { path: "src/tools/rustc-perf", exceptions: EXCEPTIONS_RUSTC_PERF, - crates_and_deps: None, + allowed_deps: None, submodules: &["src/tools/rustc-perf"], }, WorkspaceInfo { path: "tests/run-make-cargo/uefi-qemu/uefi_qemu_test", exceptions: EXCEPTIONS_UEFI_QEMU_TEST, - crates_and_deps: None, + allowed_deps: None, submodules: &[], }, ]; @@ -639,7 +657,8 @@ pub fn check(root: &Path, cargo: &Path, tidy_ctx: TidyCtx) { check_proc_macro_dep_list(root, cargo, bless, &mut check); - for &WorkspaceInfo { path, exceptions, crates_and_deps, submodules } in WORKSPACES { + for &WorkspaceInfo { path, exceptions, allowed_deps: crates_and_deps, submodules } in WORKSPACES + { if has_missing_submodule(root, submodules, tidy_ctx.is_running_on_ci()) { continue; } @@ -664,14 +683,14 @@ pub fn check(root: &Path, cargo: &Path, tidy_ctx: TidyCtx) { check.error(format!("{path} is part of another workspace ({} != {}), remove from `WORKSPACES` ({WORKSPACE_LOCATION})", absolute_root.display(), absolute_root_real.display())); } check_license_exceptions(&metadata, path, exceptions, &mut check); - if let Some((crates, permitted_deps, location)) = crates_and_deps { - let descr = crates.map_or(path, |crates| crates.get(0).unwrap_or(&path)); + if let Some(PermittedDeps { roots, deps: permitted_deps, deps_loc }) = crates_and_deps { + let descr = roots.map_or(path, |roots| roots.get(0).unwrap_or(&path)); check_permitted_dependencies( &metadata, descr, permitted_deps, - crates, - location, + roots, + deps_loc, &mut check, ); } From 288a941096948e3a6d9e85b7628dcf9b12cab633 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 24 Sep 2026 16:53:39 +0200 Subject: [PATCH 18/19] add jank leak check test --- ...straint-from-nested-projection.next.stderr | 22 +++++++++++++ .../constraint-from-nested-projection.rs | 33 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 tests/ui/higher-ranked/leak-check/constraint-from-nested-projection.next.stderr create mode 100644 tests/ui/higher-ranked/leak-check/constraint-from-nested-projection.rs diff --git a/tests/ui/higher-ranked/leak-check/constraint-from-nested-projection.next.stderr b/tests/ui/higher-ranked/leak-check/constraint-from-nested-projection.next.stderr new file mode 100644 index 0000000000000..bf3e6943f0d5a --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/constraint-from-nested-projection.next.stderr @@ -0,0 +1,22 @@ +error[E0283]: type annotations needed + --> $DIR/constraint-from-nested-projection.rs:31:5 + | +LL | pick::<(), _>(); + | ^^^^^^^^^^^^^ cannot infer type of the type parameter `U` declared on the function `pick` + | +note: multiple `impl`s satisfying `(): Pick<_>` found + --> $DIR/constraint-from-nested-projection.rs:25:1 + | +LL | impl RequiresProject<'a>> Pick for T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl Pick for T {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `pick` + --> $DIR/constraint-from-nested-projection.rs:28:12 + | +LL | fn pick, U>() {} + | ^^^^^^^ required by this bound in `pick` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/higher-ranked/leak-check/constraint-from-nested-projection.rs b/tests/ui/higher-ranked/leak-check/constraint-from-nested-projection.rs new file mode 100644 index 0000000000000..fda0fa7efa57c --- /dev/null +++ b/tests/ui/higher-ranked/leak-check/constraint-from-nested-projection.rs @@ -0,0 +1,33 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver +//@[old] check-pass + +// Evaluate in the old solver does apply constraints from nested `Projection` obligations, +// as they can constrain otherwise unconstrained inference variables. This also allows +// `Projection` goals to otherwise influence its parent obligation by returning constraints +// from matching the impl header. +// +// The new solver entirely ignores region constraints from nested goals in the leak check. +// This is the one case where the the implementation of the new solver will actually weaken +// the leak check. + +trait ProjectStatic<'a> { + type Assoc; +} +impl ProjectStatic<'static> for () { + type Assoc = u32; +} + +trait RequiresProject<'a> {} +impl<'a, T: ProjectStatic<'a, Assoc = u32>> RequiresProject<'a> for T {} + +trait Pick {} +impl RequiresProject<'a>> Pick for T {} +impl Pick for T {} + +fn pick, U>() {} + +fn main() { + pick::<(), _>(); + //[next]~^ ERROR: type annotations needed +} From db4d334408af5e18bc39515b416addf9e263af1b Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Thu, 24 Sep 2026 13:50:10 -0400 Subject: [PATCH 19/19] cleanup: clean up more dependencies that are unused Some were wholly unused, others were unused other than as dev-deps but specified as regular dependencies. This cleans things up a little bit. I used an LLM to smoke out the candidates for this cleanup, but the rest was done by hand. --- Cargo.lock | 1 - src/tools/build-manifest/Cargo.toml | 1 - src/tools/jsondoclint/Cargo.toml | 4 +++- src/tools/remote-test-client/Cargo.toml | 2 +- src/tools/replace-version-placeholder/Cargo.toml | 1 - 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5f35ce252b78e..99c2914ff343d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3440,7 +3440,6 @@ name = "replace-version-placeholder" version = "0.1.0" dependencies = [ "tidy", - "walkdir", ] [[package]] diff --git a/src/tools/build-manifest/Cargo.toml b/src/tools/build-manifest/Cargo.toml index 0b766a3e557c5..0900afd6594fe 100644 --- a/src/tools/build-manifest/Cargo.toml +++ b/src/tools/build-manifest/Cargo.toml @@ -16,5 +16,4 @@ rayon = "1.5.1" hex = "0.4.2" [build-dependencies] -serde = "1" serde_json = "1" diff --git a/src/tools/jsondoclint/Cargo.toml b/src/tools/jsondoclint/Cargo.toml index 848c0b37ae94e..e835549878834 100644 --- a/src/tools/jsondoclint/Cargo.toml +++ b/src/tools/jsondoclint/Cargo.toml @@ -9,7 +9,9 @@ edition = "2024" anyhow = "1.0.62" clap = { version = "4.0.15", features = ["derive"] } fs-err = "2.8.1" -rustc-hash = "2.0.0" rustdoc-json-types = { version = "0.1.0", path = "../../rustdoc-json-types" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.85" + +[dev-dependencies] +rustc-hash = "2.0.0" diff --git a/src/tools/remote-test-client/Cargo.toml b/src/tools/remote-test-client/Cargo.toml index 6fe690ba20380..70a6e896a01ab 100644 --- a/src/tools/remote-test-client/Cargo.toml +++ b/src/tools/remote-test-client/Cargo.toml @@ -3,5 +3,5 @@ name = "remote-test-client" version = "0.1.0" edition = "2021" -[dependencies] +[dev-dependencies] assert_cmd = "2" diff --git a/src/tools/replace-version-placeholder/Cargo.toml b/src/tools/replace-version-placeholder/Cargo.toml index 346ce6bd1dbfd..e581194fcf09f 100644 --- a/src/tools/replace-version-placeholder/Cargo.toml +++ b/src/tools/replace-version-placeholder/Cargo.toml @@ -7,4 +7,3 @@ edition = "2021" [dependencies] tidy = { path = "../tidy" } -walkdir = "2"