Skip to content

Commit

Permalink
sanitizers: Add stable and unstable sanitizers
Browse files Browse the repository at this point in the history
Add suppport for separating sanitizers support in stable and unstable
for supported targets.
  • Loading branch information
rcvalle authored and Ramon de C Valle committed Apr 23, 2024
1 parent 7f2fc33 commit 91bd12e
Show file tree
Hide file tree
Showing 124 changed files with 528 additions and 303 deletions.
3 changes: 1 addition & 2 deletions compiler/rustc_codegen_llvm/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use rustc_session::config;
pub use rustc_target::abi::call::*;
use rustc_target::abi::{self, HasDataLayout, Int, Size};
pub use rustc_target::spec::abi::Abi;
use rustc_target::spec::SanitizerSet;

use libc::c_uint;
use smallvec::SmallVec;
Expand Down Expand Up @@ -82,7 +81,7 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'
attrs.push(llattr.create_attr(cx.llcx));
}
}
} else if cx.tcx.sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::MEMORY) {
} else if cx.tcx.sess.is_sanitizer_memory_enabled() {
// If we're not optimising, *but* memory sanitizer is on, emit noundef, since it affects
// memory sanitizer's behavior.

Expand Down
10 changes: 2 additions & 8 deletions compiler/rustc_codegen_llvm/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pub fn sanitize_attrs<'ll>(
no_sanitize: SanitizerSet,
) -> SmallVec<[&'ll Attribute; 4]> {
let mut attrs = SmallVec::new();
let enabled = cx.tcx.sess.opts.unstable_opts.sanitizer - no_sanitize;
let enabled = cx.tcx.sess.opts.cg.sanitizer - no_sanitize;
if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) {
attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx));
}
Expand Down Expand Up @@ -192,13 +192,7 @@ fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
// 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 cx
.sess()
.opts
.unstable_opts
.sanitizer
.intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD)
{
if cx.sess().is_sanitizer_address_enabled() || cx.sess().is_sanitizer_thread_enabled() {
return None;
}

Expand Down
8 changes: 2 additions & 6 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1222,7 +1222,7 @@ fn add_sanitizer_libraries(
return;
}

let sanitizer = sess.opts.unstable_opts.sanitizer;
let sanitizer = sess.opts.cg.sanitizer;
if sanitizer.contains(SanitizerSet::ADDRESS) {
link_sanitizer_runtime(sess, flavor, linker, "asan");
}
Expand Down Expand Up @@ -2350,11 +2350,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.is_sanitizer_address_enabled() { "asan/" } else { "" };
cmd.arg(format!("--dynamic-linker={prefix}ld.so.1"));
}

Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_codegen_ssa/src/back/symbol_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_middle::ty::{self, SymbolName, TyCtxt};
use rustc_middle::ty::{GenericArgKind, GenericArgsRef};
use rustc_middle::util::Providers;
use rustc_session::config::{CrateType, OomStrategy};
use rustc_target::spec::{SanitizerSet, TlsModel};
use rustc_target::spec::TlsModel;

pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel {
crates_export_threshold(tcx.crate_types())
Expand Down Expand Up @@ -265,15 +265,15 @@ fn exported_symbols_provider_local(
}));
}

