From 1148afe5d7330cb223449fa0866cc7e2aa4311c5 Mon Sep 17 00:00:00 2001 From: Leonard Chan Date: Thu, 11 Jun 2026 20:25:24 +0000 Subject: [PATCH] rustc_session: accumulate multiple -Zsanitizer target modifiers When passing multiple `-Zsanitizer` flags to the compiler (e.g., `-Zsanitizer=address -Zsanitizer=shadow-call-stack`), the options parser was overwriting the previous values in `target_modifiers` instead of accumulating them. This resulted in only the last sanitizer being recorded in the crate metadata's target modifiers, even though the frontend correctly enabled all of them. The only way to provide multiple sanitizers was to combine them into a comma-separated list passed to a single `-Zsanitizer=` flag, but this does not fit well into the GN build system where different targets may pass different combinations of sanitizer flags. Consequently, this caused spurious "incompatible target modifiers" ABI mismatch errors when linking against dependencies compiled with accumulated flags (e.g., `-Zsanitizer=address,shadow-call-stack`). Fix this by entry-modifying the `target_modifiers` map to accumulate the sanitizers as a comma-separated list when the option is `sanitizer`. Also add a codegen test verifying that both CFI and SafeStack attributes/metadata are present when enabled together via multiple flags. Test: ./x.py test tests/codegen-llvm/sanitizer/multiple-sanitizers.rs --- compiler/rustc_session/src/options.rs | 24 ++++++++++++++++++- .../sanitizer/multiple-sanitizers.rs | 16 +++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/codegen-llvm/sanitizer/multiple-sanitizers.rs diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 6ae0d9721bc00..88c38aa047b1d 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -726,7 +726,29 @@ fn build_options( } if let Some(tmod) = *tmod { let v = value.map_or(String::new(), ToOwned::to_owned); - collected_options.target_modifiers.insert(tmod, v); + + // Accumulate all the -Zsanitizer flags into a single target modifier. + match tmod { + OptionsTargetModifiers::UnstableOptions( + UnstableOptionsTargetModifiers::Sanitizer, + ) => { + collected_options + .target_modifiers + .entry(tmod) + .and_modify(|existing| { + if !existing.is_empty() && !v.is_empty() { + existing.push(','); + existing.push_str(&v); + } else if existing.is_empty() { + *existing = v.clone(); + } + }) + .or_insert(v); + } + _ => { + collected_options.target_modifiers.insert(tmod, v); + } + } } if let Some(mitigation) = mitigation { collected_options.mitigations.reset_mitigation(*mitigation, index); diff --git a/tests/codegen-llvm/sanitizer/multiple-sanitizers.rs b/tests/codegen-llvm/sanitizer/multiple-sanitizers.rs new file mode 100644 index 0000000000000..f320a1673d2e9 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/multiple-sanitizers.rs @@ -0,0 +1,16 @@ +// Verifies that multiple compatible sanitizers (CFI + SafeStack) can be enabled together +// and their target modifiers accumulate instead of overwriting. +// +//@ only-x86_64 +//@ only-linux +//@ compile-flags: -Zsanitizer=cfi -Zsanitizer=safestack -Clto -Ccodegen-units=1 -C unsafe-allow-abi-mismatch=sanitizer + +#![crate_type = "lib"] + +// CHECK: ; Function Attrs:{{.*}}safestack +// CHECK: define{{.*}}foo{{.*}}!type +pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { + f(arg) +} + +// CHECK: attributes #0 = {{.*}}safestack{{.*}}