Rollup merge of #147043 - ilovepi:default-sanitizers, r=petrochenkov
Add default sanitizers to TargetOptions Some sanitizers are part of a system's ABI, like the shadow call stack on Aarch64 and RISC-V Fuchsia. Typically ABI options have other spellings, but LLVM has, for historical reasons, marked this as a sanitizer instead of an alternate ABI option. As a result, Fuchsia targets may not be compiled against the correct ABI unless this option is set. This hasn't caused correctness problems, since the backend reserves the SCS register, and thus preserves its value. But this is an issue for unwinding, as the SCS will not be an array of PCs describing the call complete call chain, and will have gaps from callers that don't use the correct ABI. In the long term, I'd like to see all the sanitizer configs that all frontends copy from clang moved into llvm's libFrontend, and exposed so that frontend consumers can use a small set of simple APIs to use sanitizers in a consistent way across the LLVM ecosystem, but that work is not yet ready today.
This commit is contained in:
commit
efdc8aca3e
13 changed files with 43 additions and 31 deletions
|
|
@ -96,7 +96,7 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if cx.tcx.sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::MEMORY) {
|
||||
} else if cx.tcx.sess.sanitizers().contains(SanitizerSet::MEMORY) {
|
||||
// If we're not optimising, *but* memory sanitizer is on, emit noundef, since it affects
|
||||
// memory sanitizer's behavior.
|
||||
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ pub(crate) fn sanitize_attrs<'ll, 'tcx>(
|
|||
no_sanitize: SanitizerSet,
|
||||
) -> SmallVec<[&'ll Attribute; 4]> {
|
||||
let mut attrs = SmallVec::new();
|
||||
let enabled = tcx.sess.opts.unstable_opts.sanitizer - no_sanitize;
|
||||
let enabled = tcx.sess.sanitizers() - no_sanitize;
|
||||
if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) {
|
||||
attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx));
|
||||
}
|
||||
|
|
@ -240,13 +240,7 @@ fn probestack_attr<'ll, 'tcx>(cx: &SimpleCx<'ll>, tcx: TyCtxt<'tcx>) -> Option<&
|
|||
// Currently stack probes seem somewhat incompatible with the address
|
||||
// sanitizer and thread sanitizer. With asan we're already protected from
|
||||
// stack overflow anyway so we don't really need stack probes regardless.
|
||||
if tcx
|
||||
.sess
|
||||
.opts
|
||||
.unstable_opts
|
||||
.sanitizer
|
||||
.intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD)
|
||||
{
|
||||
if tcx.sess.sanitizers().intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1227,7 +1227,7 @@ fn add_sanitizer_libraries(
|
|||
return;
|
||||
}
|
||||
|
||||
let sanitizer = sess.opts.unstable_opts.sanitizer;
|
||||
let sanitizer = sess.sanitizers();
|
||||
if sanitizer.contains(SanitizerSet::ADDRESS) {
|
||||
link_sanitizer_runtime(sess, flavor, linker, "asan");
|
||||
}
|
||||
|
|
@ -2497,11 +2497,7 @@ fn add_order_independent_options(
|
|||
&& crate_type == CrateType::Executable
|
||||
&& !matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))
|
||||
{
|
||||
let prefix = if sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::ADDRESS) {
|
||||
"asan/"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let prefix = if sess.sanitizers().contains(SanitizerSet::ADDRESS) { "asan/" } else { "" };
|
||||
cmd.link_arg(format!("--dynamic-linker={prefix}ld.so.1"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ impl ModuleConfig {
|
|||
debug_info_for_profiling: sess.opts.unstable_opts.debug_info_for_profiling,
|
||||
instrument_coverage: if_regular!(sess.instrument_coverage(), false),
|
||||
|
||||
sanitizer: if_regular!(sess.opts.unstable_opts.sanitizer, SanitizerSet::empty()),
|
||||
sanitizer: if_regular!(sess.sanitizers(), SanitizerSet::empty()),
|
||||
sanitizer_dataflow_abilist: if_regular!(
|
||||
sess.opts.unstable_opts.sanitizer_dataflow_abilist.clone(),
|
||||
Vec::new()
|
||||
|
|
|
|||
|
|
@ -412,7 +412,7 @@ impl CStore {
|
|||
match (&left_name_val, &right_name_val) {
|
||||
(Some(l), Some(r)) => match l.1.opt.cmp(&r.1.opt) {
|
||||
cmp::Ordering::Equal => {
|
||||
if !l.1.consistent(&tcx.sess.opts, Some(&r.1)) {
|
||||
if !l.1.consistent(&tcx.sess, Some(&r.1)) {
|
||||
report_diff(
|
||||
&l.0.prefix,
|
||||
&l.0.name,
|
||||
|
|
@ -424,26 +424,26 @@ impl CStore {
|
|||
right_name_val = None;
|
||||
}
|
||||
cmp::Ordering::Greater => {
|
||||
if !r.1.consistent(&tcx.sess.opts, None) {
|
||||
if !r.1.consistent(&tcx.sess, None) {
|
||||
report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name));
|
||||
}
|
||||
right_name_val = None;
|
||||
}
|
||||
cmp::Ordering::Less => {
|
||||
if !l.1.consistent(&tcx.sess.opts, None) {
|
||||
if !l.1.consistent(&tcx.sess, None) {
|
||||
report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None);
|
||||
}
|
||||
left_name_val = None;
|
||||
}
|
||||
},
|
||||
(Some(l), None) => {
|
||||
if !l.1.consistent(&tcx.sess.opts, None) {
|
||||
if !l.1.consistent(&tcx.sess, None) {
|
||||
report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None);
|
||||
}
|
||||
left_name_val = None;
|
||||
}
|
||||
(None, Some(r)) => {
|
||||
if !r.1.consistent(&tcx.sess.opts, None) {
|
||||
if !r.1.consistent(&tcx.sess, None) {
|
||||
report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name));
|
||||
}
|
||||
right_name_val = None;
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ pub fn walk_native_lib_search_dirs<R>(
|
|||
|| sess.target.os == "linux"
|
||||
|| sess.target.os == "fuchsia"
|
||||
|| sess.target.is_like_aix
|
||||
|| sess.target.is_like_darwin && !sess.opts.unstable_opts.sanitizer.is_empty()
|
||||
|| sess.target.is_like_darwin && !sess.sanitizers().is_empty()
|
||||
{
|
||||
f(&sess.target_tlib_path.dir, false)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg {
|
|||
ins_sym!(sym::relocation_model, sess.target.relocation_model.desc_symbol());
|
||||
}
|
||||
|
||||
for mut s in sess.opts.unstable_opts.sanitizer {
|
||||
for mut s in sess.sanitizers() {
|
||||
// KASAN is still ASAN under the hood, so it uses the same attribute.
|
||||
if s == SanitizerSet::KERNELADDRESS {
|
||||
s = SanitizerSet::ADDRESS;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ use rustc_target::spec::{
|
|||
use crate::config::*;
|
||||
use crate::search_paths::SearchPath;
|
||||
use crate::utils::NativeLib;
|
||||
use crate::{EarlyDiagCtxt, lint};
|
||||
use crate::{EarlyDiagCtxt, Session, lint};
|
||||
|
||||
macro_rules! insert {
|
||||
($opt_name:ident, $opt_expr:expr, $sub_hashes:expr) => {
|
||||
|
|
@ -111,12 +111,12 @@ mod target_modifier_consistency_check {
|
|||
lparsed & tmod_sanitizers == rparsed & tmod_sanitizers
|
||||
}
|
||||
pub(super) fn sanitizer_cfi_normalize_integers(
|
||||
opts: &Options,
|
||||
sess: &Session,
|
||||
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 sess.sanitizers().contains(SanitizerSet::KCFI) {
|
||||
if let Some(r) = r {
|
||||
return l.extend().tech_value == r.extend().tech_value;
|
||||
} else {
|
||||
|
|
@ -133,7 +133,7 @@ impl TargetModifier {
|
|||
}
|
||||
// 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 {
|
||||
pub fn consistent(&self, sess: &Session, other: Option<&TargetModifier>) -> bool {
|
||||
assert!(other.is_none() || self.opt == other.unwrap().opt);
|
||||
match self.opt {
|
||||
OptionsTargetModifiers::UnstableOptions(unstable) => match unstable {
|
||||
|
|
@ -142,7 +142,7 @@ impl TargetModifier {
|
|||
}
|
||||
UnstableOptionsTargetModifiers::sanitizer_cfi_normalize_integers => {
|
||||
return target_modifier_consistency_check::sanitizer_cfi_normalize_integers(
|
||||
opts, self, other,
|
||||
sess, self, other,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
|
|
@ -2575,6 +2575,7 @@ written to standard error output)"),
|
|||
retpoline_external_thunk: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER],
|
||||
"enables retpoline-external-thunk, retpoline-indirect-branches and retpoline-indirect-calls \
|
||||
target features (default: no)"),
|
||||
#[rustc_lint_opt_deny_field_access("use `Session::sanitizers()` instead of this field")]
|
||||
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],
|
||||
|
|
|
|||
|
|
@ -323,7 +323,7 @@ impl Session {
|
|||
}
|
||||
|
||||
pub fn is_sanitizer_cfi_enabled(&self) -> bool {
|
||||
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
|
||||
self.sanitizers().contains(SanitizerSet::CFI)
|
||||
}
|
||||
|
||||
pub fn is_sanitizer_cfi_canonical_jump_tables_disabled(&self) -> bool {
|
||||
|
|
@ -347,7 +347,7 @@ impl Session {
|
|||
}
|
||||
|
||||
pub fn is_sanitizer_kcfi_enabled(&self) -> bool {
|
||||
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::KCFI)
|
||||
self.sanitizers().contains(SanitizerSet::KCFI)
|
||||
}
|
||||
|
||||
pub fn is_split_lto_unit_enabled(&self) -> bool {
|
||||
|
|
@ -527,7 +527,7 @@ impl Session {
|
|||
// AddressSanitizer and KernelAddressSanitizer uses lifetimes to detect use after scope bugs.
|
||||
// MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
|
||||
// HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future.
|
||||
|| self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS)
|
||||
|| self.sanitizers().intersects(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS)
|
||||
}
|
||||
|
||||
pub fn diagnostic_width(&self) -> usize {
|
||||
|
|
@ -922,6 +922,10 @@ impl Session {
|
|||
min
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sanitizers(&self) -> SanitizerSet {
|
||||
return self.opts.unstable_opts.sanitizer | self.target.options.default_sanitizers;
|
||||
}
|
||||
}
|
||||
|
||||
// JUSTIFICATION: part of session construction
|
||||
|
|
|
|||
|
|
@ -214,6 +214,11 @@ impl Target {
|
|||
supported_sanitizers.into_iter().fold(SanitizerSet::empty(), |a, b| a | b);
|
||||
}
|
||||
|
||||
if let Some(default_sanitizers) = json.default_sanitizers {
|
||||
base.default_sanitizers =
|
||||
default_sanitizers.into_iter().fold(SanitizerSet::empty(), |a, b| a | b);
|
||||
}
|
||||
|
||||
forward!(generate_arange_section);
|
||||
forward!(supports_stack_protector);
|
||||
forward!(small_data_threshold_support);
|
||||
|
|
@ -392,6 +397,7 @@ impl ToJson for Target {
|
|||
target_option_val!(split_debuginfo);
|
||||
target_option_val!(supported_split_debuginfo);
|
||||
target_option_val!(supported_sanitizers);
|
||||
target_option_val!(default_sanitizers);
|
||||
target_option_val!(c_enum_min_bits);
|
||||
target_option_val!(generate_arange_section);
|
||||
target_option_val!(supports_stack_protector);
|
||||
|
|
@ -612,6 +618,7 @@ struct TargetSpecJson {
|
|||
split_debuginfo: Option<SplitDebuginfo>,
|
||||
supported_split_debuginfo: Option<StaticCow<[SplitDebuginfo]>>,
|
||||
supported_sanitizers: Option<Vec<SanitizerSet>>,
|
||||
default_sanitizers: Option<Vec<SanitizerSet>>,
|
||||
generate_arange_section: Option<bool>,
|
||||
supports_stack_protector: Option<bool>,
|
||||
small_data_threshold_support: Option<SmallDataThresholdSupport>,
|
||||
|
|
|
|||
|
|
@ -2410,6 +2410,13 @@ pub struct TargetOptions {
|
|||
/// distributed with the target, the sanitizer should still appear in this list for the target.
|
||||
pub supported_sanitizers: SanitizerSet,
|
||||
|
||||
/// The sanitizers that are enabled by default on this target.
|
||||
///
|
||||
/// Note that the support here is at a codegen level. If the machine code with sanitizer
|
||||
/// enabled can generated on this target, but the necessary supporting libraries are not
|
||||
/// distributed with the target, the sanitizer should still appear in this list for the target.
|
||||
pub default_sanitizers: SanitizerSet,
|
||||
|
||||
/// Minimum number of bits in #[repr(C)] enum. Defaults to the size of c_int
|
||||
pub c_enum_min_bits: Option<u64>,
|
||||
|
||||
|
|
@ -2658,6 +2665,7 @@ impl Default for TargetOptions {
|
|||
// `Off` is supported by default, but targets can remove this manually, e.g. Windows.
|
||||
supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
|
||||
supported_sanitizers: SanitizerSet::empty(),
|
||||
default_sanitizers: SanitizerSet::empty(),
|
||||
c_enum_min_bits: None,
|
||||
generate_arange_section: true,
|
||||
supports_stack_protector: true,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ pub(crate) fn target() -> Target {
|
|||
| SanitizerSet::CFI
|
||||
| SanitizerSet::LEAK
|
||||
| SanitizerSet::SHADOWCALLSTACK;
|
||||
base.default_sanitizers = SanitizerSet::SHADOWCALLSTACK;
|
||||
base.supports_xray = true;
|
||||
|
||||
base.add_pre_link_args(
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ pub(crate) fn target() -> Target {
|
|||
base.max_atomic_width = Some(64);
|
||||
base.stack_probes = StackProbeType::Inline;
|
||||
base.supported_sanitizers = SanitizerSet::SHADOWCALLSTACK;
|
||||
base.default_sanitizers = SanitizerSet::SHADOWCALLSTACK;
|
||||
base.supports_xray = true;
|
||||
|
||||
Target {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue