From 2532b86a3b89acaff5c7793d69bc159de8ddd214 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Tue, 19 Nov 2019 17:25:09 -0500 Subject: [PATCH 1/3] Propagate the return code from the `start` lang item Fixes #1064 This ensures that we set the error code properly when a panic unwinds past `main`. I'm not sure what the best way to write a test for this is --- src/eval.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/eval.rs b/src/eval.rs index 7203ba6bf1db..3dad5a0d8c73 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -11,7 +11,7 @@ use syntax::source_map::DUMMY_SP; use crate::{ EnvVars, Evaluator, FnVal, HelpersEvalContextExt, InterpCx, InterpError, InterpResult, MemoryExtra, MiriMemoryKind, Pointer, Scalar, StackPopCleanup, Tag, - TlsEvalContextExt, + TlsEvalContextExt, MPlaceTy }; /// Configuration needed to spawn a Miri instance. @@ -34,7 +34,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig, -) -> InterpResult<'tcx, InterpCx<'mir, 'tcx, Evaluator<'tcx>>> { +) -> InterpResult<'tcx, (InterpCx<'mir, 'tcx, Evaluator<'tcx>>, MPlaceTy<'tcx, Tag>)> { let mut ecx = InterpCx::new( tcx.at(syntax::source_map::DUMMY_SP), ty::ParamEnv::reveal_all(), @@ -170,12 +170,12 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( ecx.write_scalar(Scalar::from_u32(0), errno_place.into())?; ecx.machine.last_error = Some(errno_place); - Ok(ecx) + Ok((ecx, ret_ptr)) } pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) { - let mut ecx = match create_ecx(tcx, main_id, config) { - Ok(ecx) => ecx, + let (mut ecx, ret_ptr) = match create_ecx(tcx, main_id, config) { + Ok(v) => v, Err(mut err) => { err.print_backtrace(); panic!("Miri initialziation error: {}", err.kind) @@ -183,14 +183,18 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) { }; // Perform the main execution. - let res: InterpResult<'_> = (|| { + let res: InterpResult<'_, i64> = (|| { ecx.run()?; - ecx.run_tls_dtors() + // Read the return code pointer *before* we run TLS destructors, to assert + // that it was written to by the time that `start` lang item returned. + let return_code = ecx.read_scalar(ret_ptr.into())?.not_undef()?.to_machine_isize(&ecx)?; + ecx.run_tls_dtors()?; + Ok(return_code) })(); // Process the result. match res { - Ok(()) => { + Ok(return_code) => { let leaks = ecx.memory.leak_report(); // Disable the leak test on some platforms where we do not // correctly implement TLS destructors. @@ -199,6 +203,7 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) { if !ignore_leaks && leaks != 0 { tcx.sess.err("the evaluated program leaked memory"); } + std::process::exit(return_code as i32); } Err(mut e) => { // Special treatment for some error kinds From 310212931666421636286d223ca88841b26011c2 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Wed, 20 Nov 2019 12:43:10 -0500 Subject: [PATCH 2/3] Improve return code propagation. Don't explicitly exit if we reported an evaluation error --- src/bin/miri.rs | 5 ++++- src/eval.rs | 19 +++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 481491ea722f..9e95e4b0a466 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -15,6 +15,7 @@ extern crate rustc_interface; extern crate syntax; use std::str::FromStr; +use std::convert::TryFrom; use std::env; use hex::FromHexError; @@ -39,7 +40,9 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { // Add filename to `miri` arguments. config.args.insert(0, compiler.input().filestem().to_string()); - miri::eval_main(tcx, entry_def_id, config); + if let Some(return_code) = miri::eval_main(tcx, entry_def_id, config) { + std::process::exit(i32::try_from(return_code).expect("Return value was too large!")); + } }); compiler.session().abort_if_errors(); diff --git a/src/eval.rs b/src/eval.rs index 3dad5a0d8c73..603c30feb9f4 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -29,7 +29,10 @@ pub struct MiriConfig { pub seed: Option, } -// Used by priroda. +/// Returns a freshly created `InterpCx`, along with an `MPlaceTy` representing +/// the location where the return value of the `start` lang item will be +/// written to. +/// Used by `priroda` and `miri pub fn create_ecx<'mir, 'tcx: 'mir>( tcx: TyCtxt<'tcx>, main_id: DefId, @@ -173,7 +176,10 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( Ok((ecx, ret_ptr)) } -pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) { +/// Evaluates the main function specified by `main_id`. +/// Returns `Some(return_code)` if program executed completed. +/// Returns `None` if an evaluation error occured +pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> Option { let (mut ecx, ret_ptr) = match create_ecx(tcx, main_id, config) { Ok(v) => v, Err(mut err) => { @@ -202,13 +208,16 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) { let ignore_leaks = target_os == "windows" || target_os == "macos"; if !ignore_leaks && leaks != 0 { tcx.sess.err("the evaluated program leaked memory"); + // Ignore the provided return code - let the reported error + // determine the return code + return None; } - std::process::exit(return_code as i32); + return Some(return_code) } Err(mut e) => { // Special treatment for some error kinds let msg = match e.kind { - InterpError::Exit(code) => std::process::exit(code), + InterpError::Exit(code) => return Some(code.into()), err_unsup!(NoMirFor(..)) => format!("{}. Did you set `MIRI_SYSROOT` to a Miri-enabled sysroot? You can prepare one with `cargo miri setup`.", e), _ => e.to_string() @@ -251,6 +260,8 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) { trace!(" local {}: {:?}", i, local.value); } } + // Let the reported error determine the return code + return None; } } } From 2176bf6cf04bea76d9a1c85a8418fb9b91d17dfa Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Wed, 20 Nov 2019 13:52:04 -0500 Subject: [PATCH 3/3] Fix nits Co-Authored-By: Ralf Jung --- src/eval.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/eval.rs b/src/eval.rs index 603c30feb9f4..237ef99d1fe6 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -32,7 +32,7 @@ pub struct MiriConfig { /// Returns a freshly created `InterpCx`, along with an `MPlaceTy` representing /// the location where the return value of the `start` lang item will be /// written to. -/// Used by `priroda` and `miri +/// Public because this is also used by `priroda`. pub fn create_ecx<'mir, 'tcx: 'mir>( tcx: TyCtxt<'tcx>, main_id: DefId, @@ -178,7 +178,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( /// Evaluates the main function specified by `main_id`. /// Returns `Some(return_code)` if program executed completed. -/// Returns `None` if an evaluation error occured +/// Returns `None` if an evaluation error occured. pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> Option { let (mut ecx, ret_ptr) = match create_ecx(tcx, main_id, config) { Ok(v) => v, @@ -209,7 +209,7 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> if !ignore_leaks && leaks != 0 { tcx.sess.err("the evaluated program leaked memory"); // Ignore the provided return code - let the reported error - // determine the return code + // determine the return code. return None; } return Some(return_code) @@ -260,7 +260,7 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> trace!(" local {}: {:?}", i, local.value); } } - // Let the reported error determine the return code + // Let the reported error determine the return code. return None; } }