if tcx.sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::MEMORY) {
if tcx.sess.is_sanitizer_memory_enabled() {
let mut msan_weak_symbols = Vec::new();

// Similar to profiling, preserve weak msan symbol during LTO.
if tcx.sess.opts.unstable_opts.sanitizer_recover.contains(SanitizerSet::MEMORY) {
if tcx.sess.is_sanitizer_memory_enabled() {
msan_weak_symbols.push("__msan_keep_going");
}

if tcx.sess.opts.unstable_opts.sanitizer_memory_track_origins != 0 {
if tcx.sess.is_sanitizer_memory_track_origins_enabled() {
msan_weak_symbols.push("__msan_track_origins");
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ impl ModuleConfig {
false
),

sanitizer: if_regular!(sess.opts.unstable_opts.sanitizer, SanitizerSet::empty()),
sanitizer: if_regular!(sess.opts.cg.sanitizer, SanitizerSet::empty()),
sanitizer_dataflow_abilist: if_regular!(
sess.opts.unstable_opts.sanitizer_dataflow_abilist.clone(),
Vec::new()
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,7 @@ fn test_codegen_options_tracking_hash() {
tracked!(profile_use, Some(PathBuf::from("abc")));
tracked!(relocation_model, Some(RelocModel::Pic));
tracked!(relro_level, Some(RelroLevel::Full));
tracked!(sanitizer, SanitizerSet::ADDRESS);
tracked!(soft_float, true);
tracked!(split_debuginfo, Some(SplitDebuginfo::Packed));
tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0));
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_session/src/config/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,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.opts.cg.sanitizer {
// KASAN is still ASAN under the hood, so it uses the same attribute.
if s == SanitizerSet::KERNELADDRESS {
s = SanitizerSet::ADDRESS;
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1554,6 +1554,8 @@ options! {
"output remarks for these optimization passes (space separated, or \"all\")"),
rpath: bool = (false, parse_bool, [UNTRACKED],
"set rpath values in libs/exes (default: no)"),
sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
"use a sanitizer"),
save_temps: bool = (false, parse_bool, [UNTRACKED],
"save all temporary output files during compilation (default: no)"),
soft_float: bool = (false, parse_bool, [TRACKED],
Expand Down Expand Up @@ -1907,8 +1909,6 @@ 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],
"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],
Expand Down
35 changes: 24 additions & 11 deletions compiler/rustc_session/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,8 +356,12 @@ impl Session {
self.instrument_coverage() && self.opts.unstable_opts.coverage_options.mcdc
}

pub fn is_sanitizer_address_enabled(&self) -> bool {
self.opts.cg.sanitizer.contains(SanitizerSet::ADDRESS)
}

pub fn is_sanitizer_cfi_enabled(&self) -> bool {
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
self.opts.cg.sanitizer.contains(SanitizerSet::CFI)
}

pub fn is_sanitizer_cfi_canonical_jump_tables_disabled(&self) -> bool {
Expand All @@ -377,7 +381,19 @@ impl Session {
}

pub fn is_sanitizer_kcfi_enabled(&self) -> bool {
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::KCFI)
self.opts.cg.sanitizer.contains(SanitizerSet::KCFI)
}

pub fn is_sanitizer_memory_enabled(&self) -> bool {
self.opts.cg.sanitizer.contains(SanitizerSet::MEMORY)
}

pub fn is_sanitizer_memory_track_origins_enabled(&self) -> bool {
self.opts.unstable_opts.sanitizer_memory_track_origins != 0
}

pub fn is_sanitizer_thread_enabled(&self) -> bool {
self.opts.cg.sanitizer.contains(SanitizerSet::THREAD)
}

pub fn is_split_lto_unit_enabled(&self) -> bool {
Expand Down Expand Up @@ -595,7 +611,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.opts.cg.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS)
}

pub fn diagnostic_width(&self) -> usize {
Expand Down Expand Up @@ -722,7 +738,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.opts.cg.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY);
!more_names
}
}
Expand Down Expand Up @@ -1167,8 +1183,8 @@ 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 unsupported_sanitizers = sess.opts.unstable_opts.sanitizer - supported_sanitizers;
let supported_sanitizers = sess.target.options.supported_sanitizers.supported_sanitizers();
let unsupported_sanitizers = sess.opts.cg.sanitizer - supported_sanitizers;
match unsupported_sanitizers.into_iter().count() {
0 => {}
1 => {
Expand All @@ -1182,7 +1198,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
}
}
// Cannot mix and match sanitizers.
let mut sanitizer_iter = sess.opts.unstable_opts.sanitizer.into_iter();
let mut sanitizer_iter = sess.opts.cg.sanitizer.into_iter();
if let (Some(first), Some(second)) = (sanitizer_iter.next(), sanitizer_iter.next()) {
sess.dcx().emit_err(errors::CannotMixAndMatchSanitizers {
first: first.to_string(),
Expand All @@ -1191,10 +1207,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.opts.cg.sanitizer.is_empty() && !sess.target.is_like_msvc {
sess.dcx().emit_err(errors::CannotEnableCrtStaticLinux);
}

Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_target/src/spec/base/android.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::spec::{base, SanitizerSet, TargetOptions, TlsModel};
use crate::spec::{base, SanitizerSet, SanitizerSupport, TargetOptions, TlsModel};

pub fn opts() -> TargetOptions {
let mut base = base::linux::opts();
Expand All @@ -7,7 +7,8 @@ pub fn opts() -> TargetOptions {
base.default_dwarf_version = 2;
base.tls_model = TlsModel::Emulated;
base.has_thread_local = false;
base.supported_sanitizers = SanitizerSet::ADDRESS;
base.supported_sanitizers =
SanitizerSupport { stable: SanitizerSet::empty(), unstable: SanitizerSet::ADDRESS };
// This is for backward compatibility, see https://github.com/rust-lang/rust/issues/49867
// for context. (At that time, there was no `-C force-unwind-tables`, so the only solution
// was to always emit `uwtable`).
Expand Down
110 changes: 81 additions & 29 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,41 @@ impl ToJson for SanitizerSet {
}
}

#[derive(Clone, Copy, PartialEq, Hash, Debug)]
/// Sanitizers supported by a target.
pub struct SanitizerSupport {
/// Sanitizers supported by a target that can be used on stable.
pub stable: SanitizerSet,
/// Sanitizers supported by a target that cannot be used on stable.
pub unstable: SanitizerSet,
}

impl SanitizerSupport {
/// Returns the set of stable sanitizers.
pub fn stable_sanitizers(&self) -> SanitizerSet {
self.stable
}

/// Returns the set of supported sanitizers.
pub fn supported_sanitizers(&self) -> SanitizerSet {
self.stable | self.unstable
}

/// Returns the set of unstable sanitizers.
pub fn unstable_sanitizers(&self) -> SanitizerSet {
self.unstable
}
}

impl ToJson for SanitizerSupport {
fn to_json(&self) -> Json {
let mut object = serde_json::Map::new();
object.insert("stable".to_string(), self.stable.to_json());
object.insert("unstable".to_string(), self.unstable.to_json());
Json::Object(object)
}
}

#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum FramePointer {
/// Forces the machine code generator to always preserve the frame pointers.
Expand Down Expand Up @@ -2284,7 +2319,7 @@ pub struct TargetOptions {
/// 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 supported_sanitizers: SanitizerSet,
pub supported_sanitizers: SanitizerSupport,

/// Minimum number of bits in #[repr(C)] enum. Defaults to the size of c_int
pub c_enum_min_bits: Option<u64>,
Expand Down Expand Up @@ -2516,7 +2551,10 @@ impl Default for TargetOptions {
split_debuginfo: Default::default(),
// `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(),
supported_sanitizers: SanitizerSupport {
stable: SanitizerSet::empty(),
unstable: SanitizerSet::empty(),
},
c_enum_min_bits: None,
generate_arange_section: true,
supports_stack_protector: true,
Expand Down Expand Up @@ -2718,6 +2756,35 @@ impl Target {

let mut incorrect_type = vec![];

let parse_sanitizer_set = |sanitizers_json: &Json| -> Result<SanitizerSet, String> {
let mut sanitizer_set = SanitizerSet::empty();
if let Some(sanitizers) = sanitizers_json.as_array() {
for sanitizer in sanitizers {
let name = sanitizer
.as_str()
.ok_or_else(|| "Sanitizer name must be a string".to_string())?;
sanitizer_set |= match name {
"address" => SanitizerSet::ADDRESS,
"cfi" => SanitizerSet::CFI,
"dataflow" => SanitizerSet::DATAFLOW,
"kcfi" => SanitizerSet::KCFI,
"kernel-address" => SanitizerSet::KERNELADDRESS,
"leak" => SanitizerSet::LEAK,
"memory" => SanitizerSet::MEMORY,
"memtag" => SanitizerSet::MEMTAG,
"safestack" => SanitizerSet::SAFESTACK,
"shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK,
"thread" => SanitizerSet::THREAD,
"hwaddress" => SanitizerSet::HWADDRESS,
_ => return Err(format!("unknown sanitizer {}", name)),
};
}
} else {
return Err("Expected a list of sanitizers".to_string());
}
Ok(sanitizer_set)
};

macro_rules! key {
($key_name:ident) => ( {
let name = (stringify!($key_name)).replace("_", "-");
Expand Down Expand Up @@ -2951,32 +3018,17 @@ impl Target {
)),
}).unwrap_or(Ok(()))
} );
($key_name:ident, SanitizerSet) => ( {
let name = (stringify!($key_name)).replace("_", "-");
if let Some(o) = obj.remove(&name) {
if let Some(a) = o.as_array() {
for s in a {
base.$key_name |= match s.as_str() {
Some("address") => SanitizerSet::ADDRESS,
Some("cfi") => SanitizerSet::CFI,
Some("dataflow") => SanitizerSet::DATAFLOW,
Some("kcfi") => SanitizerSet::KCFI,
Some("kernel-address") => SanitizerSet::KERNELADDRESS,
Some("leak") => SanitizerSet::LEAK,
Some("memory") => SanitizerSet::MEMORY,
Some("memtag") => SanitizerSet::MEMTAG,
Some("safestack") => SanitizerSet::SAFESTACK,
Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK,
Some("thread") => SanitizerSet::THREAD,
Some("hwaddress") => SanitizerSet::HWADDRESS,
Some(s) => return Err(format!("unknown sanitizer {}", s)),
_ => return Err(format!("not a string: {:?}", s)),
};
}
} else {
incorrect_type.push(name)
}
}
($key_name:ident, SanitizerSupport) => ( {
let stable_sanitizers_json = obj.remove("stable")
.unwrap_or_else(|| serde_json::Value::Array(Vec::new()));
let unstable_sanitizers_json = obj.remove("unstable")
.unwrap_or_else(|| serde_json::Value::Array(Vec::new()));
let stable_sanitizers = parse_sanitizer_set(&stable_sanitizers_json)?;
let unstable_sanitizers = parse_sanitizer_set(&unstable_sanitizers_json)?;
base.$key_name = SanitizerSupport {
stable: stable_sanitizers,
unstable: unstable_sanitizers,
};
Ok::<(), String>(())
} );
($key_name:ident, link_self_contained_components) => ( {
Expand Down Expand Up @@ -3248,7 +3300,7 @@ impl Target {
key!(debuginfo_kind, DebuginfoKind)?;
key!(split_debuginfo, SplitDebuginfo)?;
key!(supported_split_debuginfo, fallible_list)?;
key!(supported_sanitizers, SanitizerSet)?;
key!(supported_sanitizers, SanitizerSupport)?;
key!(generate_arange_section, bool);
key!(supports_stack_protector, bool);
key!(entry_name);
Expand Down
Loading

0 comments on commit 91bd12e

Please sign in to comment.