Skip to content

Sanitizers target modificators #138736

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions compiler/rustc_metadata/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,23 @@ metadata_incompatible_rustc =

metadata_incompatible_target_modifiers =
mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}`
.note = `{$flag_name_prefixed}={$flag_local_value}` in this crate is incompatible with `{$flag_name_prefixed}={$flag_extern_value}` in dependency `{$extern_crate}`
.note = `{$flag_name_prefixed}={$local_value}` in this crate is incompatible with `{$flag_name_prefixed}={$extern_value}` in dependency `{$extern_crate}`
.help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely

metadata_incompatible_target_modifiers_help_allow = if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch={$flag_name}` to silence this error
metadata_incompatible_target_modifiers_help_fix = set `{$flag_name_prefixed}={$flag_extern_value}` in this crate or `{$flag_name_prefixed}={$flag_local_value}` in `{$extern_crate}`
metadata_incompatible_target_modifiers_help_fix = set `{$flag_name_prefixed}={$extern_value}` in this crate or `{$flag_name_prefixed}={$local_value}` in `{$extern_crate}`

metadata_incompatible_target_modifiers_help_fix_l_missed = set `{$flag_name_prefixed}={$extern_value}` in this crate or unset `{$flag_name_prefixed}` in `{$extern_crate}`

metadata_incompatible_target_modifiers_help_fix_r_missed = unset `{$flag_name_prefixed}` in this crate or set `{$flag_name_prefixed}={$local_value}` in `{$extern_crate}`

metadata_incompatible_target_modifiers_l_missed =
mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}`
.note = unset `{$flag_name_prefixed}` in this crate is incompatible with `{$flag_name_prefixed}={$extern_value}` in dependency `{$extern_crate}`
.help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely
metadata_incompatible_target_modifiers_r_missed =
mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}`
.note = `{$flag_name_prefixed}={$local_value}` in this crate is incompatible with unset `{$flag_name_prefixed}` in dependency `{$extern_crate}`
.help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely
metadata_incompatible_wasm_link =
`wasm_import_module` is incompatible with other arguments in `#[link]` attributes

Expand Down
79 changes: 60 additions & 19 deletions compiler/rustc_metadata/src/creader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,57 +358,98 @@ impl CStore {
) {
let span = krate.spans.inner_span.shrink_to_lo();
let allowed_flag_mismatches = &tcx.sess.opts.cg.unsafe_allow_abi_mismatch;
let name = tcx.crate_name(LOCAL_CRATE);
let local_crate = tcx.crate_name(LOCAL_CRATE);
let tmod_extender = |tmod: &TargetModifier| (tmod.extend(), tmod.clone());
let report_diff = |prefix: &String,
opt_name: &String,
flag_local_value: &String,
flag_extern_value: &String| {
flag_local_value: Option<&String>,
flag_extern_value: Option<&String>| {
if allowed_flag_mismatches.contains(&opt_name) {
return;
}
tcx.dcx().emit_err(errors::IncompatibleTargetModifiers {
span,
extern_crate: data.name(),
local_crate: name,
flag_name: opt_name.clone(),
flag_name_prefixed: format!("-{}{}", prefix, opt_name),
flag_local_value: flag_local_value.to_string(),
flag_extern_value: flag_extern_value.to_string(),
});
let extern_crate = data.name();
let flag_name = opt_name.clone();
let flag_name_prefixed = format!("-{}{}", prefix, opt_name);

match (flag_local_value, flag_extern_value) {
(Some(local_value), Some(extern_value)) => {
tcx.dcx().emit_err(errors::IncompatibleTargetModifiers {
span,
extern_crate,
local_crate,
flag_name,
flag_name_prefixed,
local_value: local_value.to_string(),
extern_value: extern_value.to_string(),
})
}
(None, Some(extern_value)) => {
tcx.dcx().emit_err(errors::IncompatibleTargetModifiersLMissed {
span,
extern_crate,
local_crate,
flag_name,
flag_name_prefixed,
extern_value: extern_value.to_string(),
})
}
(Some(local_value), None) => {
tcx.dcx().emit_err(errors::IncompatibleTargetModifiersRMissed {
span,
extern_crate,
local_crate,
flag_name,
flag_name_prefixed,
local_value: local_value.to_string(),
})
}
(None, None) => panic!("Incorrect target modifiers report_diff(None, None)"),
};
};
let mut it1 = mods.iter().map(tmod_extender);
let mut it2 = dep_mods.iter().map(tmod_extender);
let mut left_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None;
let mut right_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None;
let no_val = "*".to_string();
loop {
left_name_val = left_name_val.or_else(|| it1.next());
right_name_val = right_name_val.or_else(|| it2.next());
match (&left_name_val, &right_name_val) {
(Some(l), Some(r)) => match l.1.opt.cmp(&r.1.opt) {
cmp::Ordering::Equal => {
if l.0.tech_value != r.0.tech_value {
report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &r.1.value_name);
if !l.1.consistent(&tcx.sess.opts, Some(&r.1)) {
report_diff(
&l.0.prefix,
&l.0.name,
Some(&l.1.value_name),
Some(&r.1.value_name),
);
}
left_name_val = None;
right_name_val = None;
}
cmp::Ordering::Greater => {
report_diff(&r.0.prefix, &r.0.name, &no_val, &r.1.value_name);
if !r.1.consistent(&tcx.sess.opts, None) {
report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name));
}
right_name_val = None;
}
cmp::Ordering::Less => {
report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &no_val);
if !l.1.consistent(&tcx.sess.opts, None) {
report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None);
}
left_name_val = None;
}
},
(Some(l), None) => {
report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &no_val);
if !l.1.consistent(&tcx.sess.opts, None) {
report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None);
}
left_name_val = None;
}
(None, Some(r)) => {
report_diff(&r.0.prefix, &r.0.name, &no_val, &r.1.value_name);
if !r.1.consistent(&tcx.sess.opts, None) {
report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name));
}
right_name_val = None;
}
(None, None) => break,
Expand Down
36 changes: 34 additions & 2 deletions compiler/rustc_metadata/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -759,8 +759,40 @@ pub struct IncompatibleTargetModifiers {
pub local_crate: Symbol,
pub flag_name: String,
pub flag_name_prefixed: String,
pub flag_local_value: String,
pub flag_extern_value: String,
pub local_value: String,
pub extern_value: String,
}

#[derive(Diagnostic)]
#[diag(metadata_incompatible_target_modifiers_l_missed)]
#[help]
#[note]
#[help(metadata_incompatible_target_modifiers_help_fix_l_missed)]
#[help(metadata_incompatible_target_modifiers_help_allow)]
pub struct IncompatibleTargetModifiersLMissed {
#[primary_span]
pub span: Span,
pub extern_crate: Symbol,
pub local_crate: Symbol,
pub flag_name: String,
pub flag_name_prefixed: String,
pub extern_value: String,
}

#[derive(Diagnostic)]
#[diag(metadata_incompatible_target_modifiers_r_missed)]
#[help]
#[note]
#[help(metadata_incompatible_target_modifiers_help_fix_r_missed)]
#[help(metadata_incompatible_target_modifiers_help_allow)]
pub struct IncompatibleTargetModifiersRMissed {
#[primary_span]
pub span: Span,
pub extern_crate: Symbol,
pub local_crate: Symbol,
pub flag_name: String,
pub flag_name_prefixed: String,
pub local_value: String,
}

#[derive(Diagnostic)]
Expand Down
123 changes: 97 additions & 26 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,56 +83,127 @@ pub struct TargetModifier {
pub value_name: String,
}

mod target_modifier_consistency_check {
use super::*;
pub(super) fn sanitizer(l: &TargetModifier, r: Option<&TargetModifier>) -> bool {
let mut lparsed: SanitizerSet = Default::default();
let lval = if l.value_name.is_empty() { None } else { Some(l.value_name.as_str()) };
parse::parse_sanitizers(&mut lparsed, lval);

let mut rparsed: SanitizerSet = Default::default();
let rval = r.filter(|v| !v.value_name.is_empty()).map(|v| v.value_name.as_str());
parse::parse_sanitizers(&mut rparsed, rval);

// Some sanitizers need to be target modifiers, and some do not.
// For now, we should mark all sanitizers as target modifiers except for these:
// AddressSanitizer, LeakSanitizer
let tmod_sanitizers = SanitizerSet::MEMORY
| SanitizerSet::THREAD
| SanitizerSet::HWADDRESS
| SanitizerSet::CFI
| SanitizerSet::MEMTAG
| SanitizerSet::SHADOWCALLSTACK
| SanitizerSet::KCFI
| SanitizerSet::KERNELADDRESS
| SanitizerSet::SAFESTACK
| SanitizerSet::DATAFLOW;

lparsed & tmod_sanitizers == rparsed & tmod_sanitizers
}
pub(super) fn sanitizer_cfi_normalize_integers(
opts: &Options,
l: &TargetModifier,
r: Option<&TargetModifier>,
) -> bool {
// For kCFI, the helper flag -Zsanitizer-cfi-normalize-integers should also be a target modifier
if opts.unstable_opts.sanitizer.contains(SanitizerSet::KCFI) {
if let Some(r) = r {
return l.extend().tech_value == r.extend().tech_value;
} else {
return false;
}
}
true
}
}

impl TargetModifier {
pub fn extend(&self) -> ExtendedTargetModifierInfo {
self.opt.reparse(&self.value_name)
}
// Custom consistency check for target modifiers (or default `l.tech_value == r.tech_value`)
// When other is None, consistency with default value is checked
pub fn consistent(&self, opts: &Options, other: Option<&TargetModifier>) -> bool {
assert!(other.is_none() || self.opt == other.unwrap().opt);
match self.opt {
OptionsTargetModifiers::UnstableOptions(unstable) => match unstable {
UnstableOptionsTargetModifiers::sanitizer => {
return target_modifier_consistency_check::sanitizer(self, other);
}
UnstableOptionsTargetModifiers::sanitizer_cfi_normalize_integers => {
return target_modifier_consistency_check::sanitizer_cfi_normalize_integers(
opts, self, other,
);
}
_ => {}
},
_ => {}
};
match other {
Some(other) => self.extend().tech_value == other.extend().tech_value,
None => false,
}
}
}

fn tmod_push_impl(
opt: OptionsTargetModifiers,
tmod_vals: &BTreeMap<OptionsTargetModifiers, String>,
tmods: &mut Vec<TargetModifier>,
) {
tmods.push(TargetModifier { opt, value_name: tmod_vals.get(&opt).cloned().unwrap_or_default() })
if let Some(v) = tmod_vals.get(&opt) {
tmods.push(TargetModifier { opt, value_name: v.clone() })
}
}

macro_rules! tmod_push {
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $mods:expr, $tmod_vals:expr) => {
tmod_push_impl(
OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt_name),
$tmod_vals,
$mods,
);
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr) => {
if *$opt_expr != $init {
tmod_push_impl(
OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt_name),
$tmod_vals,
$mods,
);
}
};
}

