diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 90459090ced87..e72eeab63a590 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -95,13 +95,15 @@ mod target_modifier_consistency_check { l: &TargetModifier, r: Option<&TargetModifier>, ) -> bool { - let mut lparsed: SanitizerSet = sess.target.options.default_sanitizers; + let mut lparsed: SanitizerSet = SanitizerSet::empty(); let lval = if l.value_name.is_empty() { None } else { Some(l.value_name.as_str()) }; parse::parse_sanitizers(&mut lparsed, lval); + let lparsed = lparsed.combine_with_defaults(sess.target.options.default_sanitizers); - let mut rparsed: SanitizerSet = sess.target.options.default_sanitizers; + let mut rparsed: SanitizerSet = SanitizerSet::empty(); let rval = r.filter(|v| !v.value_name.is_empty()).map(|v| v.value_name.as_str()); parse::parse_sanitizers(&mut rparsed, rval); + let rparsed = rparsed.combine_with_defaults(sess.target.options.default_sanitizers); // Some sanitizers need to be target modifiers, and some do not. // For now, we should mark all sanitizers as target modifiers except for these: diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index f04f40dd17168..dad43f94a57db 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -929,7 +929,7 @@ impl Session { let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly) || self.opts.output_types.contains_key(&OutputType::Bitcode) // AddressSanitizer and MemorySanitizer use alloca name when reporting an issue. - || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY); + || self.sanitizers().intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY); !more_names } } @@ -1178,7 +1178,10 @@ impl Session { } pub fn sanitizers(&self) -> SanitizerSet { - return self.opts.unstable_opts.sanitizer | self.target.options.default_sanitizers; + self.opts + .unstable_opts + .sanitizer + .combine_with_defaults(self.target.options.default_sanitizers) } pub fn pointer_authentication(&self) -> bool { @@ -1497,7 +1500,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { // Sanitizers can only be used on platforms that we know have working sanitizer codegen. let supported_sanitizers = sess.target.options.supported_sanitizers; - let mut unsupported_sanitizers = sess.opts.unstable_opts.sanitizer - supported_sanitizers; + let mut unsupported_sanitizers = sess.sanitizers() - supported_sanitizers; // Niche: if `fixed-x18`, or effectively switching on `reserved-x18` flag, is enabled // we should allow Shadow Call Stack sanitizer. if sess.opts.unstable_opts.fixed_x18 && sess.target.arch == Arch::AArch64 { @@ -1518,7 +1521,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } // Cannot mix and match mutually-exclusive sanitizers. - if let Some((first, second)) = sess.opts.unstable_opts.sanitizer.mutually_exclusive() { + if let Some((first, second)) = sess.sanitizers().mutually_exclusive() { sess.dcx().emit_err(diagnostics::CannotMixAndMatchSanitizers { first: first.to_string(), second: second.to_string(), @@ -1526,10 +1529,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } // Cannot enable crt-static with sanitizers on Linux - if sess.crt_static(None) - && !sess.opts.unstable_opts.sanitizer.is_empty() - && !sess.target.is_like_msvc - { + if sess.crt_static(None) && !sess.sanitizers().is_empty() && !sess.target.is_like_msvc { sess.dcx().emit_err(diagnostics::CannotEnableCrtStaticLinux); } diff --git a/compiler/rustc_structures/src/lib.rs b/compiler/rustc_structures/src/lib.rs index a7ebea5ba9943..cb5dccdfcd180 100644 --- a/compiler/rustc_structures/src/lib.rs +++ b/compiler/rustc_structures/src/lib.rs @@ -13,3 +13,6 @@ pub use crate_type::CrateType; pub use limit::Limit; pub use native_lib_kind::NativeLibKind; pub use sanitizer_set::SanitizerSet; + +#[cfg(test)] +mod tests; diff --git a/compiler/rustc_structures/src/sanitizer_set.rs b/compiler/rustc_structures/src/sanitizer_set.rs index bce77f3abc05b..a41577441de8c 100644 --- a/compiler/rustc_structures/src/sanitizer_set.rs +++ b/compiler/rustc_structures/src/sanitizer_set.rs @@ -95,6 +95,20 @@ impl SanitizerSet { .find(|&(a, b)| self.contains(*a) && self.contains(*b)) .copied() } + + /// Disable default sanitizers that are incompatible with explicitly requested ones, + /// matching Clang's `SanitizerArgs` driver logic. + pub fn combine_with_defaults(self, mut defaults: SanitizerSet) -> SanitizerSet { + for &(a, b) in Self::MUTUALLY_EXCLUSIVE { + if defaults.contains(a) && self.contains(b) { + defaults -= a; + } + if defaults.contains(b) && self.contains(a) { + defaults -= b; + } + } + self | defaults + } } /// Formats a sanitizer set as a comma separated list of sanitizers' names. diff --git a/compiler/rustc_structures/src/tests.rs b/compiler/rustc_structures/src/tests.rs new file mode 100644 index 0000000000000..3d61d5bf3331f --- /dev/null +++ b/compiler/rustc_structures/src/tests.rs @@ -0,0 +1,36 @@ +use super::*; + +#[test] +fn test_combine_with_defaults_no_conflict() { + let defaults = SanitizerSet::SHADOWCALLSTACK; + let explicit = SanitizerSet::ADDRESS; + assert_eq!( + explicit.combine_with_defaults(defaults), + SanitizerSet::ADDRESS | SanitizerSet::SHADOWCALLSTACK + ); +} + +#[test] +fn test_combine_with_defaults_safestack_address_conflict() { + let defaults = SanitizerSet::SAFESTACK; + let explicit = SanitizerSet::ADDRESS; + // SafeStack should be implicitly disabled when Address is explicitly provided. + assert_eq!(explicit.combine_with_defaults(defaults), SanitizerSet::ADDRESS); +} + +#[test] +fn test_combine_with_defaults_empty_explicit() { + let defaults = SanitizerSet::SAFESTACK; + let explicit = SanitizerSet::empty(); + assert_eq!(explicit.combine_with_defaults(defaults), SanitizerSet::SAFESTACK); +} + +#[test] +fn test_combine_with_defaults_safestack_cfi() { + let defaults = SanitizerSet::SAFESTACK; + let explicit = SanitizerSet::CFI; + assert_eq!( + explicit.combine_with_defaults(defaults), + SanitizerSet::CFI | SanitizerSet::SAFESTACK + ); +}