Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
if unsafe { miri::native_lib::init_sv() }.is_err() {
1#![feature(rustc_private, stmt_expr_attributes)]2#![allow(3 clippy::manual_range_contains,4 clippy::useless_format,5 clippy::field_reassign_with_default,6 clippy::needless_lifetimes7)]89// The rustc crates we need10extern crate rustc_codegen_ssa;11extern crate rustc_data_structures;12extern crate rustc_driver;13extern crate rustc_hir;14extern crate rustc_hir_analysis;15extern crate rustc_interface;16extern crate rustc_log;17extern crate rustc_middle;18extern crate rustc_session;19extern crate rustc_span;2021/// See docs in https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc/src/main.rs22/// and https://github.com/rust-lang/rust/pull/146627 for why we need this.23///24/// FIXME(madsmtm): This is loaded from the sysroot that was built with the other `rustc` crates25/// above, instead of via Cargo as you'd normally do. This is currently needed for LTO due to26/// https://github.com/rust-lang/cc-rs/issues/1613.27#[cfg(feature = "jemalloc")]28// Make sure `--all-features` works: only Linux and macOS actually use jemalloc, and not on arm32.29#[cfg(all(30 any(target_os = "linux", target_os = "macos"),31 any(target_arch = "x86_64", target_arch = "x86", target_arch = "aarch64"),32))]33extern crate tikv_jemalloc_sys as _;3435mod log;3637use std::env;38use std::num::{NonZero, NonZeroI32};39use std::ops::Range;40use std::process::ExitCode;41use std::rc::Rc;42use std::str::FromStr;43use std::sync::atomic::{AtomicU32, Ordering};4445use miri::{46 BacktraceStyle, BorrowTrackerMethod, GenmcConfig, GenmcCtx, MiriConfig, MiriEntryFnType,47 ProvenanceMode, TreeBorrowsParams, ValidationMode, run_genmc_mode,48};49use rustc_codegen_ssa::traits::CodegenBackend;50use rustc_data_structures::sync::{self, DynSync};51use rustc_driver::Compilation;52use rustc_hir::def_id::LOCAL_CRATE;53use rustc_hir::{self as hir, Node};54use rustc_hir_analysis::check::check_function_signature;55use rustc_interface::interface::Config;56use rustc_interface::util::DummyCodegenBackend;57use rustc_log::tracing::debug;58use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;59use rustc_middle::middle::exported_symbols::{60 ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel,61};62use rustc_middle::query::LocalCrate;63use rustc_middle::traits::{ObligationCause, ObligationCauseCode};64use rustc_middle::ty::{self, Ty, TyCtxt};65use rustc_session::config::{CrateType, ErrorOutputType, OptLevel};66use rustc_session::{EarlyDiagCtxt, Session};67use rustc_span::def_id::DefId;6869use crate::log::setup::{deinit_loggers, init_early_loggers, init_late_loggers};7071struct MiriCompilerCalls {72 miri_config: Option<MiriConfig>,73 many_seeds: Option<ManySeedsConfig>,74}7576struct ManySeedsConfig {77 seeds: Range<u32>,78 keep_going: bool,79}8081impl MiriCompilerCalls {82 fn new(miri_config: MiriConfig, many_seeds: Option<ManySeedsConfig>) -> Self {83 Self { miri_config: Some(miri_config), many_seeds }84 }85}8687fn entry_fn(tcx: TyCtxt<'_>) -> (DefId, MiriEntryFnType) {88 if let Some((def_id, entry_type)) = tcx.entry_fn(()) {89 return (def_id, MiriEntryFnType::Rustc(entry_type));90 }91 // Look for a symbol in the local crate named `miri_start`, and treat that as the entry point.92 let sym = tcx.exported_non_generic_symbols(LOCAL_CRATE).iter().find_map(|(sym, _)| {93 if sym.symbol_name_for_local_instance(tcx).name == "miri_start" { Some(sym) } else { None }94 });95 if let Some(ExportedSymbol::NonGeneric(id)) = sym {96 let start_def_id = id.expect_local();97 let start_span = tcx.def_span(start_def_id);9899 let expected_sig = ty::Binder::dummy(tcx.mk_fn_sig_safe_rust_abi(100 [tcx.types.isize, Ty::new_imm_ptr(tcx, Ty::new_imm_ptr(tcx, tcx.types.u8))],101 tcx.types.isize,102 ));103104 let correct_func_sig = check_function_signature(105 tcx,106 ObligationCause::new(start_span, start_def_id, ObligationCauseCode::Misc),107 *id,108 expected_sig,109 )110 .is_ok();111112 if correct_func_sig {113 (*id, MiriEntryFnType::MiriStart)114 } else {115 tcx.dcx().fatal(116 "`miri_start` must have the following signature:\n\117 fn miri_start(argc: isize, argv: *const *const u8) -> isize",118 );119 }120 } else {121 tcx.dcx().fatal(122 "Miri can only run programs that have a main function.\n\123 Alternatively, you can export a `miri_start` function:\n\124 \n\125 #[cfg(miri)]\n\126 #[unsafe(no_mangle)]\n\127 fn miri_start(argc: isize, argv: *const *const u8) -> isize {\128 \n // Call the actual start function that your project implements, based on your target's conventions.\n\129 }"130 );131 }132}133134fn run_many_seeds(135 many_seeds: ManySeedsConfig,136 eval_entry_once: impl Fn(u64) -> Result<(), NonZeroI32> + DynSync,137) -> Result<(), NonZeroI32> {138 let exit_code =139 sync::IntoDynSyncSend(AtomicU32::new(rustc_driver::EXIT_SUCCESS.cast_unsigned()));140 let num_failed = sync::IntoDynSyncSend(AtomicU32::new(0));141 sync::par_for_each_in(many_seeds.seeds.clone(), |&seed| {142 if let Err(return_code) = eval_entry_once(seed.into()) {143 eprintln!("FAILING SEED: {seed}");144 if !many_seeds.keep_going {145 // `abort_if_errors` would unwind but would not actually stop miri, since146 // `par_for_each` waits for the rest of the threads to finish.147 exit(return_code.get());148 }149 // Preserve the "maximum" return code (when interpreted as `u32`), to make150 // the result order-independent and to make it 0 only if all executions were 0.151 exit_code.fetch_max(return_code.get().cast_unsigned(), Ordering::Relaxed);152 num_failed.fetch_add(1, Ordering::Relaxed);153 }154 });155 let num_failed = num_failed.0.into_inner();156 let exit_code = exit_code.0.into_inner().cast_signed();157 if num_failed > 0 {158 eprintln!("{num_failed}/{total} SEEDS FAILED", total = many_seeds.seeds.count());159 Err(NonZeroI32::new(exit_code).unwrap())160 } else {161 assert!(exit_code == 0);162 Ok(())163 }164}165166/// Generates the codegen backend for code that Miri will interpret: we basically167/// use the dummy backend, except that we put the LLVM backend in charge of168/// target features.169fn make_miri_codegen_backend(sess: &Session) -> Box<dyn CodegenBackend> {170 let early_dcx = EarlyDiagCtxt::new(sess.opts.error_format);171172 // Use the target_config method of the default codegen backend (eg LLVM) to ensure the173 // calculated target features match said backend by respecting eg -Ctarget-cpu.174 let target_config_backend = rustc_interface::util::get_codegen_backend(175 &early_dcx,176 &sess.opts.sysroot,177 None,178 &sess.target,179 );180 target_config_backend.init(sess);181182 Box::new(DummyCodegenBackend {183 target_config_override: Some(Box::new(move |sess| {184 target_config_backend.target_config(sess)185 })),186 })187}188189impl rustc_driver::Callbacks for MiriCompilerCalls {190 fn config(&mut self, config: &mut rustc_interface::interface::Config) {191 // We never reach codegen anyway.192 config.make_codegen_backend = Some(Box::new(make_miri_codegen_backend));193194 // Register our custom extra symbols.195 config.extra_symbols = miri::sym::EXTRA_SYMBOLS.into();196 }197198 fn after_analysis<'tcx>(199 &mut self,200 _: &rustc_interface::interface::Compiler,201 tcx: TyCtxt<'tcx>,202 ) -> Compilation {203 // Compilation is done, interpretation is starting. Deal with diagnostics from the204 // compilation part. We cannot call `sess.finish_diagnostics()` as then "aborting due to205 // previous errors" gets printed twice.206 tcx.dcx().emit_stashed_diagnostics();207 tcx.dcx().abort_if_errors();208 tcx.dcx().flush_delayed();209210 // Miri is taking over. Start logging.211 init_late_loggers(&EarlyDiagCtxt::new(tcx.sess.opts.error_format), tcx);212213 // Find the entry point.214 if !tcx.crate_types().contains(&CrateType::Executable) {215 tcx.dcx().fatal("miri only makes sense on bin crates");216 }217 let (entry_def_id, entry_type) = entry_fn(tcx);218219 // Obtain and complete the Miri configuration.220 let mut config = self.miri_config.take().expect("after_analysis must only be called once");221 // Add filename to `miri` arguments.222 config.args.insert(0, tcx.sess.io.input.filestem().to_string());223224 // Adjust working directory for interpretation.225 if let Some(cwd) = env::var_os("MIRI_CWD") {226 env::set_current_dir(cwd).unwrap();227 }228229 // Emit warnings for some unusual configurations.230 if tcx.sess.opts.optimize != OptLevel::No {231 tcx.dcx().warn("Miri does not support optimizations: the opt-level is ignored. The only effect \232 of selecting a Cargo profile that enables optimizations (such as --release) is to apply \233 its remaining settings, such as whether debug assertions and overflow checks are enabled.");234 }235 if tcx.sess.mir_opt_level() > 0 {236 tcx.dcx().warn("You have explicitly enabled MIR optimizations, overriding Miri's default \237 which is to completely disable them. Any optimizations may hide UB that Miri would \238 otherwise detect, and it is not necessarily possible to predict what kind of UB will \239 be missed. If you are enabling optimizations to make Miri run faster, we advise using \240 cfg(miri) to shrink your workload instead. The performance benefit of enabling MIR \241 optimizations is usually marginal at best.");242 }243244 // Invoke the interpreter.245 let res = if config.genmc_config.is_some() {246 assert!(self.many_seeds.is_none());247 run_genmc_mode(tcx, &config, |genmc_ctx: Rc<GenmcCtx>| {248 miri::eval_entry(tcx, entry_def_id, entry_type, &config, Some(genmc_ctx))249 })250 } else if let Some(many_seeds) = self.many_seeds.take() {251 assert!(config.seed.is_none());252 run_many_seeds(many_seeds, |seed| {253 let mut config = config.clone();254 config.seed = Some(seed);255 eprintln!("Trying seed: {seed}");256 miri::eval_entry(tcx, entry_def_id, entry_type, &config, /* genmc_ctx */ None)257 })258 } else {259 miri::eval_entry(tcx, entry_def_id, entry_type, &config, None)260 };261 // Process interpreter result.262 if let Err(return_code) = res {263 tcx.dcx().abort_if_errors();264 exit(return_code.get());265 } else {266 exit(rustc_driver::EXIT_SUCCESS);267 }268269 // Unreachable.270 }271}272273/// This compiler produces rlibs that are meant for later consumption by Miri.274struct MiriDepCompilerCalls;275276impl rustc_driver::Callbacks for MiriDepCompilerCalls {277 #[allow(rustc::potential_query_instability)] // rustc_codegen_ssa (where this code is copied from) also allows this lint278 fn config(&mut self, config: &mut Config) {279 // We don't need actual codegen, we just emit an rlib that Miri can later consume.280 config.make_codegen_backend = Some(Box::new(make_miri_codegen_backend));281282 // Avoid warnings about unsupported crate types. However, only do that we we are *not* being283 // queried by cargo about the supported crate types so that cargo still receives the284 // warnings it expects.285 if config.opts.prints.is_empty() {286 #[allow(rustc::bad_opt_access)] // tcx does not exist yet287 {288 let any_crate_types = !config.opts.crate_types.is_empty();289 config290 .opts291 .crate_types292 .retain(|&c| c == CrateType::Executable || c == CrateType::Rlib);293 if any_crate_types {294 // Assert that we didn't remove all crate types if any crate type was passed on295 // the cli. Otherwise we might silently change what kind of crate we are building.296 assert!(!config.opts.crate_types.is_empty());297 }298 }299 }300301 // Queries overridden here affect the data stored in `rmeta` files of dependencies,302 // which will be used later in non-`MIRI_BE_RUSTC` mode.303 config.override_queries = Some(|_, local_providers| {304 // We need to add #[used] symbols to exported_symbols for `lookup_link_section`.305 // FIXME handle this somehow in rustc itself to avoid this hack.306 local_providers.queries.exported_non_generic_symbols = |tcx, LocalCrate| {307 let reachable_set = tcx.with_stable_hashing_context(|mut hcx| {308 tcx.reachable_set(()).to_sorted(&mut hcx, true)309 });310 tcx.arena.alloc_from_iter(311 // This is based on:312 // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L62-L63313 // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L174314 reachable_set.into_iter().filter_map(|&local_def_id| {315 // Do the same filtering that rustc does:316 // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L84-L102317 // Otherwise it may cause unexpected behaviours and ICEs318 // (https://github.com/rust-lang/rust/issues/86261).319 let is_reachable_non_generic = matches!(320 tcx.hir_node_by_def_id(local_def_id),321 Node::Item(&hir::Item {322 kind: hir::ItemKind::Static(..) | hir::ItemKind::Fn{ .. },323 ..324 }) | Node::ImplItem(&hir::ImplItem {325 kind: hir::ImplItemKind::Fn(..),326 ..327 })328 if !tcx.generics_of(local_def_id).requires_monomorphization(tcx)329 );330 if !is_reachable_non_generic {331 return None;332 }333 let codegen_fn_attrs = tcx.codegen_fn_attrs(local_def_id);334 if codegen_fn_attrs.contains_extern_indicator()335 || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)336 || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)337 {338 Some((339 ExportedSymbol::NonGeneric(local_def_id.to_def_id()),340 // Some dummy `SymbolExportInfo` here. We only use341 // `exported_symbols` in shims/foreign_items.rs and the export info342 // is ignored.343 SymbolExportInfo {344 level: SymbolExportLevel::C,345 kind: SymbolExportKind::Text,346 used: false,347 rustc_std_internal_symbol: false,348 },349 ))350 } else {351 None352 }353 }),354 )355 }356 });357358 // Register our custom extra symbols.359 config.extra_symbols = miri::sym::EXTRA_SYMBOLS.into();360 }361362 fn after_analysis<'tcx>(363 &mut self,364 _: &rustc_interface::interface::Compiler,365 tcx: TyCtxt<'tcx>,366 ) -> Compilation {367 // While the dummy codegen backend doesn't do any codegen, we are still emulating368 // regular rustc builds, which would perform post-mono const-eval during collection.369 // So let's also do that here. In particular this is needed to make `compile_fail`370 // doc tests trigger post-mono errors.371 let _ = tcx.collect_and_partition_mono_items(());372373 Compilation::Continue374 }375}376377fn exit(exit_code: i32) -> ! {378 // Drop the tracing guard before exiting, so tracing calls are flushed correctly.379 deinit_loggers();380 // Make sure the supervisor knows about the exit code.381 #[cfg(all(feature = "native-lib", unix))]382 miri::native_lib::register_retcode_sv(exit_code);383 // Actually exit.384 std::process::exit(exit_code);385}386387fn fatal_error_(msg: &impl std::fmt::Display) -> ! {388 eprintln!("fatal error: {msg}");389 exit(1)390}391392macro_rules! fatal_error {393 ($($tt:tt)*) => { $crate::fatal_error_(&format_args!($($tt)*)) };394}395#[allow(unused)] // use depends on cfg396use fatal_error;397398/// Execute a compiler with the given CLI arguments and callbacks.399fn run_compiler_and_exit(400 args: &[String],401 callbacks: &mut (dyn rustc_driver::Callbacks + Send),402) -> ! {403 // Install the ctrlc handler that sets `rustc_const_eval::CTRL_C_RECEIVED`, even if404 // MIRI_BE_RUSTC is set. We do this late so that when `native_lib::init_sv` is called,405 // there are no other threads.406 rustc_driver::install_ctrlc_handler();407408 // Invoke compiler, catch any unwinding panics and handle return code.409 let exit_code =410 rustc_driver::catch_with_exit_code(move || rustc_driver::run_compiler(args, callbacks));411 exit(if exit_code == ExitCode::SUCCESS {412 rustc_driver::EXIT_SUCCESS413 } else {414 rustc_driver::EXIT_FAILURE415 })416}417418/// Parses a comma separated list of `T` from the given string:419/// `<value1>,<value2>,<value3>,...`420fn parse_comma_list<T: FromStr>(input: &str) -> Result<Vec<T>, T::Err> {421 input.split(',').map(str::parse::<T>).collect()422}423424/// Parses the input as a float in the range from 0.0 to 1.0 (inclusive).425fn parse_rate(input: &str) -> Result<f64, &'static str> {426 match input.parse::<f64>() {427 Ok(rate) if rate >= 0.0 && rate <= 1.0 => Ok(rate),428 Ok(_) => Err("must be between `0.0` and `1.0`"),429 Err(_) => Err("requires a `f64` between `0.0` and `1.0`"),430 }431}432433/// Parses a seed range434///435/// This function is used for the `-Zmiri-many-seeds` flag. It expects the range in the form436/// `<from>..<to>`. `<from>` is inclusive, `<to>` is exclusive. `<from>` can be omitted,437/// in which case it is assumed to be `0`.438fn parse_range(val: &str) -> Result<Range<u32>, &'static str> {439 let (from, to) = val.split_once("..").ok_or("expected `from..to`")?;440 let from: u32 = if from.is_empty() { 0 } else { from.parse().map_err(|_| "invalid `from`")? };441 let to: u32 = to.parse().map_err(|_| "invalid `to`")?;442 Ok(from..to)443}444445fn main() -> ExitCode {446 let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());447448 // Snapshot a copy of the environment before `rustc` starts messing with it.449 // (`install_ice_hook` might change `RUST_BACKTRACE`.)450 let env_snapshot = env::vars_os().collect::<Vec<_>>();451452 let args = rustc_driver::catch_fatal_errors(|| rustc_driver::args::raw_args(&early_dcx))453 .unwrap_or_else(|_| std::process::exit(rustc_driver::EXIT_FAILURE));454455 // If the environment asks us to actually be rustc, then do that.456 if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") {457 if crate_kind == "host" {458 // For host crates like proc macros and build scripts, we are an entirely normal rustc.459 // These eventually produce actual binaries and never run in Miri.460 return rustc_driver::main();461 } else if crate_kind != "target" {462 panic!("invalid `MIRI_BE_RUSTC` value: {crate_kind:?}")463 };464465 // Earliest rustc setup.466 rustc_driver::install_ice_hook(rustc_driver::DEFAULT_BUG_REPORT_URL, |_| ());467 rustc_driver::init_rustc_env_logger(&early_dcx);468469 let mut args = args;470 // Splice in the default arguments after the program name.471 // Some options have different defaults in Miri than in plain rustc; apply those by making472 // them the first arguments after the binary name (but later arguments can overwrite them).473 args.splice(1..1, miri::MIRI_DEFAULT_ARGS.iter().map(ToString::to_string));474475 // We cannot use `rustc_driver::main` as we want it to use `args` as the CLI arguments.476 run_compiler_and_exit(&args, &mut MiriDepCompilerCalls)477 }478479 // Add an ICE bug report hook.480 rustc_driver::install_ice_hook("https://github.com/rust-lang/miri/issues/new", |_| ());481482 // Init loggers the Miri way.483 init_early_loggers(&early_dcx);484485 // Parse our arguments and split them across `rustc` and `miri`.486 let mut many_seeds: Option<Range<u32>> = None;487 let mut many_seeds_keep_going = false;488 let mut miri_config = MiriConfig::default();489 miri_config.env = env_snapshot;490491 let mut rustc_args = vec![];492 let mut after_dashdash = false;493494 // Note that we require values to be given with `=`, not with a space.495 // This matches how rustc parses `-Z`.496 // However, unlike rustc we do not accept a space after `-Z`.497 for arg in args {498 if rustc_args.is_empty() {499 // Very first arg: binary name.500 rustc_args.push(arg);501 // Also add the default arguments.502 rustc_args.extend(miri::MIRI_DEFAULT_ARGS.iter().map(ToString::to_string));503 } else if after_dashdash {504 // Everything that comes after `--` is forwarded to the interpreted crate.505 miri_config.args.push(arg);506 } else if arg == "--" {507 after_dashdash = true;508 } else if arg == "-Zmiri-disable-validation" {509 miri_config.validation = ValidationMode::No;510 } else if arg == "-Zmiri-recursive-validation" {511 miri_config.validation = ValidationMode::Deep;512 } else if arg == "-Zmiri-disable-stacked-borrows" {513 miri_config.borrow_tracker = None;514 } else if arg == "-Zmiri-tree-borrows" {515 miri_config.borrow_tracker =516 Some(BorrowTrackerMethod::TreeBorrows(TreeBorrowsParams {517 precise_interior_mut: true,518 implicit_writes: false,519 }));520 } else if arg == "-Zmiri-tree-borrows-no-precise-interior-mut" {521 match &mut miri_config.borrow_tracker {522 Some(BorrowTrackerMethod::TreeBorrows(params)) => {523 params.precise_interior_mut = false;524 }525 _ =>526 fatal_error!(527 "`-Zmiri-tree-borrows` is required before `-Zmiri-tree-borrows-no-precise-interior-mut`"528 ),529 };530 } else if arg == "-Zmiri-tree-borrows-implicit-writes" {531 match &mut miri_config.borrow_tracker {532 Some(BorrowTrackerMethod::TreeBorrows(params)) => {533 params.implicit_writes = true;534 }535 _ =>536 fatal_error!(537 "`-Zmiri-tree-borrows` is required before `-Zmiri-tree-borrows-implicit-writes`"538 ),539 };540 } else if arg == "-Zmiri-disable-data-race-detector" {541 miri_config.data_race_detector = false;542 miri_config.weak_memory_emulation = false;543 } else if arg == "-Zmiri-disable-alignment-check" {544 miri_config.check_alignment = miri::AlignmentCheck::None;545 } else if arg == "-Zmiri-symbolic-alignment-check" {546 miri_config.check_alignment = miri::AlignmentCheck::Symbolic;547 } else if arg == "-Zmiri-disable-isolation" {548 miri_config.isolated_op = miri::IsolatedOp::Allow;549 } else if arg == "-Zmiri-disable-leak-backtraces" {550 miri_config.collect_leak_backtraces = false;551 } else if arg == "-Zmiri-disable-weak-memory-emulation" {552 miri_config.weak_memory_emulation = false;553 } else if arg == "-Zmiri-track-weak-memory-loads" {554 miri_config.track_outdated_loads = true;555 } else if let Some(param) = arg.strip_prefix("-Zmiri-isolation-error=") {556 miri_config.isolated_op = match param {557 "abort" => miri::IsolatedOp::Reject(miri::RejectOpWith::Abort),558 "hide" => miri::IsolatedOp::Reject(miri::RejectOpWith::NoWarning),559 "warn" => miri::IsolatedOp::Reject(miri::RejectOpWith::Warning),560 "warn-nobacktrace" =>561 miri::IsolatedOp::Reject(miri::RejectOpWith::WarningWithoutBacktrace),562 _ =>563 fatal_error!(564 "-Zmiri-isolation-error must be `abort`, `hide`, `warn`, or `warn-nobacktrace`"565 ),566 };567 } else if arg == "-Zmiri-ignore-leaks" {568 miri_config.ignore_leaks = true;569 miri_config.collect_leak_backtraces = false;570 } else if arg == "-Zmiri-force-intrinsic-fallback" {571 miri_config.force_intrinsic_fallback = true;572 } else if arg == "-Zmiri-deterministic-floats" {573 miri_config.float_nondet = false;574 } else if arg == "-Zmiri-no-extra-rounding-error" {575 miri_config.float_rounding_error = miri::FloatRoundingErrorMode::None;576 } else if arg == "-Zmiri-max-extra-rounding-error" {577 miri_config.float_rounding_error = miri::FloatRoundingErrorMode::Max;578 } else if arg == "-Zmiri-no-short-fd-operations" {579 miri_config.short_fd_operations = false;580 } else if arg == "-Zmiri-strict-provenance" {581 miri_config.provenance_mode = ProvenanceMode::Strict;582 } else if arg == "-Zmiri-permissive-provenance" {583 miri_config.provenance_mode = ProvenanceMode::Permissive;584 } else if arg == "-Zmiri-mute-stdout-stderr" {585 miri_config.mute_stdout_stderr = true;586 } else if arg == "-Zmiri-retag-fields" {587 eprintln!(588 "warning: `-Zmiri-retag-fields` is a NOP and will be removed in a future version of Miri.\n\589 Field retagging has been on-by-default for a long time."590 );591 } else if arg == "-Zmiri-fixed-schedule" {592 miri_config.fixed_scheduling = true;593 } else if arg == "-Zmiri-deterministic-concurrency" {594 miri_config.fixed_scheduling = true;595 miri_config.address_reuse_cross_thread_rate = 0.0;596 miri_config.cmpxchg_weak_failure_rate = 0.0;597 miri_config.weak_memory_emulation = false;598 } else if let Some(param) = arg.strip_prefix("-Zmiri-seed=") {599 let seed = param.parse::<u64>().unwrap_or_else(|_| {600 fatal_error!("-Zmiri-seed must be an integer that fits into u64")601 });602 miri_config.seed = Some(seed);603 } else if let Some(param) = arg.strip_prefix("-Zmiri-many-seeds=") {604 let range = parse_range(param).unwrap_or_else(|err| {605 fatal_error!(606 "-Zmiri-many-seeds requires a range in the form `from..to` or `..to`: {err}"607 )608 });609 many_seeds = Some(range);610 } else if arg == "-Zmiri-many-seeds" {611 many_seeds = Some(0..64);612 } else if arg == "-Zmiri-many-seeds-keep-going" {613 many_seeds_keep_going = true;614 } else if let Some(trimmed_arg) = arg.strip_prefix("-Zmiri-genmc") {615 if let Err(msg) = GenmcConfig::parse_arg(&mut miri_config.genmc_config, trimmed_arg) {616 fatal_error!("{msg}");617 }618 } else if let Some(param) = arg.strip_prefix("-Zmiri-env-forward=") {619 miri_config.forwarded_env_vars.push(param.to_owned());620 } else if let Some(param) = arg.strip_prefix("-Zmiri-env-set=") {621 let Some((name, value)) = param.split_once('=') else {622 fatal_error!("-Zmiri-env-set requires an argument of the form <name>=<value>");623 };624 miri_config.set_env_vars.insert(name.to_owned(), value.to_owned());625 } else if let Some(param) = arg.strip_prefix("-Zmiri-track-pointer-tag=") {626 let ids: Vec<u64> = parse_comma_list(param).unwrap_or_else(|err| {627 fatal_error!("-Zmiri-track-pointer-tag requires a comma separated list of valid `u64` arguments: {err}")628 });629 for id in ids.into_iter().map(miri::BorTag::new) {630 if let Some(id) = id {631 miri_config.tracked_pointer_tags.insert(id);632 } else {633 fatal_error!("-Zmiri-track-pointer-tag requires nonzero arguments");634 }635 }636 } else if let Some(param) = arg.strip_prefix("-Zmiri-track-alloc-id=") {637 let ids = parse_comma_list::<NonZero<u64>>(param).unwrap_or_else(|err| {638 fatal_error!("-Zmiri-track-alloc-id requires a comma separated list of valid non-zero `u64` arguments: {err}")639 });640 miri_config.tracked_alloc_ids.extend(ids.into_iter().map(miri::AllocId));641 } else if arg == "-Zmiri-track-alloc-accesses" {642 miri_config.track_alloc_accesses = true;643 } else if let Some(param) = arg.strip_prefix("-Zmiri-address-reuse-rate=") {644 miri_config.address_reuse_rate = parse_rate(param)645 .unwrap_or_else(|err| fatal_error!("-Zmiri-address-reuse-rate {err}"));646 } else if let Some(param) = arg.strip_prefix("-Zmiri-address-reuse-cross-thread-rate=") {647 miri_config.address_reuse_cross_thread_rate = parse_rate(param)648 .unwrap_or_else(|err| fatal_error!("-Zmiri-address-reuse-cross-thread-rate {err}"));649 } else if let Some(param) = arg.strip_prefix("-Zmiri-compare-exchange-weak-failure-rate=") {650 miri_config.cmpxchg_weak_failure_rate = parse_rate(param).unwrap_or_else(|err| {651 fatal_error!("-Zmiri-compare-exchange-weak-failure-rate {err}")652 });653 } else if let Some(param) = arg.strip_prefix("-Zmiri-preemption-rate=") {654 miri_config.preemption_rate = parse_rate(param)655 .unwrap_or_else(|err| fatal_error!("-Zmiri-preemption-rate {err}"));656 } else if arg == "-Zmiri-report-progress" {657 // This makes it take a few seconds between progress reports on my laptop.658 miri_config.report_progress = Some(1_000_000);659 } else if let Some(param) = arg.strip_prefix("-Zmiri-report-progress=") {660 let interval = param.parse::<u32>().unwrap_or_else(|err| {661 fatal_error!("-Zmiri-report-progress requires a `u32`: {}", err)662 });663 miri_config.report_progress = Some(interval);664 } else if let Some(param) = arg.strip_prefix("-Zmiri-provenance-gc=") {665 let interval = param.parse::<u32>().unwrap_or_else(|err| {666 fatal_error!("-Zmiri-provenance-gc requires a `u32`: {}", err)667 });668 miri_config.gc_interval = interval;669 } else if let Some(param) = arg.strip_prefix("-Zmiri-measureme=") {670 miri_config.measureme_out = Some(param.to_string());671 } else if let Some(param) = arg.strip_prefix("-Zmiri-backtrace=") {672 miri_config.backtrace_style = match param {673 "0" => BacktraceStyle::Off,674 "1" => BacktraceStyle::Short,675 "full" => BacktraceStyle::Full,676 _ => fatal_error!("-Zmiri-backtrace may only be 0, 1, or full"),677 };678 } else if let Some(param) = arg.strip_prefix("-Zmiri-native-lib=") {679 let filename = param.to_string();680 let file_path = std::path::Path::new(&filename);681 if file_path.exists() {682 // For directories, nonrecursively add all normal files inside683 if let Ok(dir) = file_path.read_dir() {684 for lib in dir.filter_map(|res| res.ok()) {685 if lib.file_type().unwrap().is_file() {686 miri_config.native_lib.push(lib.path().to_owned());687 }688 }689 } else {690 miri_config.native_lib.push(filename.into());691 }692 } else {693 fatal_error!("-Zmiri-native-lib `{}` does not exist", filename);694 }695 } else if arg == "-Zmiri-native-lib-enable-tracing" {696 miri_config.native_lib_enable_tracing = true;697 } else if let Some(param) = arg.strip_prefix("-Zmiri-num-cpus=") {698 let num_cpus = param699 .parse::<u32>()700 .unwrap_or_else(|err| fatal_error!("-Zmiri-num-cpus requires a `u32`: {}", err));701 if !(1..=miri::MAX_CPUS).contains(&usize::try_from(num_cpus).unwrap()) {702 fatal_error!("-Zmiri-num-cpus must be in the range 1..={}", miri::MAX_CPUS);703 }704 miri_config.num_cpus = num_cpus;705 } else if let Some(param) = arg.strip_prefix("-Zmiri-force-page-size=") {706 let page_size = param.parse::<u64>().unwrap_or_else(|err| {707 fatal_error!("-Zmiri-force-page-size requires a `u64`: {}", err)708 });709 // Convert from kilobytes to bytes.710 let page_size = if page_size.is_power_of_two() {711 page_size * 1024712 } else {713 fatal_error!("-Zmiri-force-page-size requires a power of 2: {page_size}");714 };715 miri_config.page_size = Some(page_size);716 } else if let Some(param) = arg.strip_prefix("-Zmiri-user-relevant-crates=") {717 miri_config.user_relevant_crates.extend(param.split(',').map(|s| s.to_owned()));718 } else {719 // Forward to rustc.720 rustc_args.push(arg);721 }722 }723724 // Disabling validation also disables aliasing checks (as retags are done during validation).725 if miri_config.validation == ValidationMode::No {726 miri_config.borrow_tracker = None;727 }728729 // Native calls and strict provenance are not compatible.730 if !miri_config.native_lib.is_empty() && miri_config.provenance_mode == ProvenanceMode::Strict {731 fatal_error!("strict provenance is not compatible with calling native functions");732 }733 // Native calls and many-seeds are an "interesting" combination.734 if !miri_config.native_lib.is_empty() && many_seeds.is_some() {735 eprintln!(736 "warning: `-Zmiri-many-seeds` runs multiple instances of the program in the same address space, \737 so if the native library has global state, it will leak across execution bundaries"738 );739 }740 // You can set either one seed or many.741 if many_seeds.is_some() && miri_config.seed.is_some() {742 fatal_error!("Only one of `-Zmiri-seed` and `-Zmiri-many-seeds can be set");743 }744 // We cannot emulate weak memory without the data race detector.745 if miri_config.weak_memory_emulation && !miri_config.data_race_detector {746 fatal_error!(747 "Weak memory emulation cannot be enabled when the data race detector is disabled"748 );749 };750751 // Validate GenMC settings.752 if miri_config.genmc_config.is_some()753 && let Err(err) = GenmcConfig::validate(&mut miri_config)754 {755 fatal_error!("Invalid settings: {err}");756 }757758 // Ensure we have parallelism for many-seeds mode.759 if many_seeds.is_some() && !rustc_args.iter().any(|arg| arg.starts_with("-Zthreads=")) {760 // Clamp to 20 threads; things get a less efficient beyond that due to lock contention.761 let threads = std::thread::available_parallelism().map_or(1, |n| n.get()).min(20);762 rustc_args.push(format!("-Zthreads={threads}"));763 }764 let many_seeds =765 many_seeds.map(|seeds| ManySeedsConfig { seeds, keep_going: many_seeds_keep_going });766767 debug!("rustc arguments: {:?}", rustc_args);768 debug!("crate arguments: {:?}", miri_config.args);769 if !miri_config.native_lib.is_empty() && miri_config.native_lib_enable_tracing {770 // SAFETY: No other threads are running771 #[cfg(all(feature = "native-lib", unix))]772 if unsafe { miri::native_lib::init_sv() }.is_err() {773 eprintln!(774 "warning: The native-lib tracer could not be started. Is this an x86 Linux system, and does Miri have permissions to ptrace?\n\775 Falling back to non-tracing native-lib mode."776 );777 }778 }779 run_compiler_and_exit(&rustc_args, &mut MiriCompilerCalls::new(miri_config, many_seeds))780}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.