src/tools/miri/src/bin/miri.rs RUST 781 lines View on github.com → Search inside
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}

Code quality findings 35

Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
if unsafe { miri::native_lib::init_sv() }.is_err() {
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
Err(NonZeroI32::new(exit_code).unwrap())
Warning: '.expect()' will panic with a custom message on None/Err. While better than unwrap() for debugging, prefer non-panicking error handling in production code (match, if let, ?).
warning correctness expect-usage
let mut config = self.miri_config.take().expect("after_analysis must only be called once");
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
env::set_current_dir(cwd).unwrap();
Warning: Ignoring a Result or Option using 'let _ =' can hide errors or unexpected None values. Ensure the value is handled appropriately (match, if let, ?, expect) unless intentionally discarded with justification.
warning correctness discarded-result
let _ = tcx.collect_and_partition_mono_items(());
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
if lib.file_type().unwrap().is_file() {
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
if !(1..=miri::MAX_CPUS).contains(&usize::try_from(num_cpus).unwrap()) {
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
fn miri_start(argc: isize, argv: *const *const u8) -> isize",
Info: Use of raw pointers (*const T, *mut T) typically requires 'unsafe' blocks for dereferencing. Ensure usage is justified (FFI, low-level optimizations) and memory safety is manually upheld.
info safety raw-pointer
fn miri_start(argc: isize, argv: *const *const u8) -> isize {\
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
eprintln!("FAILING SEED: {seed}");
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
eprintln!("{num_failed}/{total} SEEDS FAILED", total = many_seeds.seeds.count());
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
eprintln!("Trying seed: {seed}");
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(rustc::potential_query_instability)] // rustc_codegen_ssa (where this code is copied from) also allows this lint
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(rustc::bad_opt_access)] // tcx does not exist yet
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
eprintln!("fatal error: {msg}");
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(unused)] // use depends on cfg
Info: This standard library function returns a Result. Ensure the Result is handled properly (e.g., using '?', match, if let) rather than potentially panicking with .unwrap() or .expect().
info correctness unhandled-result
match input.parse::<f64>() {
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
rustc_args.push(arg);
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
miri_config.args.push(arg);
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &mut miri_config.borrow_tracker {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &mut miri_config.borrow_tracker {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
miri_config.isolated_op = match param {
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
eprintln!(
Info: This standard library function returns a Result. Ensure the Result is handled properly (e.g., using '?', match, if let) rather than potentially panicking with .unwrap() or .expect().
info correctness unhandled-result
let seed = param.parse::<u64>().unwrap_or_else(|_| {
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
miri_config.forwarded_env_vars.push(param.to_owned());
Info: This standard library function returns a Result. Ensure the Result is handled properly (e.g., using '?', match, if let) rather than potentially panicking with .unwrap() or .expect().
info correctness unhandled-result
let interval = param.parse::<u32>().unwrap_or_else(|err| {
Info: This standard library function returns a Result. Ensure the Result is handled properly (e.g., using '?', match, if let) rather than potentially panicking with .unwrap() or .expect().
info correctness unhandled-result
let interval = param.parse::<u32>().unwrap_or_else(|err| {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
miri_config.backtrace_style = match param {
Performance Info: Calling .to_string() (especially on &str) allocates a new String. If done repeatedly in loops, consider alternatives like working with &str or using crates like `itoa`/`ryu` for number-to-string conversion.
info performance to-string-in-loop
let filename = param.to_string();
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
miri_config.native_lib.push(lib.path().to_owned());
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
miri_config.native_lib.push(filename.into());
Info: This standard library function returns a Result. Ensure the Result is handled properly (e.g., using '?', match, if let) rather than potentially panicking with .unwrap() or .expect().
info correctness unhandled-result
.parse::<u32>()
Info: This standard library function returns a Result. Ensure the Result is handled properly (e.g., using '?', match, if let) rather than potentially panicking with .unwrap() or .expect().
info correctness unhandled-result
let page_size = param.parse::<u64>().unwrap_or_else(|err| {
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
eprintln!(
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
eprintln!(

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.