macro_rules! gather_tmods {
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr,
[SUBSTRUCT], [TARGET_MODIFIER]) => {
compile_error!("SUBSTRUCT can't be target modifier");
};
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr,
[UNTRACKED], [TARGET_MODIFIER]) => {
tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals)
tmod_push!($struct_name, $tmod_enum_name, $opt_name, $opt_expr, $init, $mods, $tmod_vals)
};
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr,
[TRACKED], [TARGET_MODIFIER]) => {
tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals)
tmod_push!($struct_name, $tmod_enum_name, $opt_name, $opt_expr, $init, $mods, $tmod_vals)
};
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr,
[TRACKED_NO_CRATE_HASH], [TARGET_MODIFIER]) => {
tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals)
tmod_push!($struct_name, $tmod_enum_name, $opt_name, $opt_expr, $init, $mods, $tmod_vals)
};
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr,
[SUBSTRUCT], []) => {
$opt_expr.gather_target_modifiers($mods, $tmod_vals);
};
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr,
[UNTRACKED], []) => {{}};
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr,
[TRACKED], []) => {{}};
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr,
[TRACKED_NO_CRATE_HASH], []) => {{}};
}

Expand Down Expand Up @@ -474,7 +545,8 @@ macro_rules! tmod_enum {
$($pout)*
Self::$opt => {
let mut parsed : $t = Default::default();
parse::$parse(&mut parsed, Some($puser_value));
let val = if $puser_value.is_empty() { None } else { Some($puser_value) };
parse::$parse(&mut parsed, val);
ExtendedTargetModifierInfo {
prefix: $prefix.to_string(),
name: stringify!($opt).to_string().replace('_', "-"),
Expand Down Expand Up @@ -569,7 +641,7 @@ macro_rules! options {
_tmod_vals: &BTreeMap<OptionsTargetModifiers, String>,
) {
$({
gather_tmods!($struct_name, $tmod_enum_name, $opt, &self.$opt, _mods, _tmod_vals,
gather_tmods!($struct_name, $tmod_enum_name, $opt, &self.$opt, $init, _mods, _tmod_vals,
[$dep_tracking_marker], [$($tmod),*]);
})*
}
Expand Down Expand Up @@ -681,10 +753,9 @@ fn build_options<O: Default>(
),
}
}
if let Some(tmod) = *tmod
&& let Some(value) = value
{
target_modifiers.insert(tmod, value.to_string());
if let Some(tmod) = *tmod {
let v = value.map_or(String::new(), ToOwned::to_owned);
target_modifiers.insert(tmod, v);
}
}
None => early_dcx.early_fatal(format!("unknown {outputname} option: `{key}`")),
Expand Down Expand Up @@ -2423,13 +2494,13 @@ options! {
remark_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
"directory into which to write optimization remarks (if not specified, they will be \
written to standard error output)"),
sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED TARGET_MODIFIER],
"use a sanitizer"),
sanitizer_cfi_canonical_jump_tables: Option<bool> = (Some(true), parse_opt_bool, [TRACKED],
"enable canonical jump tables (default: yes)"),
sanitizer_cfi_generalize_pointers: Option<bool> = (None, parse_opt_bool, [TRACKED],
"enable generalizing pointer types (default: no)"),
sanitizer_cfi_normalize_integers: Option<bool> = (None, parse_opt_bool, [TRACKED],
sanitizer_cfi_normalize_integers: Option<bool> = (None, parse_opt_bool, [TRACKED TARGET_MODIFIER],
"enable normalizing integer types (default: no)"),
sanitizer_dataflow_abilist: Vec<String> = (Vec::new(), parse_comma_list, [TRACKED],
"additional ABI list files that control how shadow parameters are passed (comma separated)"),
Expand Down
2 changes: 2 additions & 0 deletions tests/codegen/naked-asan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//@ needs-sanitizer-address
//@ compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static

//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer

#![crate_type = "lib"]
#![no_std]
#![feature(abi_x86_interrupt, naked_functions)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
//
//@ revisions:ASAN ASAN-FAT-LTO
//@ compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static
//@ compile-flags: -C unsafe-allow-abi-mismatch=sanitizer
//@[ASAN] compile-flags:
//@[ASAN-FAT-LTO] compile-flags: -Cprefer-dynamic=false -Clto=fat

Expand Down
Loading
Loading